import { Request, Response } from 'express'
import { IUser, User } from '../users/models'
import { IReaction, Reaction } from './models'
import { Message } from '../messages/models'
import { IChat } from '../chats/models'
import services from '../../utils/services'
import helpers from '../../utils/helpers'

async function getReaction(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		const messageId = req.params.messageId
		const user = res.locals.user as IUser

		const reaction = await Reaction.findOne({
			message: messageId,
			user: user._id
		}).lean()

		return res.json({ reaction: reaction ?? null })
	})
}

async function putReaction(req: Request, res: Response) {
	interface Payload {
		reaction?: string
	}
	await helpers.runApi(res, async () => {
		const messageId = req.params.messageId
		const user = res.locals.user as IUser
		const { reaction: reactionEmoji } = req.body as Payload
		const emoji = reactionEmoji ?? '❤️'

		const message = await Message.findById(messageId).populate('chat')
		if (!message) {
			return res.status(404).json({ message: 'message not found' })
		}
		const chat = message.chat as unknown as IChat
		if (!chat) {
			return res.status(404).json({ message: 'chat not found' })
		}
		const participant = chat.participants.find(e => `${e.user}` === `${user._id}`)
		if (!participant) {
			return res.status(403).json({ message: 'user must be a participant of this chat' })
		}

		let reaction = await Reaction.findOne({
			message: messageId,
			user: user._id
		}) as IReaction | null
		let created = false

		if (reaction) {
			if (reaction.reaction === emoji) {
				return res.status(200).json({ reaction })
			}
			// different emoji: replace and notify
			reaction.reaction = emoji
			await reaction.save()
		} else {
			created = true
			reaction = await Reaction.create({
				user: user._id,
				chat: chat._id,
				message: message._id,
				reaction: emoji
			}) as unknown as IReaction
		}

		// send notification and socket for both create and replace
		helpers.runInBackground(async () => {
			if (`${message.user}` === `${user._id}`) {
				return
			}
			const messageCreator = await User.findById(message.user).select('_id auth').exec()
			if (!messageCreator) {
				return
			}
			let title = `${user.name ?? user.username ?? 'Someone'} reacted to your message`
			if (chat.isGroup) {
				title += ` in "${chat.name}"`
			}
			const reactionPayload = {
				_id: reaction._id,
				user: {
					_id: user._id,
					name: user.name,
					username: user.username,
					photo: user.photo
				},
				reaction: reaction.reaction,
				at: reaction.at
			}
			await services.pushNotifyUser({
				title,
				body: reactionPayload.reaction,
				event: 'reaction-created',
				user: messageCreator as IUser,
				data: {
					reaction: reactionPayload,
					message: message._id,
					chat: chat._id,
					isGroup: chat.isGroup
				}
			})
		})

		helpers.runInBackground(async () => {
			const reactionPayload = {
				_id: `${reaction._id}`,
				user: {
					_id: `${user._id}`,
					name: user.name,
					username: user.username,
					photo: user.photo
				},
				reaction: reaction.reaction,
				at: reaction.at.toISOString()
			}
			const payload = {
				reaction: reactionPayload,
				message: `${message._id}`,
				chat: `${chat._id}`,
				isGroup: chat.isGroup
			}
			for (const p of chat.participants) {
				services.socketNotifyUser({
					userId: `${p.user}`,
					event: 'reaction-created',
					data: payload
				})
			}
		})

		return res.status(created ? 201 : 200).json({ reaction })
	})
}

async function deleteReaction(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		const messageId = req.params.messageId
		const user = res.locals.user as IUser

		const reaction = await Reaction.findOne({
			message: messageId,
			user: user._id
		}).populate('chat')
		if (!reaction) {
			return res.status(200).json({ message: 'reaction already removed' })
		}

		const chat = reaction.chat as unknown as IChat
		await reaction.deleteOne()

		// socket notify all chat participants (including the user who deleted)
		if (chat) {
			helpers.runInBackground(async () => {
				const payload = {
					reaction: `${reaction._id}`,
					message: messageId,
					chat: `${chat._id}`
				}
				for (const p of chat.participants) {
					services.socketNotifyUser({
						userId: `${p.user}`,
						event: 'reaction-deleted',
						data: payload
					})
				}
			})
		}

		return res.json({ message: 'reaction deleted successfully' })
	})
}

const controllers = {
	getReaction,
	putReaction,
	deleteReaction
}

export default controllers
