import path from "node:path";
import fs from "node:fs/promises";
import yaml from "js-yaml";
import { XMLParser } from "fast-xml-parser";

const PLUS_THEMATIC_URL =
  "https://www.jambox.pl/xml/slownik-pakietytematyczneplus.xml";
const PREMIUM_URL = "https://www.jambox.pl/xml/slownik-pakietypremium.xml";

/**
 * .env: JAMBOX_ADMIN_TOKEN="..."
 */
function isAuthorized(request) {
  const expected = import.meta.env.JAMBOX_ADMIN_TOKEN;
  if (!expected) return false;
  const token = request.headers.get("x-admin-token");
  return token === expected;
}

async function fetchXml(url) {
  const res = await fetch(url);
  if (!res.ok) {
    throw new Error(
      `Błąd pobierania XML: ${res.status} ${res.statusText} (${url})`,
    );
  }
  return await res.text();
}

/**
 * XML:
 * <xml>
 *   <node><name>...</name><tid>...</tid></node>
 *   <node>...</node>
 * </xml>
 */
function parseNodes(xmlText) {
  const parser = new XMLParser({
    ignoreAttributes: false,
    attributeNamePrefix: "@_",
  });

  const json = parser.parse(xmlText);
  const root = json.xml ?? json;

  let nodes = root?.node ?? [];
  if (!nodes) return [];
  if (!Array.isArray(nodes)) nodes = [nodes];

  return nodes;
}

function toInt(v) {
  const n = Number(v);
  return Number.isFinite(n) ? n : null;
}

function normalizeName(name) {
  let s = String(name || "").trim();

  // często XML ma "Pakiet ..." a u Ciebie w YAML jest bez "Pakiet"
  s = s.replace(/^pakiet\s+/i, "");

  // "+Max" -> "+ Max"
  s = s.replace(/\+([A-Za-zĄĆĘŁŃÓŚŹŻ])/g, "+ $1");

  // pojedyncze spacje
  s = s.replace(/\s+/g, " ").trim();

  return s;
}

function stripDiacritics(s) {
  return String(s || "")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "");
}

function slugifyId(name) {
  const s = stripDiacritics(String(name || "").toLowerCase())
    .replace(/\+/g, " plus ")
    .replace(/&/g, " and ")
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "")
    .replace(/_+/g, "_");
  return s || "pakiet";
}

function buildXmlMap(nodesA, nodesB) {
  const byTid = new Map();
  const byNormName = new Map();

  function add(nodes, source) {
    for (const n of nodes || []) {
      const tid = toInt(n?.tid);
      const rawName = String(n?.name ?? "").trim();
      const name = normalizeName(rawName);

      if (!tid || !name) continue;

      if (!byTid.has(tid)) byTid.set(tid, { tid, name, rawName, source });

      const key = stripDiacritics(name).toLowerCase();
      if (!byNormName.has(key)) byNormName.set(key, { tid, name, rawName, source });
    }
  }

  add(nodesA, "thematic_plus");
  add(nodesB, "premium");

  return { byTid, byNormName };
}

export async function POST({ request }) {
  try {
    if (!isAuthorized(request)) {
      return new Response(JSON.stringify({ ok: false, error: "Unauthorized" }), {
        status: 401,
        headers: { "Content-Type": "application/json" },
      });
    }

    const body = await request.json().catch(() => ({}));
    const dryRun = body?.dryRun === true;

    // domyślnie: tylko aktualizacja (bez dopisywania nowych)
    const addMissing = body?.addMissing === true;
    const updateNames = body?.updateNames !== false; // default true

    const YAML_PATH =
      import.meta.env.JAMBOX_TV_ADDONS_YAML_PATH ||
      path.join(
        process.cwd(),
        "src",
        "content",
        "internet-telewizja",
        "tv-addons.yaml",
      );

    // 1) XML
    const [xmlA, xmlB] = await Promise.all([
      fetchXml(PLUS_THEMATIC_URL),
      fetchXml(PREMIUM_URL),
    ]);

    const nodesA = parseNodes(xmlA);
    const nodesB = parseNodes(xmlB);

    const { byTid, byNormName } = buildXmlMap(nodesA, nodesB);

    // 2) YAML
    const rawYaml = await fs.readFile(YAML_PATH, "utf8");
    const doc = yaml.load(rawYaml);

    if (!doc || !Array.isArray(doc.dodatki)) {
      return new Response(
        JSON.stringify({ ok: false, error: "YAML: brak doc.dodatki" }),
        { status: 400, headers: { "Content-Type": "application/json" } },
      );
    }

    const changedItems = [];
    const unchangedItems = [];
    const skippedItems = [];
    const addedItems = [];

    // indeksy istniejących (dla addMissing)
    const existingByTid = new Map();
    const existingByNormName = new Map();

    for (const a of doc.dodatki) {
      const tid = toInt(a?.tid);
      const nm = normalizeName(String(a?.nazwa ?? ""));
      if (tid != null) existingByTid.set(tid, a);
      if (nm) existingByNormName.set(stripDiacritics(nm).toLowerCase(), a);
    }

    // 2a) aktualizacje
    for (const addon of doc.dodatki) {
      const id = addon?.id ?? null;

      const before = {
        tid: toInt(addon?.tid),
        nazwa: String(addon?.nazwa ?? ""),
      };

      let match = null;

      // najpierw po tid (najpewniejsze)
      if (before.tid != null && byTid.has(before.tid)) {
        match = byTid.get(before.tid);
      } else {
        // potem po nazwie
        const key = stripDiacritics(normalizeName(before.nazwa)).toLowerCase();
        if (key && byNormName.has(key)) match = byNormName.get(key);
      }

      if (!match) {
        skippedItems.push({ id, reason: "brak dopasowania w XML", before });
        continue;
      }

      const after = {
        tid: match.tid,
        nazwa: updateNames ? match.name : before.nazwa,
      };

      const willChange =
        before.tid !== after.tid || before.nazwa !== after.nazwa;

      if (!willChange) {
        unchangedItems.push({ id, tid: before.tid, nazwa: before.nazwa });
        continue;
      }

      if (!dryRun) {
        addon.tid = after.tid;
        if (updateNames) addon.nazwa = after.nazwa;
      }

      changedItems.push({
        id,
        before,
        after,
        matchedFrom: match.source,
      });
    }

    // 2b) opcjonalnie dopisz brakujące pakiety z XML do YAML
    if (addMissing) {
      for (const [tid, it] of byTid.entries()) {
        if (existingByTid.has(tid)) continue;

        const key = stripDiacritics(it.name).toLowerCase();
        if (existingByNormName.has(key)) continue;

        const newId = slugifyId(it.name);

        const newItem = {
          id: newId,
          nazwa: it.name,
          tid: tid,
          typ: "checkbox",
          opis: "",
          cena: [],
        };

        if (!dryRun) doc.dodatki.push(newItem);

        addedItems.push({ id: newId, tid, nazwa: it.name, source: it.source });
      }
    }

    if (!dryRun && (changedItems.length > 0 || addedItems.length > 0)) {
      const newYaml = yaml.dump(doc, {
        lineWidth: -1,
        noRefs: true,
        sortKeys: false,
      });
      await fs.writeFile(YAML_PATH, newYaml, "utf8");
    }

    return new Response(
      JSON.stringify({
        ok: true,
        dryRun,
        yamlPath: YAML_PATH,
        options: { addMissing, updateNames },
        xmlCounts: {
          thematic_plus: nodesA.length,
          premium: nodesB.length,
          uniqueByTid: byTid.size,
        },
        changed: changedItems.length,
        added: addedItems.length,
        unchanged: unchangedItems.length,
        skipped: skippedItems.length,
        changedItems,
        addedItems,
        skippedItems,
      }),
      { status: 200, headers: { "Content-Type": "application/json" } },
    );
  } catch (err) {
    console.error("update-tv-addons error:", err);
    return new Response(
      JSON.stringify({ ok: false, error: String(err?.message ?? err) }),
      { status: 500, headers: { "Content-Type": "application/json" } },
    );
  }
}
