import { Request, Response } from 'express'
import { Types } from 'mongoose'
import { Alert } from '../models'
import { AlertView } from '../../alert-views/models'
import { Event } from '../../events/models'
import { Reel } from '../../reels/models'
import { User } from '../../users/models'
import { IFile } from '../../../utils/types'
import helpers from '../../../utils/helpers'
import services from '../../../utils/services'
import constants from '../../../utils/constants'

async function socketNotifyAlert(alertId: Types.ObjectId, eventId?: Types.ObjectId) {
	const data = { alert: `${alertId}` }

	if (eventId) {
		const eventDoc = await Event.findById(eventId, { location: 1 })
		if (eventDoc?.location) {
			helpers.runInBackground(async () => {
				const users = await User.find({
					name: { $exists: true },
					isOnline: true,
					$and: [{ $or: [{ terminated: false }, { terminated: { $exists: false } }] }],
					location: {
						$geoWithin: {
							$centerSphere: [
								eventDoc.location.coordinates,
								constants.locationDistance / constants.earthRadius,
							],
						},
					},
				}, { _id: 1 })
				for (const u of users) {
					services.socketNotifyUser({ userId: `${u._id}`, event: 'alert-created', data })
				}
			})
			return
		}
	}

	services.socketNotifyAllUsers({ event: 'alert-created', data })
}

async function getAlerts(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		interface Payload {
			limit?: string | number
			page?: string | number
		}
		let { limit = '20', page = '1' } = req.query as Payload
		limit = +limit
		page = +page
		const skip = (page - 1) * limit

		const alerts = await Alert.find()
			.sort({ createdAt: -1 })
			.skip(skip)
			.limit(limit)
			.lean()

		const count = await Alert.countDocuments()

		return res.json({ alerts, count })
	})
}

async function getAlert(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		const id = req.params.id

		const alert = await Alert.findById(id).lean()
		if (!alert) {
			return res.status(404).json({ message: 'alert not found' })
		}

		return res.json({ alert })
	})
}

async function postAlert(req: Request, res: Response) {
	interface Payload {
		title: string
		description: string
		buttonLabel: string
		action: 'meetme' | 'home' | 'reel' | 'study' | 'event' | 'explore' | 'chats'
		event?: string
		reel?: string
		link?: string
		socketNotify?: boolean
	}
	await helpers.runApi(res, async () => {
		const { title, description, buttonLabel, action, event, reel, link, socketNotify = true } = req.body as Payload

		if (event && !(await Event.exists({ _id: event }))) {
			return res.status(404).json({ message: 'event not found' })
		}

		if (reel && !(await Reel.exists({ _id: reel }))) {
			return res.status(404).json({ message: 'reel not found' })
		}

		const alert = await Alert.create({
			title,
			description,
			buttonLabel,
			action,
			event: event || undefined,
			reel: reel || undefined,
			link: link || undefined,
		})

		if (socketNotify) {
			await socketNotifyAlert(alert._id, alert.event as Types.ObjectId | undefined ?? undefined)
		}

		return res.status(201).json({ alert })
	})
}

async function putAlertPhoto(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		const id = req.params.id
		const file = req.file as unknown as IFile
		const socketNotify = req.query.socketNotify === 'true'

		if (!file) {
			return res.status(400).json({ message: 'photo is required' })
		}

		const alert = await Alert.findById(id)
		if (!alert) {
			return res.status(404).json({ message: 'alert not found' })
		}

		const url = await helpers.uploadFile(file, `alerts/${alert._id}/${file.filename}`)
		if (!url) {
			return res.status(500).json({ message: 'unable to upload photo' })
		}

		if (alert.photo) {
			await helpers.deleteR2File(alert.photo)
		}

		alert.photo = url
		await alert.save()

		if (socketNotify) {
			await socketNotifyAlert(alert._id, alert.event as Types.ObjectId | undefined ?? undefined)
		}

		return res.json({ photo: url, message: 'photo updated successfully' })
	})
}

async function putAlertVideo(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		const id = req.params.id
		const file = req.file as unknown as IFile
		const socketNotify = req.query.socketNotify === 'true'

		if (!file) {
			return res.status(400).json({ message: 'video is required' })
		}

		const alert = await Alert.findById(id)
		if (!alert) {
			return res.status(404).json({ message: 'alert not found' })
		}

		const url = await helpers.uploadFile(file, `alerts/${alert._id}/${file.filename}`)
		if (!url) {
			return res.status(500).json({ message: 'unable to upload video' })
		}

		if (alert.video) {
			await helpers.deleteR2File(alert.video)
		}

		alert.video = url
		await alert.save()

		if (socketNotify) {
			await socketNotifyAlert(alert._id, alert.event as Types.ObjectId | undefined ?? undefined)
		}

		return res.json({ video: url, message: 'video updated successfully' })
	})
}

async function deleteAlert(req: Request, res: Response) {
	await helpers.runApi(res, async () => {
		const id = req.params.id

		const alert = await Alert.findById(id)
		if (!alert) {
			return res.status(404).json({ message: 'alert not found' })
		}

		if (alert.photo) {
			await helpers.deleteR2File(alert.photo)
		}
		if (alert.video) {
			await helpers.deleteR2File(alert.video)
		}

		await Alert.deleteOne({ _id: id })
		await AlertView.deleteMany({ alert: id })

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

const controllers = {
	getAlerts,
	getAlert,
	postAlert,
	putAlertPhoto,
	putAlertVideo,
	deleteAlert,
}

export default controllers
