import { Resident } from '#models/Resident';
import { User } from '#models/User';
import { UserAuth } from '#models/UserAuth';
import { Role } from '#models/Role';
import { UserRole as UserRoleModel } from '#models/UserRole';
import { WebhookLog } from '#models/WebhookLog';
import { Op } from 'sequelize';
import { log } from '#utils/logger';
import { PasswordService } from '#services/auth/passwordService';
import { normalizePhone } from '#utils/stringUtils';
import { UserRoleEnum } from '#constants/index';
import { randomUUID } from 'crypto';
import sequelize from '#services/database/sequelize/sequelizeService';
import { AuditLogService } from '#services/audit/auditLogService';

export interface WebhookResidentData {
	type: 'KTX' | 'tro' | string;
	c_name: string;
	c_dob?: string;
	c_tel?: string;
	c_sex?: string;
	c_cccd?: string;
	c_add?: string;
}

export interface WebhookProcessResult {
	resident: Resident;
	webhookLog: WebhookLog;
}

export class WebhookService {
	/**
	 * Process resident webhook data
	 * Creates or updates resident based on id_card or phone
	 * Tracks processing in webhook_logs table
	 */
	async processResidentData(
		data: WebhookResidentData,
		metadata: {
			eventId?: string;
			ipAddress?: string;
			userAgent?: string;
		} = {},
	): Promise<WebhookProcessResult> {
		const requestId = randomUUID();
		const traceId = randomUUID();
		const webhookLogId = randomUUID();

		// Create webhook log entry with PROCESSING status
		const webhookLog = await WebhookLog.create(
			{
				id: webhookLogId,
				event_id: metadata.eventId || null,
				webhook_type: `RESIDENT_${data.type.toUpperCase()}`,
				status: 'PROCESSING',
				payload: data,
				ip_address: metadata.ipAddress || null,
				user_agent: metadata.userAgent || null,
				retry_count: 0,
				created_at: new Date(),
				updated_at: new Date(),
			},
			{ transaction: null },
		);

		try {
			const resident = await sequelize.transaction(async (tx) => {
				const { type, c_name, c_dob, c_tel, c_sex, c_cccd, c_add } = data;

				// Normalize phone number (remove spaces, dashes)
				const normalizedPhone = c_tel ? c_tel.replace(/[\s\-]/g, '') : null;
				const normalizedIdCard = c_cccd ? c_cccd.replace(/[\s\-]/g, '') : null;

				// Check if resident exists by id_card or phone
				const whereConditions: any = {
					deleted_at: null,
				};

				const orConditions: any[] = [];
				if (normalizedIdCard) {
					orConditions.push({ id_card_normalized: normalizedIdCard });
				}
				if (normalizedPhone) {
					orConditions.push({ phone_normalized: normalizedPhone });
				}

				if (orConditions.length > 0) {
					whereConditions[Op.or] = orConditions;
				}

				const existingResident = await Resident.findOne({
					where: whereConditions,
					transaction: tx,
				});

				// Prepare resident data
				const residentData: any = {
					full_name: c_name,
					phone: c_tel || null,
					id_card: c_cccd || null,
					date_of_birth: c_dob ? this.parseDate(c_dob) : null,
					nationality: 'Việt Nam', // Default to Vietnam
					status: 'active',
				};

				// Note: normalized fields are auto-generated by database triggers
				// Do not set them manually

				// Store additional info in job field (as JSON string or plain text)
				const additionalInfo = {
					type,
					sex: c_sex,
					address: c_add,
				};
				residentData.job = JSON.stringify(additionalInfo);

				let resident;

				if (existingResident) {
					// Update existing resident
					log('INFO', `Updating existing resident: ${existingResident.id}`);
					resident = await existingResident.update(residentData, { transaction: tx });

					// Audit log for resident update
					await AuditLogService.enqueueSystemAudit({
						requestId,
						traceId,
						actorId: null,
						actorName: 'Webhook',
						actorEmail: 'webhook@bekind.vn',
						actorRole: 'SYSTEM',
						action: 'RESIDENT_UPDATED',
						module: 'WEBHOOK',
						entityId: resident.id,
						entityType: 'Resident',
						description: `Resident ${resident.full_name} updated via webhook`,
						severity: 'MEDIUM',
						payload: {
							residentId: resident.id,
							residentName: resident.full_name,
							source: 'webhook',
							webhookType: type,
						},
					} as any);
				} else {
					// Create new resident
					log('INFO', `Creating new resident for: ${c_name}`);
					resident = await Resident.create(residentData, { transaction: tx });

					// Audit log for resident creation
					await AuditLogService.enqueueSystemAudit({
						requestId,
						traceId,
						actorId: null,
						actorName: 'Webhook',
						actorEmail: 'webhook@bekind.vn',
						actorRole: 'SYSTEM',
						action: 'RESIDENT_CREATED',
						module: 'WEBHOOK',
						entityId: resident.id,
						entityType: 'Resident',
						description: `Resident ${resident.full_name} created via webhook`,
						severity: 'MEDIUM',
						payload: {
							residentId: resident.id,
							residentName: resident.full_name,
							phone: resident.phone,
							source: 'webhook',
							webhookType: type,
						},
					} as any);

					// Create user account if resident has phone number (same logic as createWithResidence)
					if (c_tel) {
						const normalizedPhone = normalizePhone(c_tel);
						const phoneForUser = normalizedPhone || c_tel;

						const passwordHash = await PasswordService.hashPassword('Bekind@123');
						const userId = randomUUID();

						// Create user
						await User.create(
							{
								id: userId,
								email: `${phoneForUser}@bekind.com`,
								phone: phoneForUser,
								username: phoneForUser,
								first_name: c_name?.split(' ')[0] || null,
								last_name: c_name?.split(' ').slice(1).join(' ') || null,
								status: 'active',
								created_at: new Date(),
								updated_at: new Date(),
							},
							{ transaction: tx },
						);

						// Create user auth record
						await UserAuth.create(
							{
								id: randomUUID(),
								user_id: userId,
								password_hash: passwordHash,
								password_changed_at: new Date(),
								login_attempts: 0,
								created_at: new Date(),
								updated_at: new Date(),
							},
							{ transaction: tx },
						);

						// Assign default role
						const defaultRole = await Role.findOne({
							where: { name: UserRoleEnum.USER },
							transaction: tx,
						});

						if (defaultRole) {
							await UserRoleModel.create(
								{
									id: randomUUID(),
									user_id: userId,
									role_id: defaultRole.id,
									assigned_at: new Date(),
									updated_at: new Date(),
								},
								{ transaction: tx },
							);
						}

						// Link user to resident
						await resident.update({ user_id: userId, updated_at: new Date() }, { transaction: tx });

						log('INFO', `Created user account for resident: ${userId}`);

						// Audit log for user account creation
						await AuditLogService.enqueueSystemAudit({
							requestId,
							traceId,
							actorId: null,
							actorName: 'Webhook',
							actorEmail: 'webhook@bekind.vn',
							actorRole: 'SYSTEM',
							action: 'USER_ACCOUNT_CREATED',
							module: 'WEBHOOK',
							entityId: userId,
							entityType: 'User',
							description: `User account created for resident ${resident.full_name} via webhook`,
							severity: 'MEDIUM',
							payload: {
								userId,
								residentId: resident.id,
								residentName: resident.full_name,
								username: phoneForUser,
								source: 'webhook',
							},
						} as any);
					}
				}

				return resident;
			});

			// Update webhook log to SUCCESS after transaction commits
			await webhookLog.update(
				{
					status: 'SUCCESS',
					resident_id: resident.id,
					response_data: {
						resident_id: resident.id,
						full_name: resident.full_name,
					},
					processed_at: new Date(),
					updated_at: new Date(),
				},
				{ transaction: null },
			);

			return { resident, webhookLog };
		} catch (error) {
			log('ERR', `Error processing resident data: ${error}`);

			// Update webhook log to FAILED
			await webhookLog.update(
				{
					status: 'FAILED',
					error_message: error instanceof Error ? error.message : String(error),
					failed_at: new Date(),
					updated_at: new Date(),
				},
				{ transaction: null },
			);

			throw error;
		}
	}

	/**
	 * Parse date from DD/MM/YYYY format to YYYY-MM-DD
	 */
	private parseDate(dateStr: string): string | null {
		if (!dateStr) return null;

		try {
			// Try DD/MM/YYYY format
			const parts = dateStr.split('/');
			if (parts.length === 3) {
				const [day, month, year] = parts;
				return `${year}-${month}-${day}`;
			}

			// Try YYYY-MM-DD format
			if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) {
				return dateStr;
			}

			return null;
		} catch {
			return null;
		}
	}
}

export default new WebhookService();
