/**
 * Captcha verification utilities
 * Supports Cloudflare Turnstile, hCaptcha, and Google reCAPTCHA (v2 and v3)
 */

import type { CaptchaConfig } from "../config/captcha.js";

interface TurnstileVerifyResponse {
	success: boolean;
	"error-codes"?: string[];
	challenge_ts?: string;
	hostname?: string;
}

interface HCaptchaVerifyResponse {
	success: boolean;
	"error-codes"?: string[];
	challenge_ts?: string;
	hostname?: string;
}

interface RecaptchaVerifyResponse {
	success: boolean;
	"error-codes"?: string[];
	challenge_ts?: string;
	hostname?: string;
	score?: number; // For reCAPTCHA v3
	action?: string; // For reCAPTCHA v3
}

/**
 * Verify Cloudflare Turnstile token
 */
async function verifyTurnstile(token: string, secretKey: string, remoteIp?: string): Promise<boolean> {
	const payload = new URLSearchParams({
		secret: secretKey,
		response: token
	});

	if (remoteIp) {
		payload.set("remoteip", remoteIp);
	}

	try {
		const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch : undefined;
		if (!fetchFn) {
			console.error("Fetch API is not available for Turnstile verification");
			return false;
		}

		const response = await fetchFn("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
			method: "POST",
			headers: {
				"Content-Type": "application/x-www-form-urlencoded"
			},
			body: payload
		});

		if (!response.ok) {
			console.error("Turnstile verification failed with status", response.status);
			return false;
		}

		const data = (await response.json()) as TurnstileVerifyResponse;
		if (!data.success) {
			console.warn("Turnstile verification rejected", data["error-codes"]);
		}
		return data.success;
	} catch (error) {
		console.error("Error verifying Turnstile token", error);
		return false;
	}
}

/**
 * Verify hCaptcha token
 */
async function verifyHCaptcha(token: string, secretKey: string, remoteIp?: string): Promise<boolean> {
	const payload = new URLSearchParams({
		secret: secretKey,
		response: token
	});

	if (remoteIp) {
		payload.set("remoteip", remoteIp);
	}

	try {
		const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch : undefined;
		if (!fetchFn) {
			console.error("Fetch API is not available for hCaptcha verification");
			return false;
		}

		const response = await fetchFn("https://hcaptcha.com/siteverify", {
			method: "POST",
			headers: {
				"Content-Type": "application/x-www-form-urlencoded"
			},
			body: payload
		});

		if (!response.ok) {
			console.error("hCaptcha verification failed with status", response.status);
			return false;
		}

		const data = (await response.json()) as HCaptchaVerifyResponse;
		if (!data.success) {
			console.warn("hCaptcha verification rejected", data["error-codes"]);
		}
		return data.success;
	} catch (error) {
		console.error("Error verifying hCaptcha token", error);
		return false;
	}
}

/**
 * Verify Google reCAPTCHA token (works for both v2 and v3)
 */
async function verifyRecaptcha(token: string, secretKey: string, remoteIp?: string, minScore = 0.5): Promise<boolean> {
	const payload = new URLSearchParams({
		secret: secretKey,
		response: token
	});

	if (remoteIp) {
		payload.set("remoteip", remoteIp);
	}

	try {
		const fetchFn = typeof globalThis.fetch === "function" ? globalThis.fetch : undefined;
		if (!fetchFn) {
			console.error("Fetch API is not available for reCAPTCHA verification");
			return false;
		}

		const response = await fetchFn("https://www.google.com/recaptcha/api/siteverify", {
			method: "POST",
			headers: {
				"Content-Type": "application/x-www-form-urlencoded"
			},
			body: payload
		});

		if (!response.ok) {
			console.error("reCAPTCHA verification failed with status", response.status);
			return false;
		}

		const data = (await response.json()) as RecaptchaVerifyResponse;
		if (!data.success) {
			console.warn("reCAPTCHA verification rejected", data["error-codes"]);
			return false;
		}

		// For reCAPTCHA v3, check the score
		if (data.score !== undefined) {
			if (data.score < minScore) {
				console.warn(`reCAPTCHA v3 score too low: ${data.score} (min: ${minScore})`);
				return false;
			}
		}

		return true;
	} catch (error) {
		console.error("Error verifying reCAPTCHA token", error);
		return false;
	}
}

/**
 * Main captcha verification function
 * Automatically selects the correct provider based on config
 */
export async function verifyCaptcha(
	token: string,
	config: CaptchaConfig,
	remoteIp?: string
): Promise<boolean> {
	// If captcha is disabled, always pass
	if (!config.enabled || config.provider === "none") {
		return true;
	}

	// Check bypass token first
	if (config.bypassToken && token === config.bypassToken) {
		return true;
	}

	// Validate that secret key is configured
	if (!config.secretKey) {
		console.error(`Captcha provider ${config.provider} is enabled but no secret key is configured`);
		return false;
	}

	// Verify based on provider
	switch (config.provider) {
		case "turnstile":
			return verifyTurnstile(token, config.secretKey, remoteIp);
		case "hcaptcha":
			return verifyHCaptcha(token, config.secretKey, remoteIp);
		case "recaptcha-v2":
		case "recaptcha-v3":
			// Use different minimum scores for v2 and v3
			const minScore = config.provider === "recaptcha-v3" ? 0.5 : 0.0;
			return verifyRecaptcha(token, config.secretKey, remoteIp, minScore);
		default:
			console.error(`Unknown captcha provider: ${config.provider}`);
			return false;
	}
}

/**
 * Extract remote IP address from request
 */
export function getRemoteAddress(req: any): string {
	return (
		req.headers["cf-connecting-ip"]
		|| req.headers["x-real-ip"]
		|| req.headers["x-forwarded-for"]?.split(",")[0]
		|| req.ip
		|| req.socket?.remoteAddress
		|| ""
	) as string;
}
