import { Request, Response } from 'express'
import { IUser, User } from '../users/models'
import { Connection } from './models'
import services from '../../utils/services'
import { IStatus } from '../statuses/models'
import { Notification } from '../notifications/models'
import { IScore } from '../scores/models'

async function getConnections(req: Request, res: Response) {
	interface Payload {
		limit?: string | number
		page?: string | number
		status?: 'pending' | 'accepted'
	}

	try {
		const user = res.locals.user as IUser
		let { limit = '10', page = '1', status } = req.query as unknown as Payload

		limit = +limit
		page = +page
		const skip = (page - 1) * limit

		const query: any = {
			$or: [
				{ from: user._id },
				{ to: user._id }
			]
		}

		if (status) {
			query.status = status
		}

		const connections = await Connection.find(query)
			.sort({ createdAt: -1 })
			.skip(skip)
			.limit(limit)
			.populate({
				path: 'from',
				select: { name: 1, username: 1, bio: 1, photo: 1 }
			})
			.populate({
				path: 'to',
				select: { name: 1, username: 1, bio: 1, photo: 1 }
			})
		const count = await Connection.countDocuments(query)

		const list = []
		for (const connection of connections) {
			const conn: any = connection.toJSON()
			const from = conn.from as IUser
			const to = conn.to as IUser
			conn.user = `${from._id}` === `${user._id}` ? to : from
			list.push(conn)
		}

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

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

		if (userId === `${user._id}`) {
			return res.status(403).json({ message: 'Can not create connection with yourself' })
		}

		const otherUser = await User.findById(userId).populate('status')
		if (!otherUser) {
			return res.status(404).json({ message: 'user not found' })
		}
		if (otherUser.terminated) {
			return res.status(403).json({ message: 'user is inactive' })
		}

		const isBlocked = await services.isUserBlocked(user._id, otherUser._id)
		if(isBlocked) {
			return res.status(403).json({message: "Can't create connection when its blocked"})
		}

		const status = otherUser.status as unknown as IStatus | undefined
		if (!status) {
			return res.status(404).json({ message: 'status for this user does not exist' })
		}
		if (status.endedAt < new Date()) {
			otherUser.status = undefined
			await otherUser.save()
			return res.status(410).json({ message: 'status expired' })
		}

		let connection = await Connection.findOne({
			$or: [
				{ from: user._id, to: userId },
				{ to: user._id, from: userId }
			]
		})
		if (connection) {
			if (`${connection.statusDetail}` === `${status._id}`) {
				return res.status(409).json({ message: 'connection already exist' })
			}
			connection.statusDetail = status._id as any
			await connection.save()
			if (connection.status === 'accepted') {
				return res.status(409).json({ message: 'accepted connection already exist' })
			}
			await services.notifyUser({
				title: 'Connection Request',
				description: `${user.username} has send you a connection request`,
				event: 'connection-request',
				user: otherUser as IUser,
				connection: connection._id,
				from: user._id,
			})
			return res.json({ connection })
		}

		connection = await Connection.create({
			from: user._id,
			to: userId,
		})

		await services.notifyUser({
			title: 'Connection Request',
			description: `${user.username} has send you a connection request`,
			event: 'connection-request',
			user: otherUser as IUser,
			connection: connection._id,
			from: user._id,
		});

		(async () => {
			try {
				const score = await services.getOrCreateTodayScore(user._id)
				if (!score) {
					return
				}
				const action = score.social.actions.find(e => e.type === 'create-meetme-request')
				if (!action || action.completed) {
					return
				}
				action.value = 1
				action.completed = true
				await services.updateScore(score as unknown as IScore)
			} catch (error) {
				console.log(error)
			}
		})()

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

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

		if (userId === `${user._id}`) {
			return res.status(400).json({ message: 'User ID should be different than your ID' })
		}
		// remove status field if his status expired
		if (user.status) {
			const status = user.status as unknown as IStatus
			if (status.endedAt < new Date()) {
				user.status = undefined;
				(async () => {
					try {
						await user.save()
					} catch (error) {
						console.log(error)
					}
				})()
			}
		}

		const otherUser = await User.findById(userId).populate('status')
		if (!otherUser) {
			return res.status(404).json({ message: 'user not found' })
		}
		if (otherUser.terminated) {
			return res.status(403).json({ message: 'user is inactive' })
		}
		const isBlocked = await services.isUserBlocked(user._id, otherUser._id)
		if(isBlocked) {
			return res.status(403).json({message: "Can't accept connection when its blocked"})
		}
		// remove status field if his status expired
		if (otherUser.status) {
			const status = otherUser.status as unknown as IStatus
			if (status.endedAt < new Date()) {
				otherUser.status = undefined;
				(async () => {
					try {
						await otherUser.save()
					} catch (error) {
						console.log(error)
					}
				})()
			}
		}

		const connection = await Connection.findOne({
			$or: [
				{ from: user._id, to: userId },
				{ to: user._id, from: userId }
			]
		})
		if (!connection) {
			return res.status(404).json({ message: 'connection not found' })
		}

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

		if (connection.status === 'accepted') {
			return res.status(409).json({ message: 'connection already accepted' })
		}

		connection.status = 'accepted'
		connection.acceptedAt = new Date();

		(async () => {
			try {
				await connection.save()

				await services.notifyUser({
					title: 'Connection Accepted',
					description: `${user.username} has accepted your connection request`,
					event: 'connection-accepted',
					user: otherUser as IUser,
					connection: connection._id,
					from: user._id,
				})

				const score = await services.getOrCreateTodayScore(user._id)
				if (!score) {
					return
				}
				const action = score.social.actions.find(e => e.type === 'add-new-friend')
				if(!action || action.completed) {
					return
				}
				action.completed = true
				action.value = 1
				await services.updateScore(score as unknown as IScore)
			} catch (error) {
				console.log(error)
			}
		})()

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

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

		if (userId === `${user._id}`) {
			return res.status(400).json({ message: 'User ID should be different than your ID' })
		}

		const connection = await Connection.findOne({
			$or: [
				{ from: user._id, to: userId },
				{ to: user._id, from: userId }
			]
		}).populate('from to')
		if (!connection) {
			return res.status(404).json({ message: 'connection not found' })
		}
		if (connection.status === 'accepted' && connection.statusDetail) {
			return res.status(409).json({ message: 'can not delete accepted meet me connection' })
		}
		// remove status field from both user if their status expired
		const from = connection.from as unknown as IUser
		const to = connection.to as unknown as IUser;

		(async () => {
			try {
				if (from.status) {
					const status = from.status as unknown as IStatus
					if (status.endedAt < new Date()) {
						from.status = undefined
						await from.save()
					}
				}
				if (to.status) {
					const status = to.status as unknown as IStatus
					if (status.endedAt < new Date()) {
						to.status = undefined
						await to.save()
					}
				}

				await connection.deleteOne()
				if (connection.status === 'pending' && `${(connection.from as any)._id}` === `${user._id}`) {
					await Notification.deleteOne({
						connection: connection._id,
						event: 'connection-request'
					})
				}
			} catch (error) {
				console.log(error)
			}
		})()

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

const controllers = {
	getConnections,
	postConnection,
	postAcceptConnection,
	deleteConnection
}

export default controllers
