import { NextResponse } from 'next/server';
import type { CaptchaProvider } from '@nexumi/shared';
import {
  deleteCaptchaChallenge,
  getCaptchaChallenge,
  getCaptchaPublicConfig,
  getClientIp,
  hashCaptchaAnswer,
  hashClientSignal,
  verifyProviderToken
} from '@/lib/captcha';
import { getVerificationQueue } from '@/lib/queues';

function escapeHtml(value: string): string {
  return value
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;');
}

const PAGE_STYLES = `
  body { font-family: system-ui, sans-serif; background:#0b1220; color:#f8fafc; display:flex; justify-content:center; align-items:center; min-height:100vh; margin:0; }
  .card { background:#111827; padding:2rem; border-radius:12px; width:min(440px, 92vw); border:1px solid #1f2937; }
  h1 { margin:0 0 0.5rem; font-size:1.35rem; }
  p { margin:0 0 1rem; color:#cbd5e1; line-height:1.45; }
  .error { color:#f87171; margin-bottom:0.75rem; }
  input, button { width:100%; padding:0.75rem; margin-top:0.75rem; border-radius:8px; border:1px solid #374151; background:#0b1220; color:#f8fafc; box-sizing:border-box; }
  button { background:#4f46e5; border:none; cursor:pointer; font-weight:600; }
  .widget { display:flex; justify-content:center; margin:1rem 0; min-height:78px; }
`;

function renderShell(body: string): string {
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Nexumi Verification</title>
  <style>${PAGE_STYLES}</style>
</head>
<body>${body}</body>
</html>`;
}

function renderMathPage(token: string, question: string, error?: string): string {
  const errorBlock = error ? `<p class="error">${escapeHtml(error)}</p>` : '';
  return renderShell(`
  <form class="card" method="POST" action="/verify/captcha">
    <h1>Nexumi Verification</h1>
    <p>Solve: <strong>${escapeHtml(question)} = ?</strong></p>
    ${errorBlock}
    <input type="hidden" name="token" value="${escapeHtml(token)}" />
    <input type="hidden" name="provider" value="MATH" />
    <input type="number" name="answer" required autofocus />
    <button type="submit">Verify</button>
  </form>`);
}

function renderProviderPage(
  token: string,
  provider: CaptchaProvider,
  siteKey: string,
  error?: string
): string {
  const errorBlock = error ? `<p class="error">${escapeHtml(error)}</p>` : '';
  const labels: Record<string, string> = {
    RECAPTCHA_V2: 'Complete the Google reCAPTCHA below.',
    RECAPTCHA_V3: 'Checking with Google reCAPTCHA…',
    HCAPTCHA: 'Complete the hCaptcha below.',
    TURNSTILE: 'Complete the Cloudflare Turnstile below.'
  };

  if (provider === 'RECAPTCHA_V3') {
    return renderShell(`
  <form class="card" id="verify-form" method="POST" action="/verify/captcha">
    <h1>Nexumi Verification</h1>
    <p>${escapeHtml(labels.RECAPTCHA_V3)}</p>
    ${errorBlock}
    <input type="hidden" name="token" value="${escapeHtml(token)}" />
    <input type="hidden" name="provider" value="RECAPTCHA_V3" />
    <input type="hidden" name="captcha_response" id="captcha_response" />
    <button type="submit" id="submit-btn" disabled>Verifying…</button>
  </form>
  <script src="https://www.google.com/recaptcha/api.js?render=${escapeHtml(siteKey)}"></script>
  <script>
    grecaptcha.ready(function () {
      grecaptcha.execute('${escapeHtml(siteKey)}', { action: 'verify' }).then(function (token) {
        document.getElementById('captcha_response').value = token;
        document.getElementById('verify-form').submit();
      });
    });
  </script>`);
  }

  let widget = '';
  let scripts = '';
  if (provider === 'RECAPTCHA_V2') {
    widget = `<div class="g-recaptcha" data-sitekey="${escapeHtml(siteKey)}"></div>`;
    scripts = `<script src="https://www.google.com/recaptcha/api.js" async defer></script>`;
  } else if (provider === 'HCAPTCHA') {
    widget = `<div class="h-captcha" data-sitekey="${escapeHtml(siteKey)}"></div>`;
    scripts = `<script src="https://js.hcaptcha.com/1/api.js" async defer></script>`;
  } else if (provider === 'TURNSTILE') {
    widget = `<div class="cf-turnstile" data-sitekey="${escapeHtml(siteKey)}"></div>`;
    scripts = `<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>`;
  }

  return renderShell(`
  <form class="card" method="POST" action="/verify/captcha">
    <h1>Nexumi Verification</h1>
    <p>${escapeHtml(labels[provider] ?? 'Complete the captcha below.')}</p>
    ${errorBlock}
    <input type="hidden" name="token" value="${escapeHtml(token)}" />
    <input type="hidden" name="provider" value="${escapeHtml(provider)}" />
    <div class="widget">${widget}</div>
    <button type="submit">Verify</button>
  </form>
  ${scripts}`);
}

function renderMisconfigured(provider: string): string {
  return renderShell(`
  <div class="card">
    <h1>Captcha unavailable</h1>
    <p>Provider <strong>${escapeHtml(provider)}</strong> is not configured on this server. Ask an admin to set the keys in <code>.env</code>, or switch the guild to Math captcha.</p>
  </div>`);
}

function renderDonePage(): string {
  return renderShell(`
  <div class="card" style="text-align:center">
    <h1>Verified</h1>
    <p>You can return to Discord.</p>
  </div>`);
}

function html(body: string, status = 200): NextResponse {
  return new NextResponse(body, {
    status,
    headers: { 'Content-Type': 'text/html; charset=utf-8' }
  });
}

function extractProviderResponse(form: FormData, provider: CaptchaProvider): string {
  const direct = String(form.get('captcha_response') ?? '').trim();
  if (direct) {
    return direct;
  }
  if (provider === 'RECAPTCHA_V2' || provider === 'RECAPTCHA_V3') {
    return String(form.get('g-recaptcha-response') ?? '').trim();
  }
  if (provider === 'HCAPTCHA') {
    return String(form.get('h-captcha-response') ?? '').trim();
  }
  if (provider === 'TURNSTILE') {
    return String(form.get('cf-turnstile-response') ?? '').trim();
  }
  return '';
}

function renderChallengePage(
  token: string,
  provider: CaptchaProvider,
  question: string | undefined,
  error?: string
): NextResponse {
  if (provider === 'MATH') {
    return html(renderMathPage(token, question ?? '?', error));
  }
  const publicConfig = getCaptchaPublicConfig(provider);
  if (!publicConfig.siteKey) {
    return html(renderMisconfigured(provider), 503);
  }
  return html(renderProviderPage(token, provider, publicConfig.siteKey, error));
}

export async function GET(request: Request): Promise<NextResponse> {
  const token = new URL(request.url).searchParams.get('token');
  if (!token) {
    return new NextResponse('Missing token', { status: 400 });
  }
  const challenge = await getCaptchaChallenge(token);
  if (!challenge) {
    return new NextResponse('Captcha expired', { status: 404 });
  }
  return renderChallengePage(token, challenge.provider, challenge.question);
}

export async function POST(request: Request): Promise<NextResponse> {
  const form = await request.formData();
  const token = String(form.get('token') ?? '');
  if (!token) {
    return new NextResponse('Missing fields', { status: 400 });
  }

  const challenge = await getCaptchaChallenge(token);
  if (!challenge) {
    return new NextResponse('Captcha expired', { status: 404 });
  }

  const provider = challenge.provider;
  const clientIp = getClientIp(request);
  const userAgent = request.headers.get('user-agent') ?? '';

  if (provider === 'MATH') {
    const answer = String(form.get('answer') ?? '');
    if (!answer || !challenge.answerHash || hashCaptchaAnswer(answer) !== challenge.answerHash) {
      return renderChallengePage(token, provider, challenge.question, 'Wrong answer. Try again.');
    }
  } else {
    const responseToken = extractProviderResponse(form, provider);
    const verified = await verifyProviderToken(provider, responseToken, clientIp);
    if (!verified.ok) {
      const message =
        verified.error === 'score_too_low'
          ? 'reCAPTCHA score too low. Try again.'
          : verified.error === 'provider_not_configured'
            ? 'Captcha provider is not configured.'
            : 'Captcha failed. Try again.';
      return renderChallengePage(token, provider, challenge.question, message);
    }
  }

  await deleteCaptchaChallenge(token);

  const ipHash = clientIp ? hashClientSignal(clientIp) : null;
  const userAgentHash = userAgent ? hashClientSignal(userAgent) : null;

  await getVerificationQueue().add(
    'verificationComplete',
    {
      guildId: challenge.guildId,
      userId: challenge.userId,
      ipHash,
      userAgentHash,
      captchaProvider: provider
    },
    { removeOnComplete: 1000, removeOnFail: 500 }
  );

  return html(renderDonePage());
}
