/**
 * POST /api/public-audit — unauthenticated lead-gen audit trigger.
 *
 * Body: { firstName, lastName, email, companyName, phoneNumber, websiteUrl }
 *
 * Flow:
 *  1. Rate-limit by IP (3 req / hour)
 *  2. Validate fields + URL
 *  3. Check lead gate (returning lead → redirect, skip OTP)
 *  4. New lead → send OTP, return { verificationRequired: true }
 *     The Lead and Audit are NOT created until /api/public-audit/verify succeeds.
 */

import { NextResponse } from "next/server";
import { randomInt } from "crypto";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";
import { extractClientIp } from "@/lib/user-agent";
import { normalizeEmail, normalizePhone } from "@/lib/audit/normalize";
import { sendVerificationCode } from "@/lib/email/send-verification";
import { createLeadAndTriggerAudit } from "@/lib/audit/create-public-audit";

export const dynamic = "force-dynamic";
export const maxDuration = 800;

// ─── In-memory rate limiter (3 req / IP / hour) ───────────────────────────────

interface RateLimitEntry { count: number; windowStart: number }
const _rl = new Map<string, RateLimitEntry>();
const RL_MAX = 3;
const RL_WINDOW_MS = 60 * 60 * 1000;

let _rlPruneCounter = 0;
function checkRateLimit(ip: string): boolean {
  const now = Date.now();
  if (++_rlPruneCounter % 100 === 0) {
    for (const [key, entry] of _rl.entries()) {
      if (now - entry.windowStart > RL_WINDOW_MS) _rl.delete(key);
    }
  }
  const entry = _rl.get(ip);
  if (!entry || now - entry.windowStart > RL_WINDOW_MS) {
    _rl.set(ip, { count: 1, windowStart: now });
    return true;
  }
  if (entry.count >= RL_MAX) return false;
  entry.count++;
  return true;
}

// ─── Free email provider list ─────────────────────────────────────────────────

const FREE_EMAIL_DOMAINS = new Set([
  "gmail.com", "googlemail.com",
  "yahoo.com", "yahoo.co.uk", "yahoo.fr", "yahoo.de", "yahoo.es", "yahoo.it",
  "yahoo.ca", "yahoo.com.au", "ymail.com",
  "hotmail.com", "hotmail.co.uk", "hotmail.fr", "hotmail.de", "hotmail.es", "hotmail.it",
  "outlook.com", "outlook.co.uk", "live.com", "msn.com",
  "icloud.com", "me.com", "mac.com",
  "aol.com", "mail.com",
  "protonmail.com", "proton.me", "pm.me",
  "gmx.com", "gmx.net", "gmx.de",
]);

function isFreeEmail(email: string): boolean {
  const domain = email.split("@")[1]?.toLowerCase() ?? "";
  return FREE_EMAIL_DOMAINS.has(domain);
}

// ─── URL validation ───────────────────────────────────────────────────────────

const DENY_HOSTNAMES = new Set([
  "localhost", "127.0.0.1", "0.0.0.0", "::1",
  "example.com", "example.org", "example.net",
  "test.com", "test.org", "invalid",
]);

const PRIVATE_IP_RE = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|169\.254\.|fc|fd|fe80)/i;

function validateUrl(raw: string): { ok: true; url: string } | { ok: false; error: string } {
  let parsed: URL;
  try {
    const withScheme = /^https?:\/\//i.test(raw) ? raw : `https://${raw}`;
    parsed = new URL(withScheme);
  } catch {
    return { ok: false, error: "Invalid URL format" };
  }

  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
    return { ok: false, error: "URL must use http or https" };
  }

  const host = parsed.hostname.toLowerCase();
  if (DENY_HOSTNAMES.has(host)) return { ok: false, error: "URL not allowed" };
  if (PRIVATE_IP_RE.test(host)) return { ok: false, error: "Private IP addresses are not allowed" };
  if (!host.includes(".")) return { ok: false, error: "URL must be a valid domain" };

  return { ok: true, url: parsed.origin + parsed.pathname.replace(/\/$/, "") || parsed.origin };
}

// ─── Phone validation ─────────────────────────────────────────────────────────

function isValidPhone(raw: string): boolean {
  const digits = raw.replace(/\D/g, "");
  return digits.length >= 7 && digits.length <= 15;
}

// ─── Email masking (j***@example.com) ────────────────────────────────────────

function maskEmail(email: string): string {
  const [local, domain] = email.split("@");
  if (!local || !domain) return email;
  const masked = local.length <= 2 ? local[0] + "***" : local[0] + "***";
  return `${masked}@${domain}`;
}

// ─── Request handler ──────────────────────────────────────────────────────────

export async function POST(req: Request) {
  const ip = extractClientIp(req) ?? "unknown";

  if (!checkRateLimit(ip)) {
    console.log(`[public-audit] rate limit hit: ip=${ip}`);
    return NextResponse.json(
      { error: "Too many requests. Please try again in an hour." },
      { status: 429 },
    );
  }

  let body: {
    firstName?: string;
    lastName?: string;
    email?: string;
    companyName?: string;
    phoneNumber?: string;
    websiteUrl?: string;
  };
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
  }

  const firstName = (body.firstName ?? "").trim();
  const lastName = (body.lastName ?? "").trim();
  const email = (body.email ?? "").trim().toLowerCase();
  const companyName = (body.companyName ?? "").trim();
  const phoneNumber = (body.phoneNumber ?? "").trim();
  const rawUrl = (body.websiteUrl ?? "").trim();

  // Field presence
  const missing: string[] = [];
  if (!firstName) missing.push("firstName");
  if (!lastName) missing.push("lastName");
  if (!email) missing.push("email");
  if (!companyName) missing.push("companyName");
  if (!phoneNumber) missing.push("phoneNumber");
  if (!rawUrl) missing.push("websiteUrl");
  if (missing.length > 0) {
    return NextResponse.json({ error: `Missing required fields: ${missing.join(", ")}` }, { status: 400 });
  }

  if (!email.includes("@") || !email.includes(".")) {
    return NextResponse.json({ error: "Please enter a valid email address." }, { status: 422 });
  }
  if (isFreeEmail(email)) {
    return NextResponse.json({ error: "Please use your business email address." }, { status: 422 });
  }
  if (!isValidPhone(phoneNumber)) {
    return NextResponse.json({ error: "Please enter a valid phone number." }, { status: 422 });
  }

  const urlCheck = validateUrl(rawUrl);
  if (!urlCheck.ok) {
    return NextResponse.json({ error: urlCheck.error }, { status: 422 });
  }

  const emailDomain = email.split("@")[1] ?? "";
  const normEmail = normalizeEmail(email);
  const normPhone = normalizePhone(phoneNumber);

  // ── Returning lead gate: skip OTP, redirect ───────────────────────────────
  const existingLead = await prisma.lead.findFirst({
    where: { OR: [{ normalizedEmail: normEmail }, { normalizedPhone: normPhone }] },
    orderBy: { createdAt: "desc" },
    select: { id: true, auditId: true, normalizedEmail: true, normalizedPhone: true },
  });

  if (existingLead?.auditId) {
    const existingAudit = await prisma.audit.findUnique({
      where: { id: existingLead.auditId },
      select: { id: true, status: true },
    });

    if (existingAudit && existingAudit.status !== "failed") {
      const matchedOn =
        existingLead.normalizedEmail === normEmail && existingLead.normalizedPhone === normPhone
          ? "both"
          : existingLead.normalizedEmail === normEmail ? "email" : "phone";

      console.log(`[public-audit] existing-lead matched=${matchedOn} auditId=${existingAudit.id} action=redirect`);

      prisma.platformEvent.create({
        data: {
          userId: null, brandId: null,
          action: "public_audit.gate_hit",
          category: "audit",
          metadata: { emailDomain, hadExistingAudit: true, matchedOn },
          ipAddress: ip,
        },
      }).catch(() => {/* non-fatal */});

      return NextResponse.json(
        {
          existingAudit: true,
          auditId: existingAudit.id,
          resultUrl: `/audit/${existingAudit.id}`,
          status: existingAudit.status === "completed" ? "completed" : "in_progress",
        },
        { status: 200 },
      );
    }
  }

  // ── Bypass OTP via env var (testing/staging) ─────────────────────────────
  if (process.env.BYPASS_PUBLIC_AUDIT_VERIFICATION === "true") {
    const ua = req.headers.get("user-agent") ?? "";
    console.log(`[public-audit] bypass=true reason=BYPASS_PUBLIC_AUDIT_VERIFICATION_set email=${maskEmail(email)}`);
    const result = await createLeadAndTriggerAudit({
      formData: { firstName, lastName, email, companyName, phoneNumber, websiteUrl: urlCheck.url },
      ipAddress: ip,
      userAgent: ua,
      bypass: true,
    });
    return NextResponse.json({ auditId: result.auditId, statusUrl: result.statusUrl, leadId: result.leadId }, { status: 202 });
  }

  // ── New lead: send OTP, hold audit creation until verify ─────────────────
  const code = String(randomInt(100000, 999999));
  const codeHash = await bcrypt.hash(code, 10);
  const expiresAt = new Date(Date.now() + 10 * 60 * 1000);

  // Delete any prior unexpired verifications for this email
  await prisma.emailVerification.deleteMany({
    where: { email: normEmail, verifiedAt: null },
  });

  await prisma.emailVerification.create({
    data: { email: normEmail, codeHash, expiresAt, ipAddress: ip },
  });

  const sent = await sendVerificationCode(email, code);
  if (!sent) {
    return NextResponse.json(
      { error: "Failed to send verification email. Please try again." },
      { status: 500 },
    );
  }

  prisma.platformEvent.create({
    data: {
      userId: null, brandId: null,
      action: "public_audit.verification_sent",
      category: "audit",
      metadata: { emailDomain },
      ipAddress: ip,
    },
  }).catch(() => {/* non-fatal */});

  console.log(`[public-audit] verification-sent email=${email} ip=${ip}`);

  return NextResponse.json(
    {
      verificationRequired: true,
      maskedEmail: maskEmail(email),
      // Echo form data back so the verify endpoint can complete the Lead+Audit
      formData: { firstName, lastName, email, companyName, phoneNumber, websiteUrl: urlCheck.url },
    },
    { status: 202 },
  );
}
