import { Request, Response } from 'express'
import { IUser, User } from '../users/models'
import { Comment, IComment } from './models'
import { IReel, Reel } from '../reels/models'
import { Like } from '../likes/models'
import { SuperLike } from '../super-likes/models'
import services from '../../utils/services'
import { Types } from 'mongoose'
import fs from 'fs/promises'
import { AICommentPromptResponse } from '../../utils/types'
import { INotification } from '../notifications/models'
import path from 'path'
import { IScore } from '../scores/models'

async function getComments(req: Request, res: Response) {
	interface Payload {
		reel: string
		parent?: string
		limit?: string | number
		page?: string | number
	}

	try {
		let { reel, parent, limit = '10', page = '1' } =
			req.query as unknown as Payload
		limit = +limit
		page = +page

		const match: any = {
			reel: new Types.ObjectId(reel),
			...(parent
				? { parent: new Types.ObjectId(parent) }
				: { parent: { $exists: false } })
		}

		const pipeline: any = [
			{ $match: match },
			{ $sort: { superLike: -1, likes: -1, createdAt: -1 } },
			{
				$facet: {
					comments: [
						{ $skip: (page - 1) * limit },
						{ $limit: limit },
						// populate root user
						{
							$lookup: {
								from: 'users',
								localField: 'user',
								foreignField: '_id',
								as: 'user'
							}
						},
						{ $unwind: '$user' },

						// ✅ Total replies count (only if no parent filter)
						...(!parent ? [
							{
								$lookup: {
									from: 'comments',
									let: { commentId: '$_id' },
									pipeline: [
										{
											$match: {
												$expr: { $eq: ['$parent', '$$commentId'] }
											}
										},
										{ $count: 'count' }
									],
									as: 'subComments'
								}
							},
							{
								$addFields: {
									totalReplies: {
										$ifNull: [{ $arrayElemAt: ['$subComments.count', 0] }, 0]
									}
								}
							},
							{ $project: { subComments: 0 } }
						] : []),

						{
							$project: {
								reel: 1,
								parent: 1,
								superLike: 1,
								likes: 1,
								createdAt: 1,
								updatedAt: 1,
								totalReplies: 1,
								'user._id': 1,
								'user.name': 1,
								'user.username': 1,
								'user.photo': 1,
								data: 1
							}
						},
						// populate data.user one by one
						{
							$addFields: {
								data: {
									$map: {
										input: '$data',
										as: 'd',
										in: {
											$cond: [
												{ $ifNull: ['$$d.user', false] },
												{
													$mergeObjects: [
														'$$d',
														{
															user: {
																$let: {
																	vars: { userId: '$$d.user' },
																	in: {
																		$first: {
																			$map: {
																				input: {
																					$filter: {
																						input: {
																							$concatArrays: [
																								['$ROOT.user'],
																							]
																						},
																						as: 'dummy',
																						cond: false
																					}
																				},
																				as: 'filtered',
																				in: '$$filtered'
																			}
																		}
																	}
																}
															}
														}
													]
												},
												'$$d'
											]
										}
									}
								}
							}
						},
						// populate data.user after mapping
						{
							$lookup: {
								from: 'users',
								let: { dataArray: '$data' },
								pipeline: [
									{
										$match: {
											$expr: { $in: ['$_id', { $map: { input: '$$dataArray', as: 'd', in: '$$d.user' } }] }
										}
									},
									{
										$project: { _id: 1, name: 1, username: 1, photo: 1 }
									}
								],
								as: 'dataUsers'
							}
						},
						{
							$addFields: {
								data: {
									$map: {
										input: '$data',
										as: 'd',
										in: {
											$cond: [
												{ $ifNull: ['$$d.user', false] },
												{
													$mergeObjects: [
														'$$d',
														{
															user: {
																$arrayElemAt: [
																	{
																		$filter: {
																			input: '$dataUsers',
																			as: 'u',
																			cond: { $eq: ['$$u._id', '$$d.user'] }
																		}
																	},
																	0
																]
															}
														}
													]
												},
												'$$d'
											]
										}
									}
								}
							}
						},
						{ $project: { dataUsers: 0 } },
						{ $sort: { superLike: -1, likes: -1, createdAt: -1 } }
					],
					totalCount: [{ $count: 'count' }]
				}
			}
		]

		const result = await Comment.aggregate(pipeline)

		const comments = result[0]?.comments || []
		const count = result[0]?.totalCount[0]?.count || 0

		return res.json({ comments, count })
	} catch (error) {
		console.error(error)
		return res.status(500).json({ message: 'server error' })
	}
}

async function postComment(req: Request, res: Response) {
	interface Payload {
		reel: string
		parent?: string
		data: { user?: string, text?: string }[]
	}
	try {
		const user = res.locals.user as IUser
		const { reel: reelId, parent, data } = req.body as Payload

		let userIds = data.filter(e => e.user).map(e => e.user!)
		userIds = [...new Set(userIds)]
		if (userIds.length) {
			const users = (await User.find({ _id: { $in: userIds } }, { _id: 1 })).map(e => `${e._id}`)
			for (const u of userIds) {
				if (!users.includes(u)) {
					return res.status(404).json({ message: `A user with _id ${u} not found` })
				}
			}
		}

		const reel = await Reel.findById(reelId).populate('user')
		if (!reel) {
			return res.status(404).json({ message: 'reel not found' })
		}

		let parentComment: IComment | null | undefined
		if (parent) {
			// @ts-ignore
			parentComment = await Comment.findOne({
				_id: parent,
				reel: reel._id,
				parent: { $exists: false }
			}).populate('user')
			if (!parentComment) {
				return res.status(404).json({ message: 'parent comment not found' })
			}
		}

		const superLike = await SuperLike.findOne({
			reel: reel._id,
			user: user._id
		})

		const _comment = await Comment.create({
			user: user._id,
			reel: reel._id,
			parent,
			data,
			superLike: superLike?._id,
		})

		const count = await Comment.countDocuments({ reel: reel._id })
		reel.comments = count
		await reel.save()

		const comment = (await Comment.findById(_comment._id)
			.populate({ path: 'user', select: { name: 1, username: 1, photo: 1 } })
			.populate({ path: 'data.user', select: { name: 1, username: 1, photo: 1 } }))!

		let notification1: INotification | undefined, notification2: INotification | undefined;
		if (`${reel.user._id}` !== `${user._id}`) {
			notification1 = await services.notifyUser({
				title: 'New comment posted on your vibe',
				description: `${user.username} commented on your vibe`,
				event: 'comment-created',
				user: reel.user as unknown as IUser,
				comment: comment._id,
				from: user._id,
				reel: reel._id
			})
		}

		if (parentComment && `${parentComment.user._id}` !== `${user._id}`) {
			notification2 = await services.notifyUser({
				title: 'New reply on your comment',
				description: `${user.username} replied on your comment`,
				event: 'comment-created',
				// @ts-ignore
				user: parentComment.user,
				comment: comment._id,
				from: user._id,
				reel: reel._id
			})
		}

		// AI prompt to get comment properties
		;(async () => {
			try {
				const systemPrompt = await fs.readFile(path.join(__dirname, 'comment-prompt.md'), {encoding: 'utf-8'})
				const userPrompt = JSON.stringify(data)
				const jsonString = await services.runAiPrompt(userPrompt, systemPrompt)
				if(!jsonString) {
					return
				}
				const json = JSON.parse(jsonString) as AICommentPromptResponse
				if(json.isInappropriate) {
					reel.comments--
					await reel.save()
					await _comment.deleteOne()
					await notification1?.deleteOne()
					await notification2?.deleteOne()
					return
				}
				comment.isKind = json.isKind || undefined
				comment.isThankyou = json.isThankyou || undefined
				comment.isNegative = json.isNegative || undefined
				await comment.save()

				if(comment.isKind || comment.isThankyou) {
					const score = await services.getOrCreateTodayScore(user._id)
					if(!score) {
						return
					}
					const action1 = score.social.actions.find(e => e.type === 'mindful-comments')
					if(action1 && !action1.completed) {
						action1.count ??= 0
						action1.count++
						action1.value = action1.count / 3
						if(action1.count >= 3) {
							action1.completed = true
						}
					}
					const action2 = score.social.actions.find(e => e.type === 'mindful-comment-reply')
					if(action2 && !action2.completed && parent) {
						action2.value = 1
						action2.completed = true
					}
					await services.updateScore(score as unknown as IScore)
				}
			} catch (error) {
				console.log(error)
			}
		})();

		return res.status(201).json({ comment })
	} catch (error) {
		console.log(error)
		return res.status(500).json({ message: 'server error' })
	}
}

async function patchComment(req: Request, res: Response) {
	interface Payload {
		data: { user?: string, text?: string }[]
	}
	try {
		const user = res.locals.user as IUser
		const id = req.params.id
		const { data } = req.body as Payload

		const comment = await Comment.findById(id)
			.populate({ path: 'reel', populate: { path: 'user' } })
			.populate({ path: 'parent', populate: { path: 'user' } })
		if (!comment) {
			return res.status(404).json({ message: 'comment not found' })
		}

		const reel = comment.reel as IReel
		// @ts-ignore
		const reelUser = reel.user as IUser
		const parentComment = comment.parent as IComment

		if (`${comment.user}` !== `${user._id}`) {
			return res.status(403).json({ message: 'permission denied' })
		}

		let userIds = data.filter(e => e.user).map(e => e.user!)
		userIds = [...new Set(userIds)]
		if (userIds.length) {
			const users = (await User.find({ _id: { $in: userIds } }, { _id: 1 })).map(e => e._id.toString())
			for (const u of userIds) {
				if (!users.includes(u)) {
					return res.status(404).json({ message: `A user with _id ${u} not found` })
				}
			}
		}

		comment.data = data as any
		await comment.save()

		const updatedComment = await Comment.findById(comment._id)
			.populate({ path: 'user', select: { name: 1, username: 1, photo: 1 } })
			.populate({ path: 'data.user', select: { name: 1, username: 1, photo: 1 } })

		if (`${reel.user._id}` !== `${user._id}`) {
			await services.notifyUser({
				title: 'A comment has been updated on your vibe',
				description: `${user.username} updated its comment`,
				event: 'comment-updated',
				user: reelUser,
				comment: comment._id,
				from: user._id,
				reel: reel._id
			})
		}

		if (parentComment && `${parentComment.user._id}` !== `${user._id}`) {
			await services.notifyUser({
				title: 'A sub-comment updated',
				description: `${user.username} updated its sub-comment`,
				event: 'comment-updated',
				// @ts-ignore
				user: parentComment.user,
				comment: comment!._id,
				from: user._id,
				reel: reel._id
			})
		}

		return res.json({ comment: updatedComment })
	} catch (error) {
		console.log(error)
		return res.status(500).json({ message: 'server error' })
	}
}

async function deleteComment(req: Request, res: Response) {
	try {
		const user = res.locals.user as IUser
		const id = req.params.id

		const comment = await Comment.findById(id).populate('reel')
		if (!comment) {
			return res.status(404).json({ message: 'comment not found' })
		}

		if (`${comment.user}` !== `${user._id}`) {
			return res.status(403).json({ message: 'permission denied' })
		}

		(async () => {
			try {
				await comment.deleteOne()
				if (!comment.parent) {
					const commentIds = (await Comment.find({ parent: comment._id }, { _id: 1 })).map(e => e._id)
					commentIds.push(comment._id)
					await Like.deleteMany({ comment: { $in: commentIds } })
					await Comment.deleteMany({ _id: { $in: commentIds } })
				}
				const reel = comment.reel as unknown as IReel
				const count = await Comment.countDocuments({ reel: reel._id })
				reel.comments = count
				await reel.save()
			} catch (error) {
				console.log(error)
			}
		})()

		return res.json({ message: 'comment deleted successfully' })
	} catch (error) {
		console.log(error)
		return res.status(500).json({ message: 'server error' })
	}
}

const controllers = {
	getComments,
	postComment,
	patchComment,
	deleteComment
}

export default controllers
