const { EventAttendies, Events, Members, ConventionUsers, Tickets, LinkedTickets } = require('../../models');
const { formatCase } = require('../../util/formatter');
const { sequelize } = require('../../models');
const { Op, literal } = require('sequelize');

exports.list = async (req, res, next) => {
	try {
		const { search } = req.query;

		const where = {};
		const memberWhere = {};

		if (search) {
			where[Op.or] = [
				{ ticket_id: { [Op.like]: `%${search}%` } },
				{ convention_center: { [Op.like]: `%${search}%` } },
				{ scanned_by: { [Op.like]: `%${search}%` } },
				sequelize.where(sequelize.fn('DATE', sequelize.col('timestamp')), {
					[Op.like]: `%${search}%`,
				}),
			];

			memberWhere[Op.or] = [
				{ account_id: { [Op.like]: `%${search}%` } },
				{ first_name: { [Op.like]: `%${search}%` } },
				{ last_name: { [Op.like]: `%${search}%` } },
			];
		}

		let attendies = await EventAttendies.findAll({
			where,
			attributes: {
				exclude: ['event_id', 'member_id', 'convention_user_id', 'createdAt', 'updatedAt'],
			},
			include: [
				{
					model: Members,
					as: 'member',
					where: Object.keys(memberWhere).length ? memberWhere : undefined,
				},
			],
		});

		attendies = attendies.map((item) => {
			let attendee = item.get({ plain: true });
			if (attendee.member) {
				attendee.account_id = attendee.member.account_id;
				attendee.name = `${attendee.member.first_name || ''} ${attendee.member.last_name || ''}`.trim();
				attendee.country = attendee.member.country ?? attendee.member.nationality ?? '';
			} else {
				attendee.name = '';
				attendee.country = '';
				attendee.account_id = '';
			}

			delete attendee.member;

			return attendee;
		});

		return res.status(200).json({
			success: true,
			data: formatCase(attendies),
		});
	} catch (error) {
		next(error);
	}
};

exports.scan = async (req, res, next) => {
	try {
		const { qr } = req.body;

		let ticket;
		let member = await Members.findOne({
			where: { id: qr },
			include: [
				{
					model: LinkedTickets,
					as: 'linkedTickets',
					include: [
						{
							model: Tickets,
							as: 'ticket',
							where: {
								is_used: 0,
							},
						},
					],
				},
			],
		});

		if (!member) {
			ticket = await Tickets.findOne({
				where: {
					id: qr,
				},
				include: [
					{
						model: LinkedTickets,
						as: 'linked',
						include: [
							{
								model: Members,
								as: 'member',
							},
						],
					},
					{
						model: Members,
						as: 'purchasedBy',
					},
				],
			});

			if (!ticket) {
				return res.status(404).json({ success: false, message: 'QR not valid.' });
			}

			if (ticket.is_used) {
				return res.status(400).json({ success: false, message: 'Ticket already used.' });
			}

			if (ticket?.linked?.member) {
				member = ticket.linked.member;
			} else if (ticket?.purchasedBy) {
				member = ticket.purchasedBy;
			} else {
				return res.status(404).json({ success: false, message: 'Member not found.' });
			}
		}

		const scanned = await EventAttendies.findOne({
			where: {
				member_id: member.id,
				ticket_id: {
					[Op.ne]: null,
				},
			},
			include: [
				{
					model: Events,
					as: 'event',
				},
				{
					model: Tickets,
					as: 'ticket',
				},
			],
			order: [['createdAt', 'DESC']],
		});

		if (!scanned) {
			if (!ticket && member?.linkedTickets?.length > 0) {
				ticket = member.linkedTickets[0].ticket;
			}

			if (!ticket) {
				ticket = await Tickets.findOne({
					where: {
						purchased_by: qr,
						is_used: 0,
						[Op.or]: [
							literal(`NOT EXISTS (
						SELECT 1 FROM linked_tickets 
						WHERE linked_tickets.ticket_no = Tickets.ticket_no
					)`),
							literal(`EXISTS (
						SELECT 1 FROM linked_tickets 
						WHERE linked_tickets.ticket_no = Tickets.ticket_no 
						AND linked_tickets.member_id = '${qr}'
					)`),
						],
					},
				});
			}

			if (!ticket) {
				return res.status(404).json({ success: false, message: 'No valid ticket found for this member.' });
			}
		} else {
			if (!ticket) {
				ticket = scanned.ticket;
			}
		}

		const exists = await EventAttendies.findOne({
			where: {
				member_id: member.id,
				worldcon_ticket_id: ticket?.worldcon_ticket_id,
				event_id: req?.user?.event_id ?? null,
			},
			include: [
				{
					model: Events,
					as: 'event',
				},
			],
		});

		if (exists) {
			return res.status(400).json({
				success: false,
				message: 'Member already scanned for this event: ' + (exists?.event?.title ?? ''),
			});
		}

		const event = await Events.findByPk(req?.user?.event_id ?? null);

		await sequelize.transaction(async (transaction) => {
			const eventAttendies = await EventAttendies.create(
				{
					member_id: member.id,
					event_id: req?.user?.event_id ?? null,
					worldcon_ticket_id: ticket.worldcon_ticket_id,
					convention_user_id: req.user.id,
					ticket_id: ticket.id,
					food_stab: 'Not Claimed',
					convention_center: event?.title ?? req?.user?.convention_center,
					timestamp: new Date(),
					scanned_by: req.user.first_name + ' ' + req.user.last_name,
				},
				{ transaction },
			);

			ticket.is_used = true;
			await ticket.save({ transaction });

			return res.status(200).json({
				success: true,
				data: formatCase(eventAttendies),
				message: 'Member scanned successfully.',
			});
		});
	} catch (error) {
		next(error);
	}
};

exports.claimFoodStub = async (req, res, next) => {
	try {
		const { id } = req.params;

		const foodStub = await EventAttendies.findOne({
			where: { member_id: id, food_stab: 'Not Claimed' },
		});

		if (!foodStub) {
			return res.status(404).json({ success: false, message: 'Food stub not found or already claimed.' });
		}

		foodStub.food_stab = 'Claimed';
		await foodStub.save();

		return res.status(200).json({ success: true, message: 'Food stub claimed successfully.', foodStub });
	} catch (error) {
		next(error);
	}
};

///TEST
exports.getEvents = async (req, res, next) => {
	try {
		const events = await Events.findAll();

		return res.status(200).json({
			success: true,
			data: formatCase(events),
		});
	} catch (error) {
		next(error);
	}
};

exports.updateSelf = async (req, res, next) => {
	try {
		const user = await ConventionUsers.findByPk(req.user.id);

		if (!user) {
			return res.status(404).json({ success: false, message: 'User not found' });
		}

		await user.update(req.body);

		delete user.password;
		return res.status(200).json({ success: true, data: formatCase(user) });
	} catch (error) {
		next(error);
	}
};
