import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import { env } from './env.js';
import { redis } from './redis.js';
import {
  deleteCaptchaChallenge,
  getCaptchaChallenge,
  hashCaptchaAnswer
} from './modules/verification/service.js';
import {
  automodQueue,
  backupQueue,
  birthdayQueue,
  feedsQueue,
  presenceQueue,
  retentionQueue,
  ticketQueue,
  verificationQueue
} from './queues.js';
import { collectMetricsSnapshot, formatPrometheus } from './metrics.js';

function readBody(req: IncomingMessage): Promise<string> {
  return new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    req.on('data', (chunk) => chunks.push(chunk));
    req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
    req.on('error', reject);
  });
}

function parseFormBody(body: string): Record<string, string> {
  return Object.fromEntries(new URLSearchParams(body));
}

function renderCaptchaPage(token: string, question: string, error?: string): string {
  const errorBlock = error ? `<p style="color:#ef4444">${error}</p>` : '';
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Nexumi Verification</title>
  <style>
    body { font-family: Inter, sans-serif; background:#111827; color:#f9fafb; display:flex; justify-content:center; align-items:center; min-height:100vh; margin:0; }
    form { background:#1f2937; padding:2rem; border-radius:12px; width:min(420px, 90vw); }
    input, button { width:100%; padding:0.75rem; margin-top:0.75rem; border-radius:8px; border:1px solid #374151; background:#111827; color:#f9fafb; }
    button { background:#6366f1; border:none; cursor:pointer; font-weight:600; }
  </style>
</head>
<body>
  <form method="POST" action="/verify/captcha">
    <h1>Nexumi Verification</h1>
    <p>Solve: <strong>${question} = ?</strong></p>
    ${errorBlock}
    <input type="hidden" name="token" value="${token}" />
    <input type="number" name="answer" required autofocus />
    <button type="submit">Verify</button>
  </form>
</body>
</html>`;
}

async function handleCaptchaGet(url: URL, res: ServerResponse): Promise<void> {
  const token = url.searchParams.get('token');
  if (!token) {
    res.statusCode = 400;
    res.end('Missing token');
    return;
  }
  const challenge = await getCaptchaChallenge(redis, token);
  if (!challenge) {
    res.statusCode = 404;
    res.end('Captcha expired');
    return;
  }
  // External providers (reCAPTCHA / hCaptcha / Turnstile) are served by the WebUI.
  if (challenge.provider !== 'MATH' && env.WEBUI_URL) {
    const target = `${env.WEBUI_URL.replace(/\/$/, '')}/verify/captcha?token=${encodeURIComponent(token)}`;
    res.statusCode = 302;
    res.setHeader('Location', target);
    res.end();
    return;
  }
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  res.end(renderCaptchaPage(token, challenge.question ?? '?'));
}

async function handleCaptchaPost(req: IncomingMessage, res: ServerResponse): Promise<void> {
  const body = await readBody(req);
  const form = parseFormBody(body);
  const token = form.token;
  const answer = form.answer;
  if (!token || !answer) {
    res.statusCode = 400;
    res.end('Missing fields');
    return;
  }
  const challenge = await getCaptchaChallenge(redis, token);
  if (!challenge) {
    res.statusCode = 404;
    res.end('Captcha expired');
    return;
  }
  if (challenge.provider !== 'MATH') {
    if (env.WEBUI_URL) {
      const target = `${env.WEBUI_URL.replace(/\/$/, '')}/verify/captcha?token=${encodeURIComponent(token)}`;
      res.statusCode = 302;
      res.setHeader('Location', target);
      res.end();
      return;
    }
    res.statusCode = 400;
    res.end('Use the WebUI captcha URL for this provider');
    return;
  }
  if (!challenge.answerHash || hashCaptchaAnswer(answer) !== challenge.answerHash) {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.end(renderCaptchaPage(token, challenge.question ?? '?', 'Wrong answer. Try again.'));
    return;
  }
  await deleteCaptchaChallenge(redis, token);
  await verificationQueue.add(
    'verificationComplete',
    {
      guildId: challenge.guildId,
      userId: challenge.userId,
      captchaProvider: challenge.provider
    },
    { removeOnComplete: 1000, removeOnFail: 500 }
  );
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  res.end('<html><body style="font-family:sans-serif;background:#111827;color:#f9fafb;display:flex;justify-content:center;align-items:center;min-height:100vh"><div><h1>Verified</h1><p>You can return to Discord.</p></div></body></html>');
}

async function approximateQueueWaiting(): Promise<number> {
  const counts = await Promise.all([
    automodQueue.getWaitingCount(),
    backupQueue.getWaitingCount(),
    birthdayQueue.getWaitingCount(),
    feedsQueue.getWaitingCount(),
    presenceQueue.getWaitingCount(),
    retentionQueue.getWaitingCount(),
    ticketQueue.getWaitingCount()
  ]);
  return counts.reduce((sum, n) => sum + n, 0);
}

export function startHealthServer(): void {
  const server = createServer(async (req, res) => {
    if (!req.url) {
      res.statusCode = 400;
      res.end('Bad Request');
      return;
    }
    const url = new URL(req.url, `http://127.0.0.1:${env.HEALTH_PORT}`);

    if (url.pathname === '/health') {
      res.statusCode = 200;
      res.end('ok');
      return;
    }

    if (url.pathname === '/metrics') {
      const auth = req.headers.authorization;
      if (!env.METRICS_TOKEN || auth !== `Bearer ${env.METRICS_TOKEN}`) {
        res.statusCode = 401;
        res.end('Unauthorized');
        return;
      }
      try {
        const snapshot = await collectMetricsSnapshot(redis);
        snapshot.queueWaiting = await approximateQueueWaiting();
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
        res.end(formatPrometheus(snapshot));
      } catch {
        res.statusCode = 500;
        res.end('metrics error');
      }
      return;
    }

    if (url.pathname === '/verify/captcha' && req.method === 'GET') {
      await handleCaptchaGet(url, res);
      return;
    }

    if (url.pathname === '/verify/captcha' && req.method === 'POST') {
      await handleCaptchaPost(req, res);
      return;
    }

    res.statusCode = 404;
    res.end('Not Found');
  });

  server.listen(env.HEALTH_PORT);
}
