import fs from "node:fs/promises";
import path from "node:path";
import vm from "node:vm";
import xlsx from "xlsx";

const ROOT = process.cwd();

const LEGACY_DIR = path.join(ROOT, "legacy");
const OUT_DIR = path.join(ROOT, "apps", "web", "public", "data", "legacy");

const PERMITTED_XLSX = path.join(LEGACY_DIR, "AIS_permitted_Chinese_2025_정리완료_최종.xlsx");
const CHECKLIST_XLSX = path.join(LEGACY_DIR, "항적점검표_본선_부속선_운반선.xlsx");
const FLEET_906_JSX = path.join(LEGACY_DIR, "선단구성_906척_어업수역 (1).jsx");

function toStr(x) {
  return (x ?? "").toString().trim();
}

function toNumOrNull(x) {
  const s = toStr(x);
  if (!s) return null;
  const n = Number(s);
  return Number.isFinite(n) ? n : null;
}

function shipCodeFromChecklistSheet(sheetName) {
  if (sheetName.includes("본선")) return "PT";
  if (sheetName.includes("부속선")) return "PT-S";
  if (sheetName.includes("운반선")) return "FC";
  return null;
}

function extractArrayLiteral(txt, marker) {
  const markerIdx = txt.indexOf(marker);
  if (markerIdx < 0) throw new Error(`Marker not found: ${marker}`);
  const start = txt.indexOf("[", markerIdx);
  if (start < 0) throw new Error(`'[' not found after marker: ${marker}`);

  let depth = 0;
  let inString = false;
  let esc = false;
  for (let i = start; i < txt.length; i += 1) {
    const ch = txt[i];
    if (inString) {
      if (esc) {
        esc = false;
        continue;
      }
      if (ch === "\\") {
        esc = true;
        continue;
      }
      if (ch === "\"") {
        inString = false;
      }
      continue;
    }

    if (ch === "\"") {
      inString = true;
      continue;
    }

    if (ch === "[") depth += 1;
    if (ch === "]") depth -= 1;
    if (depth === 0) return txt.slice(start, i + 1);
  }

  throw new Error(`Unterminated array literal for marker: ${marker}`);
}

function readPermittedListXlsx(filePath) {
  const wb = xlsx.readFile(filePath, { cellDates: true });
  const sheetName = wb.SheetNames[0];
  const ws = wb.Sheets[sheetName];
  const rows = xlsx.utils.sheet_to_json(ws, { defval: "", raw: false });

  const byPermitNo = new Map();
  for (const r of rows) {
    const permitNo = toStr(r.permit_no);
    if (!permitNo) continue;
    const mmsi = toNumOrNull(r.target_id);

    const prev = byPermitNo.get(permitNo);
    if (prev) {
      if (mmsi && !prev.mmsiList.includes(mmsi)) prev.mmsiList.push(mmsi);
      if (!prev.mmsi && mmsi) prev.mmsi = mmsi;
      continue;
    }

    byPermitNo.set(permitNo, {
      permitNo,
      shipNameRoman: toStr(r.ship_name),
      ton: toNumOrNull(r.ton),
      callSign: toStr(r.call_sign),
      shipPower: toNumOrNull(r.ship_power),
      shipLen: toNumOrNull(r.ship_len),
      shipWidth: toNumOrNull(r.ship_width),
      shipDept: toNumOrNull(r.ship_dept),
      workSeaArea: toStr(r.work_sea_area),
      workTerm1: toStr(r.work_term1),
      workTerm2: toStr(r.work_term2),
      quota: toStr(r.quota),
      shipCode: toStr(r.ship_code),
      mmsi: mmsi,
      mmsiList: mmsi ? [mmsi] : [],
      sources: { permittedList: true, checklist: false, fleet906: false },
      ownerCn: null,
      ownerRoman: null,
      shipNameCn: null,
      pairPermitNo: null,
      pairShipNameCn: null,
      checklistSheet: null,
    });
  }

  return Array.from(byPermitNo.values());
}

function readChecklistXlsx(filePath) {
  const wb = xlsx.readFile(filePath, { cellDates: true });
  const out = [];

  for (const sheetName of wb.SheetNames) {
    const code = shipCodeFromChecklistSheet(sheetName);
    if (!code) continue;
    const ws = wb.Sheets[sheetName];
    const rows = xlsx.utils.sheet_to_json(ws, { defval: "", raw: false, range: 2 });

    for (const r of rows) {
      const permitNo = toStr(r["허가번호"]);
      if (!permitNo) continue;
      out.push({
        permitNo,
        shipCode: code,
        shipNameCn: toStr(r["선박명\n(중국)"]),
        shipNameRoman: toStr(r["선박명\n(로마)"]),
        ton: toNumOrNull(r["톤수"]),
        ownerCn: toStr(r["소유주\n(중국)"]),
        ownerRoman: toStr(r["소유주\n(로마)"]),
        pairPermitNo: toStr(r["짝 허가번호"]),
        pairShipNameCn: toStr(r["짝 선박명"]),
        workSeaArea: toStr(r["허가수역"]),
        checklistSheet: sheetName,
      });
    }
  }

  return out;
}

async function readFleet906(filePath) {
  const txt = await fs.readFile(filePath, "utf-8");
  const arrLit = extractArrayLiteral(txt, "const D=");
  const D = vm.runInNewContext(`(${arrLit})`, {}, { timeout: 10_000 });
  if (!Array.isArray(D)) throw new Error("Fleet906 D is not an array");

  const out = [];

  function pushVessel(v, shipCode, own, ownEn, extra = {}) {
    if (!Array.isArray(v) || v.length < 4) return;
    out.push({
      permitNo: toStr(v[0]),
      shipNameCn: toStr(v[1]),
      shipNameRoman: toStr(v[2]),
      ton: toNumOrNull(v[3]),
      shipCode,
      ownerCn: toStr(own),
      ownerRoman: toStr(ownEn),
      ...extra,
    });
  }

  for (const r of D) {
    if (!Array.isArray(r) || r.length < 9) continue;
    const [own, ownEn, pairs, gn, ot, ps, fc, upt, upts] = r;

    if (Array.isArray(pairs)) {
      for (const p of pairs) {
        if (!Array.isArray(p) || p.length < 2) continue;
        const main = p[0];
        const sub = p[1];
        const mainPermitNo = Array.isArray(main) ? toStr(main[0]) : "";
        const subPermitNo = Array.isArray(sub) ? toStr(sub[0]) : "";

        pushVessel(main, "PT", own, ownEn, { pairPermitNo: subPermitNo });
        pushVessel(sub, "PT-S", own, ownEn, { pairPermitNo: mainPermitNo });
      }
    }

    if (Array.isArray(gn)) for (const v of gn) pushVessel(v, "GN", own, ownEn);
    if (Array.isArray(ot)) for (const v of ot) pushVessel(v, "OT", own, ownEn);
    if (Array.isArray(ps)) for (const v of ps) pushVessel(v, "PS", own, ownEn);
    if (Array.isArray(fc)) for (const v of fc) pushVessel(v, "FC", own, ownEn);
    if (Array.isArray(upt)) for (const v of upt) pushVessel(v, "PT", own, ownEn);
    if (Array.isArray(upts)) for (const v of upts) pushVessel(v, "PT-S", own, ownEn);
  }

  return out;
}

async function main() {
  await fs.mkdir(OUT_DIR, { recursive: true });

  const permitted = readPermittedListXlsx(PERMITTED_XLSX);
  const checklist = readChecklistXlsx(CHECKLIST_XLSX);
  const fleet906 = await readFleet906(FLEET_906_JSX);

  const byPermitNo = new Map();
  for (const v of permitted) byPermitNo.set(v.permitNo, v);

  for (const c of checklist) {
    const prev = byPermitNo.get(c.permitNo);
    if (!prev) {
      byPermitNo.set(c.permitNo, {
        permitNo: c.permitNo,
        shipNameRoman: c.shipNameRoman,
        ton: c.ton,
        callSign: "",
        shipPower: null,
        shipLen: null,
        shipWidth: null,
        shipDept: null,
        workSeaArea: c.workSeaArea,
        workTerm1: "",
        workTerm2: "",
        quota: "",
        shipCode: c.shipCode,
        mmsi: null,
        mmsiList: [],
        sources: { permittedList: false, checklist: true, fleet906: false },
        ownerCn: c.ownerCn || null,
        ownerRoman: c.ownerRoman || null,
        shipNameCn: c.shipNameCn || null,
        pairPermitNo: c.pairPermitNo || null,
        pairShipNameCn: c.pairShipNameCn || null,
        checklistSheet: c.checklistSheet || null,
      });
      continue;
    }

    prev.sources.checklist = true;
    prev.shipCode = prev.shipCode || c.shipCode;
    prev.shipNameCn = prev.shipNameCn || c.shipNameCn || null;
    prev.shipNameRoman = prev.shipNameRoman || c.shipNameRoman || "";
    prev.ton = prev.ton ?? c.ton ?? null;
    prev.ownerCn = prev.ownerCn || c.ownerCn || null;
    prev.ownerRoman = prev.ownerRoman || c.ownerRoman || null;
    prev.pairPermitNo = prev.pairPermitNo || c.pairPermitNo || null;
    prev.pairShipNameCn = prev.pairShipNameCn || c.pairShipNameCn || null;
    prev.workSeaArea = prev.workSeaArea || c.workSeaArea || "";
    prev.checklistSheet = prev.checklistSheet || c.checklistSheet || null;
  }

  for (const f of fleet906) {
    const prev = byPermitNo.get(f.permitNo);
    if (!prev) continue;
    prev.sources.fleet906 = true;
    prev.shipNameCn = prev.shipNameCn || f.shipNameCn || null;
    prev.shipNameRoman = prev.shipNameRoman || f.shipNameRoman || "";
    prev.ton = prev.ton ?? f.ton ?? null;
    prev.shipCode = prev.shipCode || f.shipCode || "";
    prev.ownerCn = prev.ownerCn || f.ownerCn || null;
    prev.ownerRoman = prev.ownerRoman || f.ownerRoman || null;
    prev.pairPermitNo = prev.pairPermitNo || f.pairPermitNo || null;
  }

  const vessels = Array.from(byPermitNo.values()).filter((v) => v && v.permitNo);

  const out = {
    generatedAt: new Date().toISOString(),
    counts: {
      permittedList: permitted.length,
      checklist: checklist.length,
      fleet906: fleet906.length,
      merged: vessels.length,
    },
    vessels,
  };

  const outPath = path.join(OUT_DIR, "chinese-permitted.v1.json");
  await fs.writeFile(outPath, JSON.stringify(out), "utf-8");
  // eslint-disable-next-line no-console
  console.log(`Wrote ${vessels.length} vessels -> ${path.relative(ROOT, outPath)}`);
}

main().catch((err) => {
  // eslint-disable-next-line no-console
  console.error(err);
  process.exit(1);
});
