import { Application } from 'express';
import { Resource } from 'express-automatic-routes';
import { Req, Res } from '#interfaces/IApi';
import { createRateLimit } from '#middlewares/auth';
import { validateQueryZod } from '#middlewares/validators';
import {
	VerifyEmailQuerySchema,
	VerifyEmailResponseDataSchema,
	type VerifyEmailResponseData,
} from '#contracts/auth/schema';
import { sendSuccess } from '#utils/responseUtils';
import { GenericError } from '#interfaces/error/generic';
import EmailVerificationService from '#services/auth/emailVerificationService';
import { AuditLogService } from '#services/audit/auditLogService';
import { randomUUID } from 'crypto';

/** Checks if the request prefers HTML (browser navigation) vs JSON (API client). */
function prefersHtml(req: Req): boolean {
	const accept = req.headers.accept || '';
	return accept.includes('text/html');
}

/**
 * GET /api/v1/auth/verify-email?token=...
 *
 * Public endpoint hit directly from the verification email link. Marks the
 * verification token as used and flips the user to `active` + sets
 * `email_verified_at`.
 *
 * Behaviour:
 *  - Browser (Accept: text/html) → redirects to /verify-email.html which calls
 *    this endpoint via fetch(Accept: application/json) and renders the result inline.
 *  - API client (Accept: application/json or absent) → returns JSON as before.
 */
export default (_express: Application) => {
	return <Resource>{
		get: {
			middleware: [createRateLimit(5 * 60 * 1000, 30), validateQueryZod(VerifyEmailQuerySchema)] as any,
			handler: async (req: Req, res: Res) => {
				try {
					const { token } = req.query as unknown as { token: string };

					// ── Browser path: redirect to the standalone HTML page ───────────
					if (prefersHtml(req)) {
						return res.redirect(302, `/verify-email.html?token=${encodeURIComponent(token)}`);
					}

					const outcome = await EmailVerificationService.getInstance().verifyToken(String(token));

					if (outcome.kind === 'not_found') {
						throw new GenericError('VERIFICATION_TOKEN_INVALID');
					}
					if (outcome.kind === 'used') {
						// Email đã verified rồi — thử auto-login OIDC nếu có context.
						if (outcome.tokenRecord.oidc_uid) {
							const loginResult = await EmailVerificationService.autoLoginAfterVerification(
								outcome.user.id,
								outcome.tokenRecord.oidc_uid,
							);
							if (loginResult.resumed && loginResult.redirectUrl) {
								if (prefersHtml(req)) {
									return res.redirect(302, loginResult.redirectUrl);
								}
								const data: VerifyEmailResponseData = {
									verified: true,
									email: outcome.user.email,
									status: outcome.user.status ?? 'active',
									email_verified_at: outcome.user.email_verified_at
										? outcome.user.email_verified_at.toISOString()
										: new Date().toISOString(),
									auto_login: true,
									redirect_to: loginResult.redirectUrl,
									message: 'Email đã được xác thực trước đó. Đang chuyển hướng đến ứng dụng...',
								};
								VerifyEmailResponseDataSchema.parse(data);
								return sendSuccess(res, data);
							}
							// OIDC failed — fall through to manual login message
						}
						// Không có OIDC context hoặc auto-login thất bại → thông báo manual login.
						const data: VerifyEmailResponseData = {
							verified: true,
							email: outcome.user.email,
							status: outcome.user.status ?? 'active',
							email_verified_at: outcome.user.email_verified_at
								? outcome.user.email_verified_at.toISOString()
								: new Date().toISOString(),
							auto_login: false,
							auto_login_error: outcome.tokenRecord.oidc_uid ? 'ALREADY_USED' : undefined,
							message: 'Email đã được xác thực trước đó. Bạn có thể đăng nhập ngay bây giờ.',
						};
						VerifyEmailResponseDataSchema.parse(data);
						return sendSuccess(res, data);
					}
					if (outcome.kind === 'expired') {
						throw new GenericError('VERIFICATION_TOKEN_EXPIRED');
					}

				// outcome.kind === 'ok' — fire-and-forget audit log
				AuditLogService.enqueueSystemAudit({
					requestId: randomUUID(),
					traceId: randomUUID(),
					actorId: outcome.user.id,
					actorName:
						`${outcome.user.first_name || ''} ${outcome.user.last_name || ''}`.trim() || outcome.user.email,
					actorEmail: outcome.user.email,
					actorRole: 'USER',
					action: 'EMAIL_VERIFIED',
					module: 'AUTH',
					entityId: outcome.user.id,
					entityType: 'User',
					description: `Email verified for ${outcome.user.email}`,
					severity: 'LOW',
				} as any).catch(() => {});

				// Attempt OIDC auto-login if the token was issued from an OIDC registration flow.
				if (outcome.tokenRecord.oidc_uid) {
					const loginResult = await EmailVerificationService.autoLoginAfterVerification(
						outcome.user.id,
						outcome.tokenRecord.oidc_uid,
					);

					if (loginResult.resumed && loginResult.redirectUrl) {
						// Browser: redirect to the OIDC resume URL so oidc-provider can
						// complete the authorization code flow. API clients receive the URL.
						if (prefersHtml(req)) {
							return res.redirect(302, loginResult.redirectUrl);
						}
						const data: VerifyEmailResponseData = {
							verified: true,
							email: outcome.user.email,
							status: outcome.user.status ?? 'active',
							email_verified_at: outcome.user.email_verified_at
								? outcome.user.email_verified_at.toISOString()
								: new Date().toISOString(),
							auto_login: true,
							redirect_to: loginResult.redirectUrl,
							message:
								'Email đã được xác thực. Đang chuyển hướng đến ứng dụng...',
						};
						VerifyEmailResponseDataSchema.parse(data);
						return sendSuccess(res, data);
					}

					// Auto-login failed (interaction expired, invalid state, etc.) —
					// fall through to a normal response so the user can log in manually.
					const data: VerifyEmailResponseData = {
						verified: true,
						email: outcome.user.email,
						status: outcome.user.status ?? 'active',
						email_verified_at: outcome.user.email_verified_at
							? outcome.user.email_verified_at.toISOString()
							: new Date().toISOString(),
						auto_login: false,
						auto_login_error: loginResult.error ?? 'UNKNOWN_ERROR',
						message:
							'Email đã được xác thực nhưng không thể đăng nhập tự động. Bạn có thể đăng nhập thủ công.',
					};
					VerifyEmailResponseDataSchema.parse(data);
					return sendSuccess(res, data);
				}

				// No OIDC context (REST API registration or no interaction): return normal response.
				const data: VerifyEmailResponseData = {
					verified: true,
					email: outcome.user.email,
					status: outcome.user.status ?? 'active',
					email_verified_at: outcome.user.email_verified_at
						? outcome.user.email_verified_at.toISOString()
						: new Date().toISOString(),
					message:
						'Email đã được xác thực thành công. Bạn có thể đăng nhập ngay bây giờ. / Email verified successfully. You may log in now.',
				};
				// Validate data matches the response schema (compile-time + runtime sanity)
				VerifyEmailResponseDataSchema.parse(data);
				return sendSuccess(res, data);
				} catch (error) {
					return res.error(error);
				}
			},
		},
	};
};