#!/usr/bin/env node
/**
 * update.js — обновление Налогового кодекса РК
 *
 * Скачивает новый НК с adilet.zan.kz, парсит, заменяет current → outdated.
 *
 * Использование:
 *   node update.js --doc=K2600000XXX --name="НК РК 2027" --effective=2027-01-01
 *
 * С локальными HTML-файлами (если скачали вручную):
 *   node update.js --doc=K2600000XXX --name="НК РК 2027" --effective=2027-01-01 \
 *                  --html-ru=./nk-ru.html --html-kaz=./nk-kaz.html
 *
 * После выполнения:
 *   data/tax-code-current-ru.md   — новый кодекс (ru)
 *   data/tax-code-current-kaz.md  — новый кодекс (kaz)
 *   data/tax-code-outdated-ru.md  — предыдущий кодекс (ru)
 *   data/tax-code-outdated-kaz.md — предыдущий кодекс (kaz)
 *   data/versions.json            — обновлены метаданные
 */

import { readFileSync, writeFileSync, existsSync, renameSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

// adilet.zan.kz использует сертификат НУЦ РК (национальный УЦ Казахстана),
// который не входит в стандартный trust store Node.js.
// Отключаем проверку только для этого процесса.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

const __dirname  = dirname(fileURLToPath(import.meta.url));
const DATA_DIR   = join(__dirname, "..", "data");
const VERS_FILE  = join(DATA_DIR, "versions.json");

// ── CLI args ───────────────────────────────────────────────────────────────────
const args = Object.fromEntries(
  process.argv.slice(2)
    .filter(a => a.startsWith("--"))
    .map(a => {
      const eq = a.indexOf("=");
      return eq === -1 ? [a.slice(2), true] : [a.slice(2, eq), a.slice(eq + 1)];
    })
);

const docId     = args["doc"];
const name      = args["name"];
const effective = args["effective"];
const htmlRuFile  = args["html-ru"]  ?? null;
const htmlKazFile = args["html-kaz"] ?? null;

if (!docId || !name || !effective) {
  console.error([
    "Использование:",
    '  node update.js --doc=K2600000XXX --name="НК РК 2027" --effective=2027-01-01',
    "",
    "Опционально (локальные HTML):",
    "  --html-ru=./nk-ru.html --html-kaz=./nk-kaz.html",
  ].join("\n"));
  process.exit(1);
}

// ── HTML → Markdown parser ─────────────────────────────────────────────────────
function parseHtml(html) {
  // Extract article/doc content block
  let content = html;
  const articleMatch = html.match(/<article[^>]*>([\s\S]*?)<\/article>/i);
  if (articleMatch) {
    content = articleMatch[1];
  } else {
    const divMatch = html.match(/<div[^>]*class="[^"]*doc[^"]*"[^>]*>([\s\S]*?)<\/div>/i);
    if (divMatch) content = divMatch[1];
  }

  // Strip scripts/styles
  content = content
    .replace(/<script[\s\S]*?<\/script>/gi, "")
    .replace(/<style[\s\S]*?<\/style>/gi, "");

  function clean(s) {
    return s
      .replace(/<[^>]+>/g, "")
      .replace(/&nbsp;/g, " ")
      .replace(/&amp;/g, "&")
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&quot;/g, '"')
      .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(parseInt(n)))
      .replace(/\s+/g, " ")
      .trim();
  }

  const lines = [];
  const parts = content.split(/<\/(?:p|h[1-4]|li|div|td|tr)>/i);

  for (const part of parts) {
    const m = part.match(/<(h[1-4]|p|li|td)[^>]*>([\s\S]*)$/i);
    if (!m) continue;
    const tag  = m[1].toLowerCase();
    const text = clean(m[2]);
    if (!text || text.length < 2) continue;

    const isRuArticle  = /^Статья\s+\d+[\.\-]/.test(text);
    const isKazArticle = /^\d+-бап[.\s]/.test(text);

    if      (tag === "h1")                        lines.push("# " + text);
    else if (tag === "h2")                        lines.push("## " + text);
    else if (tag === "h3") {
      if (isKazArticle || isRuArticle) lines.push("\n## " + text);
      else                             lines.push("### " + text);
    }
    else if (tag === "h4")                        lines.push("#### " + text);
    else if (tag === "li")                        lines.push("- " + text);
    else if (isRuArticle || isKazArticle)         lines.push("\n## " + text);
    else                                          lines.push(text);
  }

  return lines.join("\n\n").replace(/\n{4,}/g, "\n\n\n").trim();
}

// ── Fetch HTML from adilet.zan.kz ─────────────────────────────────────────────
async function fetchHtml(doc, langCode) {
  const siteLang = langCode === "kaz" ? "kaz" : "rus";
  const url = `https://adilet.zan.kz/${siteLang}/docs/${doc}`;
  console.log(`  Загрузка ${url} ...`);

  const res = await fetch(url, {
    headers: {
      "User-Agent": "Mozilla/5.0 (compatible; NKUpdater/1.0)",
      "Accept": "text/html,application/xhtml+xml",
    },
    signal: AbortSignal.timeout(120_000), // 2 min timeout
  });

  if (!res.ok) {
    throw new Error(`HTTP ${res.status} ${res.statusText} — ${url}`);
  }

  const buf = await res.arrayBuffer();
  const ct  = res.headers.get("content-type") ?? "";

  // Detect encoding from Content-Type header
  const encMatch = ct.match(/charset=([^\s;]+)/i);
  const enc = encMatch ? encMatch[1].toLowerCase() : null;

  if (enc && enc !== "utf-8" && enc !== "utf8") {
    try {
      return new TextDecoder(enc).decode(buf);
    } catch {
      // fallback
    }
  }

  // Try UTF-8 strict, fall back to windows-1251
  try {
    return new TextDecoder("utf-8", { fatal: true }).decode(buf);
  } catch {
    return new TextDecoder("windows-1251").decode(buf);
  }
}

// ── Count articles in markdown ─────────────────────────────────────────────────
function countArticles(md) {
  return (md.match(/^## (?:Статья\s+\d+|[\d]+-бап)/gm) ?? []).length;
}

// ── Main ───────────────────────────────────────────────────────────────────────
async function main() {
  console.log(`\n🔄 Обновление НК РК`);
  console.log(`   Новый кодекс : ${name} (${docId})`);
  console.log(`   Действует с  : ${effective}\n`);

  // Read current versions.json
  let versions = {};
  if (existsSync(VERS_FILE)) {
    versions = JSON.parse(readFileSync(VERS_FILE, "utf-8"));
  }

  const localFiles = { ru: htmlRuFile, kaz: htmlKazFile };

  for (const lang of ["ru", "kaz"]) {
    console.log(`── Язык: ${lang} ──`);

    // Get HTML
    let html;
    if (localFiles[lang]) {
      console.log(`  Читаю локальный файл: ${localFiles[lang]}`);
      html = readFileSync(localFiles[lang], "utf-8");
    } else {
      html = await fetchHtml(docId, lang);
    }

    // Parse
    process.stdout.write(`  Парсинг HTML → Markdown ...`);
    const md = parseHtml(html);
    const articleCount = countArticles(md);
    console.log(` ${Math.round(md.length / 1024)}KB, статей: ${articleCount}`);

    if (articleCount < 10) {
      throw new Error(`Слишком мало статей (${articleCount}) для ${lang} — возможно, страница не загрузилась корректно.`);
    }

    // Rotate files: current → outdated
    const currentFile  = join(DATA_DIR, `tax-code-current-${lang}.md`);
    const outdatedFile = join(DATA_DIR, `tax-code-outdated-${lang}.md`);

    if (existsSync(currentFile)) {
      console.log(`  Архивирую current-${lang}.md → outdated-${lang}.md`);
      renameSync(currentFile, outdatedFile);
    }

    // Write new current
    console.log(`  Записываю tax-code-current-${lang}.md`);
    writeFileSync(currentFile, md, "utf-8");

    console.log();
  }

  // Update versions.json
  const newVersions = {
    current: {
      doc:       docId,
      name,
      effective,
      updatedAt: new Date().toISOString().slice(0, 10),
    },
    outdated: versions.current
      ? {
          doc:       versions.current.doc,
          name:      versions.current.name,
          effective: versions.current.effective,
          retired:   new Date().toISOString().slice(0, 10),
        }
      : (versions.outdated ?? null),
  };

  writeFileSync(VERS_FILE, JSON.stringify(newVersions, null, 2) + "\n", "utf-8");

  console.log("✅ Готово!");
  console.log(`   Актуальный : ${newVersions.current.name} (${newVersions.current.doc})`);
  if (newVersions.outdated) {
    console.log(`   Устаревший : ${newVersions.outdated.name} (${newVersions.outdated.doc})`);
  }
  console.log("\nНикаких изменений в коде не требуется при следующем обновлении — только запустить update.js снова.");
}

main().catch(e => {
  console.error(`\n❌ Ошибка: ${e.message}`);
  process.exit(1);
});
