import express from 'express';
import { OidcService } from './oidcService';
import { AuditLoggerService } from '#audit/auditLoggerService';
import { AUDIT_EVENTS } from '#audit/auditEvents';
import { PasswordService } from '../services/auth/passwordService';
import Config from '#config';

const router = express.Router();

// GET /oidc/interaction/:uid — render login or consent page
router.get('/:uid', async (req, res) => {
	try {
		const details = await OidcService.interactionDetails(req, res);

		if (details.prompt.name === 'consent') {
			return res.render('consent', {
				uid: req.params.uid,
				client: details.params.client_id,
				csrfToken: (req as any).csrfToken?.() ?? '',
			});
		}

		return res.render('login', {
			uid: req.params.uid,
			client: details.params.client_id,
			error: undefined,
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	} catch {
		return res.status(500).send('Internal Server Error');
	}
});

// GET /oidc/interaction/:uid/register — render registration page
router.get('/:uid/register', async (req, res) => {
	return res.render('register', {
		uid: req.params.uid,
		error: undefined,
		csrfToken: (req as any).csrfToken?.() ?? '',
	});
});

// POST /oidc/interaction/:uid/login — submit login credentials
router.post('/:uid/login', async (req, res) => {
	const { email, password, remember } = req.body as { email?: string; password?: string; remember?: string };

	try {
		const user = await validateCredentials(email ?? '', password ?? '');
		await logAudit(AUDIT_EVENTS.LOGIN_SUCCESS, user.id, req);

		const result = {
			login: {
				accountId: user.id,
				remember: Boolean(remember),
				ts: Math.floor(Date.now() / 1000),
			},
		};

		return OidcService.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
	} catch (err: any) {
		await logAudit(AUDIT_EVENTS.LOGIN_FAILED, undefined, req);

		if (err.status === 429) {
			return res.status(429).render('login', {
				uid: req.params.uid,
				client: req.query.client_id as string ?? '',
				error: err.message,
				csrfToken: (req as any).csrfToken?.() ?? '',
			});
		}

		return res.status(401).render('login', {
			uid: req.params.uid,
			client: req.query.client_id as string ?? '',
			error: 'Email or password is invalid',
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	}
});

// POST /oidc/interaction/:uid/register — create account then login
router.post('/:uid/register', async (req, res) => {
	const { email, username, password, confirmPassword } = req.body as {
		email?: string;
		username?: string;
		password?: string;
		confirmPassword?: string;
	};

	// Validate required fields
	if (!email || !password) {
		return res.status(400).render('register', {
			uid: req.params.uid,
			error: 'Email and password are required',
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	}

	// Validate password match
	if (password !== confirmPassword) {
		return res.status(400).render('register', {
			uid: req.params.uid,
			error: 'Passwords do not match',
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	}

	// Validate password minimum length (OIDC policy)
	const minPasswordLength = 12;
	if (password.length < minPasswordLength) {
		return res.status(400).render('register', {
			uid: req.params.uid,
			error: `Password must be at least ${minPasswordLength} characters`,
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	}

	try {
		// TODO: Create user via UserProvider
		// const derivedUsername = username || email.split('@')[0];
		// const passwordHash = await PasswordService.hashPassword(password);
		// const user = await userProvider.create({ email, username: derivedUsername, password_hash: passwordHash });
		// await logAudit(AUDIT_EVENTS.REGISTER_SUCCESS, user.id, req);

		// Placeholder: user registration not yet wired
		return res.status(501).render('register', {
			uid: req.params.uid,
			error: 'User registration is not yet available. Please use an existing account.',
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	} catch (err: any) {
		await logAudit(AUDIT_EVENTS.REGISTER_FAILED, undefined, req);
		const isUniqueViolation = err.code === '23505';

		return res.status(400).render('register', {
			uid: req.params.uid,
			error: isUniqueViolation
				? 'Email or username already exists. Please try again.'
				: 'Unable to create account. Please try again.',
			csrfToken: (req as any).csrfToken?.() ?? '',
		});
	}
});

// POST /oidc/interaction/:uid/confirm — approve consent
router.post('/:uid/confirm', async (req, res) => {
	try {
		const details = await OidcService.interactionDetails(req, res);
		const { prompt, grantId, session, params } = details;

		let grant: any;
		if (grantId) {
			grant = await OidcService.getInstance().Grant.find(grantId);
		} else {
			grant = new (OidcService.getInstance().Grant as any)({
				accountId: session.accountId,
				clientId: params.client_id,
			});
		}

		if (prompt.details?.missingOIDCScope) {
			grant.addOIDCScope(prompt.details.missingOIDCScope.join(' '));
		}
		if (prompt.details?.missingOIDCClaims) {
			grant.addOIDCClaims(prompt.details.missingOIDCClaims);
		}
		if (prompt.details?.missingResourceScopes) {
			for (const [indicator, scopes] of Object.entries(prompt.details.missingResourceScopes as Record<string, string[]>)) {
				grant.addResourceScope(indicator, scopes.join(' '));
			}
		}

		const result = { consent: { grantId: await grant.save() } };
		return OidcService.interactionFinished(req, res, result, { mergeWithLastSubmission: true });
	} catch {
		return res.status(500).send('Internal Server Error');
	}
});

// POST /oidc/interaction/:uid/cancel — deny consent
router.post('/:uid/cancel', async (req, res) => {
	return OidcService.interactionFinished(
		req,
		res,
		{ error: 'access_denied', error_description: 'User cancelled the request' },
		{ mergeWithLastSubmission: false },
	);
});

// ── Helpers ──────────────────────────────────────────────────────────────────

async function validateCredentials(
	email: string,
	password: string,
): Promise<{ id: string; email: string; username: string; status: string }> {
	// TODO: Wire with UserProvider + PasswordService + brute-force protection
	// For now, throw a generic error to indicate not yet implemented
	const err: any = new Error('Authentication not yet wired');
	err.status = 401;
	throw err;
}

async function logAudit(eventType: string, userId?: string, req?: express.Request): Promise<void> {
	if (!Config.audit.enabled) return;
	try {
		const audit = new AuditLoggerService();
		const logEntry: { eventType: string; userId?: string; status: 'success' | 'failed'; ip?: string; userAgent?: string; requestId?: string } = {
			eventType,
			status: eventType.endsWith('_SUCCESS') ? 'success' : 'failed',
		};
		if (userId) logEntry.userId = userId;
		if (req?.ip) logEntry.ip = req.ip;
		if (req?.headers['user-agent']) logEntry.userAgent = req.headers['user-agent'];
		if ((req as any)?.requestId) logEntry.requestId = (req as any).requestId;
		await audit.log(logEntry);
	} catch {
		// Don't let audit failures break the auth flow
	}
}

export default router;
