import { Request, Response } from 'express'
import { IUser } from '../users/models'
import { Reward } from './models'
import { RewardHash } from '../reward-hashes/models'
import helpers from '../../utils/helpers'
import constants from '../../utils/constants'
import { IScore } from '../scores/models'
import services from '../../utils/services'

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

	try {
		const user = res.locals.user as IUser
		let { limit = '10', page = '1' } = req.query as unknown as Payload
		limit = +limit
		page = +page
		const skip = (page - 1) * limit
		const twoDaysAgo = new Date()
		twoDaysAgo.setDate(twoDaysAgo.getDate() - 2)

		const match: any = {
			isMeetme: true,
			$and: [
				{
					$or: [
						{expiredAt: {$exists: false}},
						{expiredAt: {$gt: twoDaysAgo}},
					]
				}
			]
		}
		if (constants.locationMode && user.location) {
			match.$and.push({
				$or: [
					{
						location: {
							$geoWithin: {
								$centerSphere: [
									user.location.coordinates, // [lng, lat]
									constants.locationDistance / constants.earthRadius // meters → radians
								]
							}
						}
					},
					{ location: { $exists: false } }
				]
			})
		}

		// 1️⃣ Get paginated data
		const rewards = await Reward.aggregate([
			{ $match: match },
			{ $sort: { createdAt: -1 } },
			{ $skip: skip },
			{ $limit: limit },
			{
				$lookup: {
					from: 'reward-hashes',
					let: { rewardId: '$_id', userId: user._id },
					pipeline: [
						{
							$match: {
								$expr: {
									$and: [
										{ $eq: ['$reward', '$$rewardId'] },
										{ $eq: ['$user', '$$userId'] }
									]
								}
							}
						},
						{ $limit: 1 }
					],
					as: 'hash'
				}
			},
			{
				$unwind: {
					path: '$hash',
					preserveNullAndEmptyArrays: true
				}
			}
		])
		for (const e of rewards) {
			e.isConsumed = e.hash?.isConsumed
			e.hash = e.hash?._id
		}

		// 2️⃣ Get total count
		const count = await Reward.countDocuments(match)

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

async function getProfileRewards(req: Request, res: Response) {
	try {
		const user = res.locals.user as IUser
		const limit = 6
		const page = 1
		const skip = (page - 1) * limit
		const at = new Date()
		at.setDate(at.getDate() - 2)

		const match: any = {
			$or: [
				{isMeetme: {$exists: false}},
				{isMeetme: false},
			],
			user: user._id
		}

		const rewardHashes = await RewardHash.aggregate([
			{ $match: match },
			{ $sort: { at: -1 } },
			{ $skip: skip },
			{ $limit: limit },
			{
				$lookup: {
					from: 'rewards',
					localField: 'reward',
					foreignField: '_id',
					as: 'reward'
				}
			},
			{ $unwind: { path: '$reward' } },
			{ $project: { hash: 0 } }
		])
		const count = await RewardHash.countDocuments(match)

		const rewardCoins: {type: 'unlocked' | 'pending' | 'locked'}[] = []
		for(let i = 0; i < 7; i++) {
			if(i < rewardHashes.length) {
				rewardCoins.push({type: 'unlocked'})
			} else if(i === rewardHashes.length) {
				rewardCoins.push({type: 'pending'})
			} else {
				rewardCoins.push({type: 'locked'})
			}
		}

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

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

		const reward = await Reward.findById(id)
		if (!reward) {
			return res.status(404).json({ message: 'reward not found' })
		}
		if(!reward.isMeetme) {
			return res.status(403).json({message: 'only applies to meet me reward'})
		}
		if (reward.remaining <= 0) {
			return res.status(410).json({ message: 'reward expired' })
		}
		if(reward.expiredAt && reward.expiredAt <= new Date()) {
			return res.status(410).json({ message: 'reward expired' })
		}

		let rewardHash = await RewardHash.findOne({
			user: user._id,
			reward: reward._id
		})
		if (rewardHash) {
			return res.json({ hash: rewardHash._id })
		}

		rewardHash = await RewardHash.create({
			user: user._id,
			reward: reward._id,
			hash: helpers.getHash(`${user._id}:${reward._id}`),
			isMeetme: true
		})

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

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

		(async () => {
			try {
				const score = await services.getOrCreateTodayScore(user._id)
				if (!score) {
					return
				}
				const action = score.social.actions.find(e => e.type === 'open-view-all-rewards')
				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.json({ message: 'done' })
	} catch (error) {
		console.log(error)
		return res.status(500).json({ message: 'server error' })
	}
}

const controllers = {
	getMeetmeRewards,
	getProfileRewards,
	postGenerateRewardHash,
	postOpenedViewAll
}

export default controllers
