import { Types } from 'mongoose'
import { IFile } from '../../utils/types'
import { Event } from './models'
import { Chat } from '../chats/models'
import { User } from '../users/models'
import helpers from '../../utils/helpers'
import constants from '../../utils/constants'
import services from '../../utils/services'
import { Community } from '../communities/models'

export interface CreateEventPayload {
	title: string
	description: string
	date: string
	time: string
	locationString: string
	latitude: string | number
	longitude: string | number
	category: string
	url?: string
	communityId?: string | Types.ObjectId
	communities?: string[]
	joiningCost?: number
	membersLimit?: number
	/** Only admin can set this when creating an event */
	featured?: boolean
}

export function getEventTime(date: string, time: string) {
	const startedAt = new Date(`${date} ${time}`)
	const endedAt = new Date(startedAt.getTime() + 12 * 60 * 60 * 1000)  // 12 hours later
	return { startedAt, endedAt }
}

/**
 * Create an event and its chat for a user. Uploads cover if provided.
 * Runs background job to notify nearby users via socket (event-created).
 * Assumes date+time produce a future startedAt (caller must validate).
 */
export async function createEvent(
	userId: Types.ObjectId,
	payload: CreateEventPayload,
	currentCommunity?: Types.ObjectId,
	cover?: IFile,
	isRelief?: boolean
): Promise<{ event: any; chat: any }> {
	const { title, description, date, time, locationString, latitude, longitude, category, url: eventUrl, communityId, communities: communitiesIds, joiningCost, membersLimit, featured } = payload

	const { startedAt, endedAt } = getEventTime(date, time)

	const communities = communitiesIds?.length
		? communitiesIds.map((id) => new Types.ObjectId(id))
		: communityId
			? [new Types.ObjectId(communityId)]
			: undefined

	const tags = []
	if(currentCommunity && isRelief) {
		const community = await Community.findById(currentCommunity)
		if(community?.isUniversity) {
			tags.push(...[
				{tag: 'exams', label: 'Exams'},
				{tag: 'lectures', label: 'Lectures & Summaries'},
			])
		}
	}
	const chat = new Chat({
		creator: userId,
		community: communityId,
		communities: communities,
		isGroup: true,
		name: title,
		allowPublicPost: true,
		participants: [
			{
				user: userId,
				isAdmin: true,
			}
		],
		category,
		tags,
		...(joiningCost ? {restricted: true} : {})
	})

	const event = new Event({
		user: userId,
		community: communityId,
		communities,
		title,
		description,
		date,
		time,
		location: {
			type: 'Point',
			coordinates: [+longitude, +latitude]
		},
		isRelief: isRelief || undefined,
		locationString,
		chat: chat._id,
		url: eventUrl,
		category,
		joiningCost,
		membersLimit,
		startedAt,
		endedAt,
		...(featured !== undefined ? { featured } : {}),
	})

	let url: string | undefined = undefined
	if (cover) {
		url = await helpers.uploadFile(cover, `chats/${chat._id}/${cover.filename}`)
		if (!url) {
			throw new Error('unable to upload file')
		}
	}

	// @ts-ignore
	chat.event = event._id
	chat.photo = url
	event.cover = url
	await chat.save()
	await event.save()

	// Socket notify nearby users (background)
	helpers.runInBackground(async () => {
		if (!event.location) {
			return
		}
		const users = await User.find({
			name: { $exists: true },
			isOnline: true,
			$and: [
				{
					$or: [
						{ terminated: false },
						{ terminated: { $exists: false } }
					]
				},
			],
			location: {
				$geoWithin: {
					$centerSphere: [
						event.location.coordinates, // [lng, lat]
						constants.locationDistance / constants.earthRadius // meters → radians
					]
				}
			}
		}, { _id: 1 })
		for (const u of users) {
			services.socketNotifyUser({
				userId: `${u._id}`,
				event: 'event-created',
				data: { event: `${event._id}` }
			})
		}
	})

	return { event, chat }
}
