// src/lib/keywords/ytg-parse.ts — PURE parser for YTG/Lumenia brief + guide .txt files.
// No server/db imports: safe to call from anywhere.
import type { KeywordBrief } from "./ytg-types";

const LANG_MAP: Record<string, string> = {
  English: "en",
  French: "fr",
  Spanish: "es",
  German: "de",
  Italian: "it",
  Portuguese: "pt",
  Dutch: "nl",
};

const COUNTRY_MAP: Record<string, string> = {
  USA: "US",
  "United States": "US",
  "United States of America": "US",
  "U.S.": "US",
  US: "US",
  UK: "GB",
  "United Kingdom": "GB",
  "Great Britain": "GB",
  England: "GB",
  GB: "GB",
  Ireland: "IE",
  "New Zealand": "NZ",
  France: "FR",
  Belgium: "BE",
  Canada: "CA",
  Australia: "AU",
  Spain: "ES",
  Germany: "DE",
  Austria: "AT",
  Italy: "IT",
  Portugal: "PT",
  Netherlands: "NL",
  Switzerland: "CH",
  Brazil: "BR",
  Mexico: "MX",
  Argentina: "AR",
};

/** Derive ISO lang + alpha-2 country from a raw "Name (Place)" language string. */
function parseLocale(language: string): { lang: string; country: string } {
  const m = language.match(/^(.+?)\s*\(([^)]+)\)\s*$/);
  if (!m) return { lang: "", country: "" };
  const lang = LANG_MAP[m[1].trim()] ?? "";
  const country = COUNTRY_MAP[m[2].trim()] ?? "";
  return { lang, country };
}

/** Best-effort detect {lang,country} from the raw .txt contents (reads the
 *  "Language:" line). Returns "" for fields it can't map. */
export function detectLocaleFromTexts(texts: string[]): {
  lang: string;
  country: string;
} {
  for (const t of texts) {
    const language = matchLine(t, /^Language\s*:\s*(.+)$/m);
    if (language) return parseLocale(language);
  }
  return { lang: "", country: "" };
}

function isBriefFile(t: string): boolean {
  return t.includes("SEO Brief") || t.includes("Context and Objective");
}

function isGuideFile(t: string): boolean {
  return t.includes("SEO GUIDE") || t.includes("== TOP TERMS ==");
}

function matchLine(text: string, re: RegExp): string {
  const m = text.match(re);
  return m ? m[1].trim() : "";
}

/** Strip a leading "- " bullet marker from a trimmed line. */
function stripBullet(line: string): string {
  return line.replace(/^-\s*/, "").trim();
}

interface BriefSections {
  primaryIntent: string;
  objectives: string[];
  keySubjects: string[];
  questions: string[];
  tone: string[];
  style: string[];
  valueProps: string[];
}

/** The list-typed (string[]) fields of BriefSections. */
type BriefListField = Exclude<keyof BriefSections, "primaryIntent">;

/** Map a sub-label to a target list field; null if not a recognized label. */
const SUBLABEL_FIELDS: Record<string, BriefListField> = {
  Objective: "objectives",
  "Key Subjects": "keySubjects",
  "Questions to consider": "questions",
  "Recommended Tone": "tone",
  "Recommended Style": "style",
};

function parseBrief(text: string): BriefSections {
  const out: BriefSections = {
    primaryIntent: "",
    objectives: [],
    keySubjects: [],
    questions: [],
    tone: [],
    style: [],
    valueProps: [],
  };
  const lines = text.split(/\r?\n/);
  let currentSection = "";
  let currentLabel: BriefListField | null = null;
  const isRuleLine = (s: string) => /^=+$/.test(s) && s.length > 0;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i].trim();

    // Footer: stop collecting.
    if (line === "---" || line.startsWith("Generated by")) break;

    if (line === "" || isRuleLine(line)) continue;

    // A section title is a line sandwiched between two `===` rule lines.
    const prev = i > 0 ? lines[i - 1].trim() : "";
    const next = i + 1 < lines.length ? lines[i + 1].trim() : "";
    if (isRuleLine(prev) && isRuleLine(next)) {
      currentSection = line;
      currentLabel = null;
      continue;
    }

    // Inline "Primary Intent: X".
    const intentM = line.match(/^Primary Intent\s*:\s*(.+)$/);
    if (intentM) {
      out.primaryIntent = intentM[1].trim();
      currentLabel = null;
      continue;
    }

    // Sub-label line (ends with ':' and matches a known label).
    if (/:$/.test(line)) {
      const label = line.replace(/:$/, "").trim();
      const field = SUBLABEL_FIELDS[label];
      currentLabel = field ?? null;
      continue;
    }

    // Bullet line.
    if (line.startsWith("-")) {
      const value = stripBullet(line);
      if (!value) continue;
      if (currentLabel) {
        out[currentLabel].push(value);
      } else if (currentSection === "Value Proposition") {
        out.valueProps.push(value);
      }
    }
  }

  return out;
}

interface GuideSections {
  topTerms: string[];
  twoGrams: string[];
  threeGrams: string[];
  entities: string[];
}

const GUIDE_SECTION_FIELDS: Record<string, keyof GuideSections> = {
  "TOP TERMS": "topTerms",
  "TWO GRAMS": "twoGrams",
  "THREE GRAMS": "threeGrams",
  "NAMED ENTITIES": "entities",
};

function parseGuide(text: string): GuideSections {
  const out: GuideSections = {
    topTerms: [],
    twoGrams: [],
    threeGrams: [],
    entities: [],
  };
  const lines = text.split(/\r?\n/);
  let currentField: keyof GuideSections | null = null;

  for (const raw of lines) {
    const line = raw.trim();
    if (line === "") continue;

    const sectionM = line.match(/^==\s*(.+?)\s*==$/);
    if (sectionM) {
      currentField = GUIDE_SECTION_FIELDS[sectionM[1].trim()] ?? null;
      continue;
    }

    if (!currentField) continue;

    const termM = line.match(/^\d+\.\s*(.+)$/);
    if (termM) {
      const term = termM[1].trim();
      if (term) out[currentField].push(term);
    }
  }

  return out;
}

/** Parse 1..2 YTG/Lumenia .txt contents into a structured KeywordBrief. */
export function parseYtgTexts(
  texts: string[],
  importedAt: string,
): { ok: true; brief: KeywordBrief } | { ok: false; error: string } {
  let briefText = "";
  let guideText = "";
  for (const t of texts) {
    if (!briefText && isBriefFile(t)) briefText = t;
    else if (!guideText && isGuideFile(t)) guideText = t;
  }

  if (!briefText && !guideText) {
    return {
      ok: false,
      error: "Fichier YTG non reconnu (brief/guide manquant).",
    };
  }

  // Query + Language can come from whichever file has them.
  const querySrc = briefText || guideText;
  const langSrc = briefText || guideText;
  const query =
    matchLine(briefText, /^Query\s*:\s*(.+)$/m) ||
    matchLine(guideText, /^Query\s*:\s*(.+)$/m) ||
    matchLine(querySrc, /^Query\s*:\s*(.+)$/m);
  const language =
    matchLine(briefText, /^Language\s*:\s*(.+)$/m) ||
    matchLine(guideText, /^Language\s*:\s*(.+)$/m) ||
    matchLine(langSrc, /^Language\s*:\s*(.+)$/m);

  const { lang, country } = parseLocale(language);

  const briefParts = briefText
    ? parseBrief(briefText)
    : {
        primaryIntent: "",
        objectives: [],
        keySubjects: [],
        questions: [],
        tone: [],
        style: [],
        valueProps: [],
      };

  const guideParts = guideText
    ? parseGuide(guideText)
    : { topTerms: [], twoGrams: [], threeGrams: [], entities: [] };

  const brief: KeywordBrief = {
    source: "ytg",
    query,
    language,
    lang,
    country,
    importedAt,
    primaryIntent: briefParts.primaryIntent,
    objectives: briefParts.objectives,
    keySubjects: briefParts.keySubjects,
    questions: briefParts.questions,
    tone: briefParts.tone,
    style: briefParts.style,
    valueProps: briefParts.valueProps,
    topTerms: guideParts.topTerms,
    twoGrams: guideParts.twoGrams,
    threeGrams: guideParts.threeGrams,
    entities: guideParts.entities,
    rawBrief: briefText,
    rawGuide: guideText,
  };

  return { ok: true, brief };
}
