// scripts/importJamboxChannelLists.js
import Database from "better-sqlite3";
import { XMLParser } from "fast-xml-parser";

const DB_PATH =
  process.env.FUZ_DB_PATH || "./src/data/ServicesRange.db";

// źródła: URL + docelowy pakiet (source + slug)
const FEEDS = [
  {
    url: "https://www.jambox.pl/xml/listakanalow-smart.xml",
    source: "EVIO",
    slug: "smart",
  },
  {
    url: "https://www.jambox.pl/xml/listakanalow-optimum.xml",
    source: "EVIO",
    slug: "optimum",
  },
  {
    url: "https://www.jambox.pl/xml/listakanalow-platinum.xml",
    source: "EVIO",
    slug: "platinum",
  },
  {
    url: "https://www.jambox.pl/xml/listakanalow-pluspodstawowy.xml",
    source: "PLUS",
    slug: "podstawowy",
  },
  {
    url: "https://www.jambox.pl/xml/listakanalow-pluskorzystny.xml",
    source: "PLUS",
    slug: "korzystny",
  },
  {
    url: "https://www.jambox.pl/xml/listakanalow-plusbogaty.xml",
    source: "PLUS",
    slug: "bogaty",
  },
];

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

/**
 * Parser XML -> tablica node'ów z <xml><node>...</node></xml>
 */
function parseNodes(xmlText) {
  const parser = new XMLParser({
    ignoreAttributes: false,
    attributeNamePrefix: "@_",
  });

  const json = parser.parse(xmlText);
  let nodes = json.xml?.node ?? json.node ?? [];

  if (!nodes) return [];
  if (!Array.isArray(nodes)) nodes = [nodes];
  return nodes;
}

/**
 * Prosta dekodacja encji HTML (&ndash; &nbsp; etc.)
 * – nie bawimy się w pełen parser HTML, tylko najczęstsze kody
 */
function decodeHtmlEntities(str) {
  if (!str) return "";
  return String(str)
    .replace(/&ndash;/g, "–")
    .replace(/&nbsp;/g, " ")
    .replace(/&quot;/g, '"')
    .replace(/&apos;/g, "'")
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">");
}

/**
 * Z node'a zrobimy obiekt kanału
 */
function mapChannelNode(node) {
  const number = Number(node.nr);
  const name = node.nazwa_kanalu ?? node.nazwa ?? null;

  if (!number || !name) {
    console.warn("⚠ Pomijam node bez numeru / nazwy:", node);
    return null;
  }

  // opis – chcemy HTML, ale z poprawionymi encjami
  let descriptionHtml = "";

  if (typeof node.opis === "string") {
    descriptionHtml = node.opis;
  } else if (node.opis && node.opis.p) {
    // <opis><p>...</p></opis>
    if (typeof node.opis.p === "string") {
      descriptionHtml = `<p>${node.opis.p}</p>`;
    } else if (Array.isArray(node.opis.p)) {
      // kilka <p>...</p>
      descriptionHtml = node.opis.p
        .map((p) =>
          typeof p === "string" ? `<p>${p}</p>` : ""
        )
        .join("");
    }
  }

  descriptionHtml = decodeHtmlEntities(descriptionHtml.trim());

  // logo – interesuje nas przede wszystkim src
  let logoUrl = null;
  const logoNode = node.field_logo_fid;

  if (typeof logoNode === "string") {
    // próbujemy wyciągnąć src="..."
    const m = logoNode.match(/src="([^"]+)"/);
    if (m) logoUrl = m[1];
  } else if (logoNode && logoNode.img) {
    // fast-xml-parser: <field_logo_fid><img ... /></field_logo_fid>
    const img = logoNode.img;
    if (img["@_src"]) {
      logoUrl = img["@_src"];
    }
  }

  // gwarantowany
  const guaranteed = String(node.gwarantowany || "")
    .toLowerCase()
    .includes("gwarantowany")
    ? 1
    : 0;

  return {
    number,
    name: String(name).trim(),
    description: descriptionHtml || null,
    logo_url: logoUrl || null,
    guaranteed,
  };
}

/**
 * Upewniamy się, że tabela docelowa istnieje
 */
function ensureSchema(db) {
  db.exec(`
    CREATE TABLE IF NOT EXISTS jambox_package_channels (
      id          INTEGER PRIMARY KEY AUTOINCREMENT,
      package_id  INTEGER NOT NULL,
      number      INTEGER NOT NULL,
      name        TEXT    NOT NULL,
      description TEXT,
      logo_url    TEXT,
      guaranteed  INTEGER NOT NULL DEFAULT 0,
      updated_at  TEXT    NOT NULL,
      UNIQUE (package_id, number),
      FOREIGN KEY (package_id)
        REFERENCES jambox_base_packages (id) ON DELETE CASCADE
    );
  `);
}

/**
 * Znajdowanie id pakietu po (source, slug)
 */
function getPackageId(db, source, slug) {
  const row = db
    .prepare(
      `
      SELECT id
      FROM jambox_base_packages
      WHERE source = ?
        AND slug   = ?;
    `
    )
    .get(source, slug);

  return row?.id ?? null;
}

/**
 * Upsert kanałów dla danego pakietu
 */
function upsertChannelsForPackage(db, packageId, channels) {
  const now = new Date().toISOString();

  const stmt = db.prepare(`
    INSERT INTO jambox_package_channels
      (package_id, number, name, description, logo_url, guaranteed, updated_at)
    VALUES
      (@package_id, @number, @name, @description, @logo_url, @guaranteed, @updated_at)
    ON CONFLICT(package_id, number) DO UPDATE SET
      name        = excluded.name,
      description = excluded.description,
      logo_url    = excluded.logo_url,
      guaranteed  = excluded.guaranteed,
      updated_at  = excluded.updated_at;
  `);

  const tx = db.transaction((rows) => {
    let count = 0;
    for (const ch of rows) {
      stmt.run({
        package_id: packageId,
        number: ch.number,
        name: ch.name,
        description: ch.description,
        logo_url: ch.logo_url,
        guaranteed: ch.guaranteed,
        updated_at: now,
      });
      count++;
    }
    console.log(`  → zapisano/zmieniono ${count} kanałów dla package_id=${packageId}`);
  });

  tx(channels);
}

async function main() {
  console.log(`Używam bazy: ${DB_PATH}`);
  const db = new Database(DB_PATH);

  ensureSchema(db);

  for (const feed of FEEDS) {
    console.log(
      `\n=== Przetwarzam ${feed.url} (source=${feed.source}, slug=${feed.slug}) ===`
    );

    const packageId = getPackageId(db, feed.source, feed.slug);
    if (!packageId) {
      console.warn(
        `⚠ Brak pakietu w jambox_base_packages dla source=${feed.source}, slug="${feed.slug}". Pomijam ten feed.`
      );
      continue;
    }

    try {
      const xml = await fetchXml(feed.url);
      const nodes = parseNodes(xml);

      console.log(`📦 Znaleziono ${nodes.length} node'ów (kanałów) w XML-u.`);

      const channels = [];
      for (const node of nodes) {
        const mapped = mapChannelNode(node);
        if (!mapped) continue;
        channels.push(mapped);
      }

      console.log(
        `🧮 Po zmapowaniu pozostaje ${channels.length} poprawnych kanałów.`
      );

      upsertChannelsForPackage(db, packageId, channels);
    } catch (err) {
      console.error(`❌ Błąd przy przetwarzaniu ${feed.url}:`, err);
    }
  }

  db.close();
  console.log("\n🎉 Import list kanałów JAMBOX zakończony.");
}

main().catch((err) => {
  console.error("❌ Krytyczny błąd:", err);
  process.exit(1);
});
