/**
 * Spam screening for public enquiries (PLANNING.md §9).
 * Honeypot first (free), then Turnstile — but only when a secret is configured
 * AND a token was sent. Absent config skips the check rather than failing it,
 * so the frontend can ship the captcha field before the backend has an account.
 */
import { env } from '../../env';
import { logger } from '../../lib/logger';
import type { EnquiryInput } from '../../types/contract';

export type SpamReason = 'honeypot' | 'turnstile';

export interface ScreenResult {
  status: 'new' | 'spam';
  spamReason: SpamReason | null;
  spamScore: number;
}

export async function screen(input: EnquiryInput, ip: string): Promise<ScreenResult> {
  if (input.website && input.website.trim() !== '') {
    return { status: 'spam', spamReason: 'honeypot', spamScore: 100 };
  }

  if (env.TURNSTILE_SECRET && input.token) {
    const ok = await verifyTurnstile(env.TURNSTILE_SECRET, input.token, ip);
    if (!ok) return { status: 'spam', spamReason: 'turnstile', spamScore: 80 };
  }

  return { status: 'new', spamReason: null, spamScore: 0 };
}

async function verifyTurnstile(secret: string, token: string, ip: string): Promise<boolean> {
  try {
    const res = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
      method: 'POST',
      body: new URLSearchParams({ secret, response: token, remoteip: ip }),
      signal: AbortSignal.timeout(5000),
    });
    const json = (await res.json()) as { success?: boolean };
    return json.success === true;
  } catch (err) {
    // A Cloudflare outage must not block real enquiries — fail open.
    logger.warn({ err }, 'turnstile verification errored — failing open');
    return true;
  }
}
