import { BaseProvider } from '#templates/base/provider';
import { Contract } from '#models/Contract';
import { ContractType } from '#models/ContractType';
import { Resident } from '#models/Resident';
import { randomUUID } from 'crypto';
import { AuditLogService } from '#services/audit/auditLogService';
import { GenericError } from '#interfaces/error/generic';

export class ContractProvider extends BaseProvider<Contract> {
	public static instance: ContractProvider;

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

	public static get model() {
		return Contract;
	}

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

	/**
	 * Override getAll to apply resident-level scoping based on user context (meta.user)
	 */
	public override async getAll(options?: any, meta?: { user?: any }) {
		try {
			const user = meta?.user ?? options?.user ?? null;
			const isAdmin = user?.roles?.some((r: string) => ['admin', 'system_admin'].includes(r)) ?? false;
			const payload = { ...(options || {}) } as any;
			if (!isAdmin) {
				const residentId = user?.resident_id ?? null;
				if (!residentId) {
					return { count: 0, rows: [], page: payload.page || 1, pageSize: Math.min(payload.pageSize || 20, 100) };
				}
				payload.filters = { ...(payload.filters || {}), resident_id: residentId };
			}

			// Include contract_type details
			payload.include = [
				...(payload.include || []),
				{
					model: ContractType,
					as: 'contract_type',
					attributes: ['id', 'code', 'name_vi', 'name_en', 'description'],
					required: false,
					where: { deleted_at: null },
				},
			];

			return super.getAll(payload);
		} catch (error) {
			await this.logError(error as Error);
			throw error;
		}
	}

	override async create(data: any, options?: any): Promise<Contract> {
		const requestId = randomUUID();
		const traceId = randomUUID();

		// Validate resident_id exists
		if (data.resident_id) {
			const resident = await Resident.findByPk(data.resident_id);
			if (!resident) {
				throw new GenericError('NOT_FOUND', { resource: 'Resident', id: data.resident_id });
			}
		}

		const contract = await super.create(data, options);

		// Log contract creation
		await AuditLogService.enqueueSystemAudit({
			requestId,
			traceId,
			actorId: data.created_by || null,
			actorName: data.created_by ? undefined : 'System',
			actorEmail: data.created_by ? undefined : 'system@bekind.vn',
			actorRole: data.created_by ? undefined : 'SYSTEM',
			action: 'CONTRACT_CREATED',
			module: 'CONTRACT',
			entityId: contract.get('id'),
			entityType: 'Contract',
			description: `Contract created for resident ${data.resident_id}`,
			severity: 'MEDIUM',
			payload: {
				contractId: contract.get('id'),
				residentId: data.resident_id,
				startDate: data.start_date,
				endDate: data.end_date,
				createdBy: data.created_by,
			},
		} as any);

		return contract;
	}

	override async delete(
		id: string,
		options?: {
			transaction?: any;
			force?: boolean;
			deleted_by?: string;
		},
	): Promise<string> {
		const requestId = randomUUID();
		const traceId = randomUUID();

		const toDelete = await Contract.findByPk(id);
		if (!toDelete) {
			throw new GenericError('NOT_FOUND', { resource: 'Contract', id });
		}

		const result = await super.delete(id, options);

		// Log contract deletion
		await AuditLogService.enqueueSystemAudit({
			requestId,
			traceId,
			actorId: options?.deleted_by || null,
			actorName: options?.deleted_by ? undefined : 'System',
			actorEmail: options?.deleted_by ? undefined : 'system@bekind.vn',
			actorRole: options?.deleted_by ? undefined : 'SYSTEM',
			action: 'CONTRACT_DELETED',
			module: 'CONTRACT',
			entityId: id,
			entityType: 'Contract',
			description: `Contract deleted for resident ${toDelete.get('resident_id')}`,
			severity: 'HIGH',
			payload: {
				contractId: id,
				residentId: toDelete.get('resident_id'),
				force: options?.force || false,
				deletedBy: options?.deleted_by,
			},
		} as any);

		return result;
	}
}
