import { createHash } from 'node:crypto';
import type { CaptchaProvider } from '@nexumi/shared';
import { redis } from './redis';
import { env } from './env';

const CAPTCHA_PREFIX = 'verify:captcha:';

export type CaptchaChallenge = {
  token: string;
  guildId: string;
  userId: string;
  provider: CaptchaProvider;
  question?: string;
  answerHash?: string;
};

export async function getCaptchaChallenge(token: string): Promise<CaptchaChallenge | null> {
  const raw = await redis.get(`${CAPTCHA_PREFIX}${token}`);
  if (!raw) {
    return null;
  }
  try {
    const parsed = JSON.parse(raw) as CaptchaChallenge;
    if (!parsed.provider) {
      parsed.provider = 'MATH';
    }
    return parsed;
  } catch {
    return null;
  }
}

export async function deleteCaptchaChallenge(token: string): Promise<void> {
  await redis.del(`${CAPTCHA_PREFIX}${token}`);
}

export function hashCaptchaAnswer(answer: string): string {
  return createHash('sha256').update(answer.trim()).digest('hex');
}

export function hashClientSignal(value: string): string | null {
  const salt = env.CAPTCHA_IP_HASH_SALT;
  if (!salt || !value) {
    return null;
  }
  return createHash('sha256').update(`${salt}:${value}`).digest('hex');
}

export function getClientIp(request: Request): string | null {
  const forwarded = request.headers.get('x-forwarded-for');
  if (forwarded) {
    const first = forwarded.split(',')[0]?.trim();
    if (first) {
      return first;
    }
  }
  const realIp = request.headers.get('x-real-ip')?.trim();
  return realIp || null;
}

export type CaptchaPublicConfig = {
  provider: CaptchaProvider;
  siteKey: string | null;
  recaptchaV3MinScore: number;
};

export function getAvailableCaptchaProviders(): CaptchaProvider[] {
  const providers: CaptchaProvider[] = ['MATH'];
  if (env.RECAPTCHA_SITE_KEY && env.RECAPTCHA_SECRET_KEY) {
    providers.push('RECAPTCHA_V2');
  }
  const v3Site = env.RECAPTCHA_V3_SITE_KEY ?? env.RECAPTCHA_SITE_KEY;
  const v3Secret = env.RECAPTCHA_V3_SECRET_KEY ?? env.RECAPTCHA_SECRET_KEY;
  if (v3Site && v3Secret) {
    providers.push('RECAPTCHA_V3');
  }
  if (env.HCAPTCHA_SITE_KEY && env.HCAPTCHA_SECRET_KEY) {
    providers.push('HCAPTCHA');
  }
  if (env.TURNSTILE_SITE_KEY && env.TURNSTILE_SECRET_KEY) {
    providers.push('TURNSTILE');
  }
  return providers;
}

export function getCaptchaPublicConfig(provider: CaptchaProvider): CaptchaPublicConfig {
  switch (provider) {
    case 'RECAPTCHA_V2':
      return {
        provider,
        siteKey: env.RECAPTCHA_SITE_KEY ?? null,
        recaptchaV3MinScore: env.RECAPTCHA_V3_MIN_SCORE
      };
    case 'RECAPTCHA_V3':
      return {
        provider,
        siteKey: env.RECAPTCHA_V3_SITE_KEY ?? env.RECAPTCHA_SITE_KEY ?? null,
        recaptchaV3MinScore: env.RECAPTCHA_V3_MIN_SCORE
      };
    case 'HCAPTCHA':
      return {
        provider,
        siteKey: env.HCAPTCHA_SITE_KEY ?? null,
        recaptchaV3MinScore: env.RECAPTCHA_V3_MIN_SCORE
      };
    case 'TURNSTILE':
      return {
        provider,
        siteKey: env.TURNSTILE_SITE_KEY ?? null,
        recaptchaV3MinScore: env.RECAPTCHA_V3_MIN_SCORE
      };
    default:
      return { provider: 'MATH', siteKey: null, recaptchaV3MinScore: env.RECAPTCHA_V3_MIN_SCORE };
  }
}

type SiteVerifyResult = { success: boolean; score?: number; 'error-codes'?: string[] };

async function postSiteVerify(
  url: string,
  secret: string,
  responseToken: string,
  remoteip?: string | null
): Promise<SiteVerifyResult> {
  const body = new URLSearchParams();
  body.set('secret', secret);
  body.set('response', responseToken);
  if (remoteip) {
    body.set('remoteip', remoteip);
  }
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body
  });
  if (!res.ok) {
    return { success: false, 'error-codes': [`http_${res.status}`] };
  }
  return (await res.json()) as SiteVerifyResult;
}

export async function verifyProviderToken(
  provider: CaptchaProvider,
  responseToken: string,
  remoteip?: string | null
): Promise<{ ok: boolean; error?: string }> {
  if (!responseToken.trim()) {
    return { ok: false, error: 'missing_token' };
  }

  if (provider === 'RECAPTCHA_V2') {
    if (!env.RECAPTCHA_SECRET_KEY) {
      return { ok: false, error: 'provider_not_configured' };
    }
    const result = await postSiteVerify(
      'https://www.google.com/recaptcha/api/siteverify',
      env.RECAPTCHA_SECRET_KEY,
      responseToken,
      remoteip
    );
    return result.success ? { ok: true } : { ok: false, error: 'provider_rejected' };
  }

  if (provider === 'RECAPTCHA_V3') {
    const secret = env.RECAPTCHA_V3_SECRET_KEY ?? env.RECAPTCHA_SECRET_KEY;
    if (!secret) {
      return { ok: false, error: 'provider_not_configured' };
    }
    const result = await postSiteVerify(
      'https://www.google.com/recaptcha/api/siteverify',
      secret,
      responseToken,
      remoteip
    );
    if (!result.success) {
      return { ok: false, error: 'provider_rejected' };
    }
    const score = result.score ?? 0;
    if (score < env.RECAPTCHA_V3_MIN_SCORE) {
      return { ok: false, error: 'score_too_low' };
    }
    return { ok: true };
  }

  if (provider === 'HCAPTCHA') {
    if (!env.HCAPTCHA_SECRET_KEY) {
      return { ok: false, error: 'provider_not_configured' };
    }
    const result = await postSiteVerify(
      'https://hcaptcha.com/siteverify',
      env.HCAPTCHA_SECRET_KEY,
      responseToken,
      remoteip
    );
    return result.success ? { ok: true } : { ok: false, error: 'provider_rejected' };
  }

  if (provider === 'TURNSTILE') {
    if (!env.TURNSTILE_SECRET_KEY) {
      return { ok: false, error: 'provider_not_configured' };
    }
    const result = await postSiteVerify(
      'https://challenges.cloudflare.com/turnstile/v0/siteverify',
      env.TURNSTILE_SECRET_KEY,
      responseToken,
      remoteip
    );
    return result.success ? { ok: true } : { ok: false, error: 'provider_rejected' };
  }

  return { ok: false, error: 'unsupported_provider' };
}
