/**
 * Backlink toxicity scoring
 * ──────────────────────────
 * Deterministic, rule-based scoring that runs on every backlink at sync
 * time. Produces a 0-100 score, a friendly tier label, and the list of
 * red-flag reasons that contributed to the score so the Toxic Panel can
 * render "why is this toxic?" expandable details and the Google
 * disavow file can embed the reasons as comments.
 *
 * This is a sibling to backlink-monitor.analyzeQuality(), which calls
 * an LLM. The two are complementary:
 *   • Rule-based (this file)  — runs on every link, costs nothing,
 *     catches obvious spam patterns (low DA, sketchy TLDs, casino
 *     anchors, known PBN domains). Ground truth for toxicityScore +
 *     qualityTier + toxicityReasons.
 *   • AI-based (analyzeQuality) — runs on the top N borderline links,
 *     adds qualitative judgment about PBN networks, paid placements,
 *     editorial relevance. Only allowed to OVERRIDE the deterministic
 *     toxicity score when the AI says >= 60 — the heuristic score
 *     stays canonical for everything else.
 *
 * Tier labels are capitalised + spaced ("High Risk" not "high_risk") so
 * the raw DB value can render unmodified in the UI.
 */

export interface ToxicityInput {
  domain: string;
  da: number | null;
  anchor: string | null;
  type: string | null;     // dofollow | nofollow | ugc | sponsored
  sourcePage?: string | null;
}

export type ToxicityTier = "Healthy" | "Low Risk" | "Moderate Risk" | "High Risk" | "Toxic";

export interface ToxicityResult {
  /** 0-100, higher = more toxic. Sum of weighted heuristics, capped at 100. */
  score: number;
  /** Human-readable list of which heuristics fired. Surfaced in the UI. */
  reasons: string[];
  /** Friendly bucket label stored in Backlink.qualityTier. */
  tier: ToxicityTier;
}

// ── Heuristic patterns ──────────────────────────────────────────────

/** TLDs commonly associated with throwaway/spam domains. Consistently
 *  flagged by every commercial spam scorer (Moz, Ahrefs, Majestic). */
const SUSPICIOUS_TLDS = /\.(xyz|top|club|online|site|icu|buzz|rest|quest|wang|pw|cc|tk|ml|ga|cf|gq|cyou|monster|fit|click|cam|live|stream|press|gdn)$/i;

/** Commercial-intent anchor phrases that scream SEO manipulation when
 *  they appear on a non-commercial domain linking to healthcare. */
const SPAMMY_ANCHOR_PHRASE = /\b(near me|best|top \d|cheap|buy|order|discount|free|click here|find out|read more|learn more|the best|number one|#1)\b/i;

/** Niche-irrelevant anchors. A cardiology site getting linked with
 *  these anchors is almost always a hacked / injected link. */
const IRRELEVANT_ANCHOR = /\b(casino|poker|viagra|cialis|crypto|bitcoin|forex|loan|payday|essay|term paper|adult|porn|xxx|dating|escort|gambling|betting|slots|pharmacy|weed|cbd oil)\b/i;

/** Subdomain patterns that suggest free hosting / blog farms. */
const FREE_HOST_SUBDOMAIN = /\.(blogspot|wordpress|tumblr|weebly|wix|webnode|jimdo|github\.io|netlify\.app|vercel\.app)\./i;

/** Known spam / PBN / scraper domain families that keep turning up in
 *  real-world backlink profiles. Not exhaustive — these are the ones
 *  surfaced during investigation; add to the regex as new patterns
 *  show up. Matched case-insensitively against the bare hostname. */
const KNOWN_SPAM_DOMAINS = /clientsbee|lolcurrency|businesscrystal|mytravelguidez|flusrishthishome|interestpin|e3energycubes/i;

// ── Scoring ─────────────────────────────────────────────────────────

export function calculateToxicityScore(input: ToxicityInput): ToxicityResult {
  let score = 0;
  const reasons: string[] = [];

  const domain = (input.domain || "").toLowerCase();
  const anchor = (input.anchor || "").toLowerCase();
  const sourcePage = (input.sourcePage || "").toLowerCase();
  const da = input.da ?? null;

  // ── Domain Authority ──────────────────────────────────────────
  if (da != null) {
    if (da < 5)       { score += 30; reasons.push("Very low domain authority (<5)"); }
    else if (da < 10) { score += 25; reasons.push("Very low domain authority (<10)"); }
    else if (da < 15) { score += 15; reasons.push("Low domain authority (<15)"); }
    else if (da < 20) { score += 10; reasons.push("Below average domain authority (<20)"); }
    // 20+ → no penalty; that's the healthy band for most niches
  } else {
    score += 5; reasons.push("Domain authority unknown");
  }

  // ── Anchor text ───────────────────────────────────────────────
  if (anchor) {
    if (IRRELEVANT_ANCHOR.test(anchor)) {
      score += 40;
      reasons.push("Irrelevant/harmful anchor text");
    }
    if (SPAMMY_ANCHOR_PHRASE.test(anchor)) {
      score += 15;
      reasons.push("Spammy keyword-stuffed anchor text");
    }
    if (anchor.split(/\s+/).length > 8) {
      score += 10;
      reasons.push("Overly long anchor text");
    }
  }

  // ── Domain shape ──────────────────────────────────────────────
  if (SUSPICIOUS_TLDS.test(domain)) {
    score += 20;
    reasons.push("Suspicious top-level domain");
  }
  if (FREE_HOST_SUBDOMAIN.test(`.${domain}.`)) {
    score += 15;
    reasons.push("Free-hosting subdomain (blog farm pattern)");
  }
  if (/\d{4,}/.test(domain)) {
    score += 15;
    reasons.push("Numeric pattern in domain");
  }
  const hyphenCount = (domain.match(/-/g) || []).length;
  if (hyphenCount > 2) {
    score += 10;
    reasons.push("Excessive hyphens in domain");
  }
  if (domain.length > 30) {
    score += 10;
    reasons.push("Unusually long domain name");
  }

  // ── Source page hints ─────────────────────────────────────────
  if (sourcePage && /utm_source=/i.test(sourcePage)) {
    score += 10;
    reasons.push("UTM tracking suggests paid placement");
  }
  if (sourcePage && /\/(forum|comment|guestbook|profile)\//i.test(sourcePage)) {
    score += 12;
    reasons.push("Link placed on forum/comment/profile page");
  }

  // ── Known spam domain patterns ───────────────────────────────
  if (KNOWN_SPAM_DOMAINS.test(domain)) {
    score += 25;
    reasons.push("Known spam/PBN domain pattern");
  }

  // ── Dofollow amplifier — worse than a nofollow from the same source
  if (input.type === "dofollow" && score > 30) {
    score += 10;
    reasons.push("Dofollow link from suspicious source");
  }
  if (input.type === "sponsored") {
    score += 5;
    reasons.push("Sponsored link attribute");
  }

  // ── Cap + bucket ──────────────────────────────────────────────
  const finalScore = Math.min(100, Math.max(0, score));
  const tier = scoreToTier(finalScore);

  return { score: finalScore, reasons, tier };
}

/** Re-bucket a raw score into a tier label. Handles the case where
 *  older rows have a legacy tier value (excellent/good/neutral/poor/
 *  toxic from the AI scorer) — callers can re-derive the tier from
 *  the score so the UI stays consistent. */
export function scoreToTier(score: number): ToxicityTier {
  if (score >= 80) return "Toxic";
  if (score >= 60) return "High Risk";
  if (score >= 40) return "Moderate Risk";
  if (score >= 20) return "Low Risk";
  return "Healthy";
}

/** 5-tier colour palette aligned with the user-facing spec.
 *  Kept here so both the Toxic Panel and the main table share one
 *  source of truth for the score → colour mapping. */
export const TIER_COLORS: Record<ToxicityTier, { bar: string; text: string; bg: string; border: string; emoji: string }> = {
  "Healthy":        { bar: "#10B981", text: "text-emerald-700", bg: "bg-emerald-50", border: "border-emerald-200", emoji: "✅" },
  "Low Risk":       { bar: "#84CC16", text: "text-lime-700",    bg: "bg-lime-50",    border: "border-lime-200",    emoji: "🟢" },
  "Moderate Risk":  { bar: "#F59E0B", text: "text-amber-700",   bg: "bg-amber-50",   border: "border-amber-200",   emoji: "🟡" },
  "High Risk":      { bar: "#F97316", text: "text-orange-700",  bg: "bg-orange-50",  border: "border-orange-200",  emoji: "🟠" },
  "Toxic":          { bar: "#EF4444", text: "text-red-700",     bg: "bg-red-50",     border: "border-red-200",     emoji: "☠️" },
};
