import { BaseProvider } from '#templates/base/provider';
import { Incident } from '#models/Incident';
import { IncidentType } from '#models/IncidentType';
import { Room } from '#models/Room';
import { Apartment } from '#models/Apartment';
import { Building } from '#models/Building';
import { User } from '#models/User';
import { Technician } from '#models/Technician';
import sequelize from '#services/database/sequelize/sequelizeService';
import { Op } from 'sequelize';

export class IncidentProvider extends BaseProvider<Incident> {
	public static instance: IncidentProvider;

	public static getInstance(): IncidentProvider {
		IncidentProvider.instance ??= new IncidentProvider();
		return IncidentProvider.instance;
	}

	public static get model() {
		return Incident;
	}

	constructor() {
		super('Incident');
	}

	/**
	 * Get all incidents with optimized response using Sequelize
	 * Returns: id, title, code, priority, status, deadline, type_name, room_name, apartment_name,
	 *          building_name, reporter_name, technician_name, technician_phone, technician_email, timestamps
	 * Supports filtering via options.filters (built by queryModifier middleware from query params)
	 */
	public async getAllOptimized(options?: any, meta?: { user?: any }) {
		const limit = Math.min(options?.pageSize || 20, 100);
		const offset = ((options?.page || 1) - 1) * limit || 0;

		const order: any[] = [];
		if (options?.sortField) {
			order.push([options.sortField, options?.sortOrder || 'DESC']);
		} else {
			order.push(['created_at', 'DESC']);
		}

		// Determine user context (meta takes precedence)
		const user = meta?.user ?? options?.user ?? null;
		const isAdmin = user?.roles?.some((r: string) => ['admin', 'system_admin'].includes(r)) ?? false;
		const filters = { ...(options?.filters || {}) } as any;
		if (!isAdmin) {
			const filterConditions: any[] = [{ reporter_id: user?.id ?? null }];
			const technician = await Technician.findOne({
				where: { user_id: user?.id },
				attributes: ['id'],
			});
			if (technician) {
				filterConditions.push({ technician_id: technician.id });
			}
			filters[Op.or] = filterConditions;
		}

		try {
			const result = await Incident.findAndCountAll({
				where: {
					deleted_at: null,
					...(filters || {}),
				},
				include: [
					{
						model: IncidentType,
						as: 'type',
						attributes: [],
						required: false,
						where: { deleted_at: null },
					},
					{
						model: Room,
						as: 'room',
						attributes: [],
						required: false,
						where: { deleted_at: null },
						include: [
							{
								model: Apartment,
								as: 'apartment',
								attributes: [],
								required: false,
								where: { deleted_at: null },
								include: [
									{
										model: Building,
										as: 'building',
										attributes: [],
										required: false,
										where: { deleted_at: null },
									},
								],
							},
						],
					},
					{
						model: User,
						as: 'reporter',
						attributes: [],
						required: false,
						where: { deleted_at: null },
					},
					{
						model: Technician,
						as: 'technician',
						attributes: [],
						required: false,
						where: { deleted_at: null },
						include: [
							{
								model: User,
								as: 'user',
								attributes: [],
								required: false,
								where: { deleted_at: null },
							},
						],
					},
				],
				attributes: [
					'id',
					'title',
					'code',
					'type_id',
					'room_id',
					'reporter_id',
					'technician_id',
					'priority',
					'status',
					'deadline',
					'created_at',
					'updated_at',
					[sequelize.col('type.name'), 'type_name'],
					[sequelize.col('room.name'), 'room_name'],
					[sequelize.col('room.apartment.name'), 'apartment_name'],
					[sequelize.col('room.apartment.building.name'), 'building_name'],
					[
						sequelize.fn(
							'CONCAT',
							sequelize.fn('COALESCE', sequelize.col('reporter.first_name'), ''),
							' ',
							sequelize.fn('COALESCE', sequelize.col('reporter.last_name'), ''),
						),
						'reporter_name',
					],
					[
						sequelize.fn(
							'CONCAT',
							sequelize.fn('COALESCE', sequelize.col('technician.user.first_name'), ''),
							' ',
							sequelize.fn('COALESCE', sequelize.col('technician.user.last_name'), ''),
						),
						'technician_name',
					],
					[sequelize.col('technician.user.phone'), 'technician_phone'],
					[sequelize.col('technician.user.email'), 'technician_email'],
				],
				raw: true,
				subQuery: false,
				limit,
				offset,
				order,
				distinct: true,
			});

			return {
				count: result.count,
				rows: result.rows,
				page: options?.page || 1,
				pageSize: limit,
			};
		} catch (error) {
			await this.logError(error as Error);
			throw error;
		}
	}

	/**
	 * Get incident by ID with optimized response using Sequelize
	 */
	public async getByIdOptimized(id: string) {
		try {
			const incident = await Incident.findOne({
				where: { id, deleted_at: null },
				include: [
					{
						model: IncidentType,
						as: 'type',
						attributes: ['id', 'name'],
						where: { deleted_at: null },
						required: false,
					},
					{
						model: Room,
						as: 'room',
						attributes: ['id', 'name', 'apartment_id'],
						where: { deleted_at: null },
						required: false,
						include: [
							{
								model: Apartment,
								as: 'apartment',
								attributes: ['id', 'name', 'building_id'],
								where: { deleted_at: null },
								required: false,
								include: [
									{
										model: Building,
										as: 'building',
										attributes: ['id', 'name'],
										where: { deleted_at: null },
										required: false,
									},
								],
							},
						],
					},
					{
						model: User,
						as: 'reporter',
						attributes: ['id', 'first_name', 'last_name', 'phone', 'email'],
						where: { deleted_at: null },
						required: false,
					},
					{
						model: Technician,
						as: 'technician',
						attributes: ['id'],
						where: { deleted_at: null },
						required: false,
						include: [
							{
								model: User,
								as: 'user',
								attributes: ['id', 'first_name', 'last_name', 'phone', 'email'],
								where: { deleted_at: null },
								required: false,
							},
						],
					},
				],
				raw: false,
			});

			if (!incident) return null;

			// Transform to expected format
			const technicianData = incident.technician as any;
			const technicianUser = technicianData?.user;
			return {
				id: incident.id,
				title: incident.title,
				code: incident.code,
				type_id: incident.type_id,
				room_id: incident.room_id,
				description: incident.description,
				priority: incident.priority,
				status: incident.status,
				deadline: incident.deadline,
				created_at: incident.created_at,
				updated_at: incident.updated_at,
				type_name: (incident.type as any)?.name,
				room_name: (incident.room as any)?.name,
				apartment_name: (incident.room as any)?.apartment?.name,
				building_name: (incident.room as any)?.apartment?.building?.name,
				reporter: incident.reporter_id
					? {
							id: incident.reporter_id,
							name: `${((incident.reporter as any)?.first_name || '').trim()} ${((incident.reporter as any)?.last_name || '').trim()}`.trim(),
							phone: (incident.reporter as any)?.phone,
							email: (incident.reporter as any)?.email,
						}
					: null,
				technician:
					technicianData && technicianUser
						? {
								id: incident.technician_id,
								name: `${(technicianUser.first_name || '').trim()} ${(technicianUser.last_name || '').trim()}`.trim(),
								phone: technicianUser.phone,
								email: technicianUser.email,
							}
						: null,
			};
		} catch (error) {
			await this.logError(error as Error);
			throw error;
		}
	}
}
