import { createAdminClient } from "@/lib/supabase/admin";
import { BANNED_AI_TELLS } from "./persona-prompts";

export interface QualityGateResult {
  passed: boolean;
  bannedWordsFound: string[];
  softWordsFound: string[];
  aiDetectorScore: number;
  reasons: string[];
}

function escapeRegex(s: string): string {
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function stripHtml(html: string): string {
  return html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
}

export function findBannedAiTells(text: string): string[] {
  const lower = " " + text.toLowerCase() + " ";
  const hits: string[] = [];
  for (const phrase of BANNED_AI_TELLS) {
    const p = phrase.toLowerCase();
    const needsWordBoundary = !p.includes(" ") && !p.endsWith(",");
    const pattern = needsWordBoundary
      ? new RegExp(`\\b${escapeRegex(p)}\\b`)
      : new RegExp(escapeRegex(p));
    if (pattern.test(lower)) hits.push(phrase);
  }
  return Array.from(new Set(hits));
}

interface BannedTermRow {
  term: string;
  ban_level: "hard" | "soft";
}

async function fetchCustomBannedTerms(): Promise<BannedTermRow[]> {
  try {
    const sb = createAdminClient();
    const { data } = await sb
      .from("banned_terms")
      .select("term, ban_level")
      .eq("is_active", true);
    return ((data || []) as any[]).map((r) => ({
      term: String(r.term || ""),
      ban_level: r.ban_level === "soft" ? "soft" : "hard",
    }));
  } catch {
    return [];
  }
}

function matchTermInText(term: string, text: string): boolean {
  const t = term.toLowerCase();
  if (!t) return false;
  const haystack = " " + text.toLowerCase() + " ";
  // word boundary for single-word terms; substring for multi-word phrases or
  // hyphenated terms where \b doesn't bind cleanly across the hyphen
  const isPhrase = t.includes(" ");
  const pattern = isPhrase
    ? new RegExp(escapeRegex(t))
    : new RegExp(`(^|[^a-z0-9])${escapeRegex(t)}([^a-z0-9]|$)`);
  return pattern.test(haystack);
}

export function aiDetectorHeuristic(text: string): number {
  const stripped = stripHtml(text);
  const words = stripped.split(/\s+/).filter(Boolean);
  if (words.length < 50) return 0.2;

  let score = 0;
  const sentences = stripped.split(/(?<=[.!?])\s+/).filter((s) => s.length > 4);

  const lower = stripped.toLowerCase();
  let aiTellCount = 0;
  for (const phrase of BANNED_AI_TELLS) {
    const p = phrase.toLowerCase();
    const re = p.includes(" ") || p.endsWith(",")
      ? new RegExp(escapeRegex(p), "g")
      : new RegExp(`\\b${escapeRegex(p)}\\b`, "g");
    const m = lower.match(re);
    if (m) aiTellCount += m.length;
  }
  score += Math.min(0.4, aiTellCount * 0.08);

  if (sentences.length > 0) {
    const avgLen = words.length / sentences.length;
    const variance =
      sentences.reduce((acc, s) => {
        const w = s.split(/\s+/).length;
        return acc + (w - avgLen) ** 2;
      }, 0) / sentences.length;
    const stddev = Math.sqrt(variance);
    if (stddev < 4) score += 0.2;
    if (avgLen > 28) score += 0.1;
  }

  const longSentenceRatio =
    sentences.filter((s) => s.split(/\s+/).length > 30).length / Math.max(1, sentences.length);
  if (longSentenceRatio > 0.4) score += 0.15;

  const tropeCount = (lower.match(/it is (worth|important) noting/g) || []).length
    + (lower.match(/in conclusion/g) || []).length
    + (lower.match(/in summary/g) || []).length;
  score += Math.min(0.2, tropeCount * 0.1);

  return Math.min(1, Math.max(0, score));
}

export async function runQualityGates(
  title: string,
  body: string,
  excerpt: string,
  detectorThreshold = 0.65
): Promise<QualityGateResult> {
  const all = `${title}\n\n${excerpt}\n\n${body}`;

  const aiTells = findBannedAiTells(all);

  const customTerms = await fetchCustomBannedTerms();
  const hardHits: string[] = [];
  const softHits: string[] = [];
  for (const row of customTerms) {
    if (!matchTermInText(row.term, all)) continue;
    if (row.ban_level === "hard") hardHits.push(row.term);
    else softHits.push(row.term);
  }

  const bannedWords = Array.from(new Set([...aiTells, ...hardHits]));
  const softWords = Array.from(new Set(softHits));

  // Base heuristic + soft-term contribution: each soft hit adds 0.05,
  // capped at +0.30 so it can shove a borderline output over the
  // threshold without single-handedly failing it.
  const baseScore = aiDetectorHeuristic(body);
  const softContribution = Math.min(0.3, softWords.length * 0.05);
  const detectorScore = Math.min(1, baseScore + softContribution);

  const reasons: string[] = [];
  if (bannedWords.length > 0) {
    reasons.push(`banned phrases: ${bannedWords.slice(0, 5).join(", ")}${bannedWords.length > 5 ? " …" : ""}`);
  }
  if (detectorScore > detectorThreshold) {
    reasons.push(
      `AI-detector score ${detectorScore.toFixed(2)} > threshold ${detectorThreshold.toFixed(2)}` +
        (softWords.length ? ` (soft hits: ${softWords.slice(0, 5).join(", ")}${softWords.length > 5 ? " …" : ""})` : "")
    );
  }

  return {
    passed: reasons.length === 0,
    bannedWordsFound: bannedWords,
    softWordsFound: softWords,
    aiDetectorScore: detectorScore,
    reasons,
  };
}
