/**
 * AI Content Humanization Scorer
 * ──────────────────────────────
 * Purely algorithmic — no external API. Analyzes text on 8 linguistic
 * dimensions and returns a 0–100 score indicating how "human" the
 * writing reads. Used to catch AI-generated content before publishing.
 */

export interface ScoringFactor {
  name: string;
  score: number; // 0-100
  weight: number;
  feedback: string;
}

export interface HumanizationResult {
  score: number;
  grade: "Very Human" | "Mostly Human" | "Mixed" | "AI-Detected" | "Strongly AI";
  factors: ScoringFactor[];
  suggestions: string[];
}

// ── Helpers ──────────────────────────────────────────────────────────────────

function sentences(text: string): string[] {
  return text.split(/(?<=[.!?])\s+/).map((s) => s.trim()).filter((s) => s.length > 5);
}
function paragraphs(text: string): string[] {
  return text.split(/\n\s*\n/).map((p) => p.trim()).filter((p) => p.length > 20);
}
function words(text: string): string[] {
  return text.split(/\s+/).filter((w) => w.length > 0);
}
function stddev(nums: number[]): number {
  if (nums.length < 2) return 0;
  const mean = nums.reduce((a, b) => a + b, 0) / nums.length;
  return Math.sqrt(nums.reduce((s, n) => s + (n - mean) ** 2, 0) / nums.length);
}
function clamp(v: number, min = 0, max = 100): number {
  return Math.max(min, Math.min(max, Math.round(v)));
}
function lerp(value: number, low: number, high: number): number {
  if (value <= low) return 0;
  if (value >= high) return 100;
  return ((value - low) / (high - low)) * 100;
}

// ── AI-typical transition words ──────────────────────────────────────────────
const AI_WORDS = /\b(furthermore|moreover|additionally|in conclusion|it['']?s worth noting|it['']?s important to|delve|leverage|utilize|comprehensive|robust|crucial|pivotal|realm|landscape|paradigm|synergy|streamline|foster|facilitate|underscore|noteworthy|intricacies)\b/gi;

// ── Passive voice detector (simplified) ──────────────────────────────────────
const PASSIVE_RE = /\b(is|are|was|were|been|being|gets?|got)\s+(being\s+)?\w+(ed|en|own|ung|ept)\b/gi;

// ── Contraction patterns ─────────────────────────────────────────────────────
const CONTRACTIONS = /\b(don['']t|won['']t|can['']t|isn['']t|aren['']t|wasn['']t|weren['']t|couldn['']t|shouldn['']t|wouldn['']t|it['']s|that['']s|there['']s|here['']s|who['']s|what['']s|let['']s|we['']re|they['']re|you['']re|i['']m|he['']s|she['']s|we['']ve|they['']ve|i['']ve|you['']ve|we['']ll|they['']ll|i['']ll|you['']ll)\b/gi;

// ── Main scorer ──────────────────────────────────────────────────────────────

export function scoreHumanization(content: string): HumanizationResult {
  // Strip markdown headings for analysis
  const clean = content.replace(/^#{1,3}\s+.+$/gm, "").trim();
  const sents = sentences(clean);
  const paras = paragraphs(clean);
  const wordList = words(clean);
  const wordCount = wordList.length;
  const factors: ScoringFactor[] = [];
  const suggestions: string[] = [];

  // 1. Sentence length variety (20%)
  const sentLengths = sents.map((s) => words(s).length);
  const sentMean = sentLengths.length ? sentLengths.reduce((a, b) => a + b, 0) / sentLengths.length : 0;
  const sentStd = stddev(sentLengths);
  const sentVariety = sentMean > 0 ? (sentStd / sentMean) * 100 : 0;
  const sentScore = clamp(lerp(sentVariety, 10, 40));
  factors.push({ name: "Sentence length variety", score: sentScore, weight: 20, feedback: sentScore >= 70 ? "Good sentence length variation" : "Sentences are too uniform in length — mix short punchy sentences with longer complex ones" });
  if (sentScore < 50) suggestions.push("Vary sentence lengths — add some short (5-8 word) and long (25+ word) sentences");

  // 2. Paragraph length variety (10%)
  const paraLengths = paras.map((p) => words(p).length);
  const paraMean = paraLengths.length ? paraLengths.reduce((a, b) => a + b, 0) / paraLengths.length : 0;
  const paraStd = stddev(paraLengths);
  const paraVariety = paraMean > 0 ? (paraStd / paraMean) * 100 : 0;
  const paraScore = clamp(lerp(paraVariety, 10, 40));
  factors.push({ name: "Paragraph length variety", score: paraScore, weight: 10, feedback: paraScore >= 70 ? "Good paragraph length variation" : "Paragraphs are too uniform — mix 1-2 sentence paragraphs with longer ones" });

  // 3. Transition word overuse (15%)
  const aiMatches = clean.match(AI_WORDS) || [];
  const aiPer1000 = wordCount > 0 ? (aiMatches.length / wordCount) * 1000 : 0;
  const transScore = clamp(lerp(10 - aiPer1000, 0, 8) * 100 / 100);
  factors.push({ name: "Transition word naturalness", score: transScore, weight: 15, feedback: transScore >= 70 ? "Minimal AI-typical phrasing" : `Found ${aiMatches.length} AI-typical words (${aiMatches.slice(0, 3).map(w => `"${w}"`).join(", ")})` });
  if (transScore < 50 && aiMatches.length > 0) suggestions.push(`Replace AI-typical words: ${[...new Set(aiMatches.map(w => w.toLowerCase()))].slice(0, 4).join(", ")}`);

  // 4. Passive voice ratio (10%)
  const passiveCount = (clean.match(PASSIVE_RE) || []).length;
  const passiveRatio = sents.length > 0 ? (passiveCount / sents.length) * 100 : 0;
  const passiveScore = clamp(lerp(30 - passiveRatio, 0, 20) * 100 / 100);
  factors.push({ name: "Active voice usage", score: passiveScore, weight: 10, feedback: passiveScore >= 70 ? "Good active voice balance" : `${passiveRatio.toFixed(0)}% passive sentences — convert some to active voice` });
  if (passiveScore < 50) suggestions.push("Convert passive voice to active — e.g., change 'was completed by' to 'completed'");

  // 5. First person / personal voice (10%)
  const firstPerson = (clean.match(/\b(I|we|our|my|us)\b/g) || []).length;
  const fpPer100 = wordCount > 0 ? (firstPerson / wordCount) * 100 : 0;
  const fpScore = fpPer100 > 1.5 ? 100 : fpPer100 > 0.3 ? 70 : wordCount > 200 ? 20 : 50;
  factors.push({ name: "Personal voice", score: clamp(fpScore), weight: 10, feedback: fpScore >= 70 ? "Natural personal voice present" : "Content reads impersonal — add first-person perspective where appropriate" });
  if (fpScore < 50) suggestions.push("Add personal voice — use 'we', 'our team', or 'in our experience' in 2-3 places");

  // 6. Sentence starter variety (15%)
  const starters = sents.map((s) => words(s).slice(0, 2).join(" ").toLowerCase());
  const starterCounts = new Map<string, number>();
  starters.forEach((s) => starterCounts.set(s, (starterCounts.get(s) || 0) + 1));
  const maxRepeat = Math.max(0, ...starterCounts.values());
  const repeatRatio = starters.length > 0 ? (maxRepeat / starters.length) * 100 : 0;
  const starterScore = clamp(lerp(40 - repeatRatio, 0, 30) * 100 / 100);
  factors.push({ name: "Sentence starter variety", score: starterScore, weight: 15, feedback: starterScore >= 70 ? "Good variety in sentence openings" : "Too many sentences start the same way — vary your openings" });
  if (starterScore < 50) { const top = [...starterCounts.entries()].sort((a, b) => b[1] - a[1])[0]; if (top) suggestions.push(`Vary sentence starters — "${top[0]}..." appears ${top[1]} times`); }

  // 7. Contractions usage (10%)
  const contractionCount = (clean.match(CONTRACTIONS) || []).length;
  const contPer1000 = wordCount > 0 ? (contractionCount / wordCount) * 1000 : 0;
  const contScore = clamp(contPer1000 >= 8 ? 100 : contPer1000 >= 3 ? 80 : contPer1000 >= 1 ? 50 : 15);
  factors.push({ name: "Contraction usage", score: contScore, weight: 10, feedback: contScore >= 70 ? "Natural use of contractions" : "No contractions — add don't, won't, it's, etc. for natural tone" });
  if (contScore < 50) suggestions.push("Add contractions — change 'do not' to 'don't', 'it is' to 'it's', etc.");

  // 8. Readability naturalness (10%)
  const avgSentLen = sentMean;
  const readScore = clamp(avgSentLen >= 10 && avgSentLen <= 22 ? 90 : avgSentLen >= 8 && avgSentLen <= 28 ? 60 : 30);
  factors.push({ name: "Readability naturalness", score: readScore, weight: 10, feedback: readScore >= 70 ? "Natural reading level" : avgSentLen > 22 ? "Sentences too long on average — break some up" : "Sentences too short — add some complexity" });

  // ── Weighted total ──
  const total = factors.reduce((sum, f) => sum + f.score * f.weight, 0) / factors.reduce((sum, f) => sum + f.weight, 0);
  const score = clamp(Math.round(total));

  const grade =
    score >= 80 ? "Very Human" :
    score >= 60 ? "Mostly Human" :
    score >= 40 ? "Mixed" :
    score >= 20 ? "AI-Detected" : "Strongly AI";

  return { score, grade, factors, suggestions };
}
