#!/usr/bin/env node
/**
 * build_hwpx.mjs — 마크다운 + 양식(.hwpx) → 보고서 .hwpx (md2hwpx 엔진 재사용)
 *
 * 사용: (<P> = 활성 과제 폴더 projects/<연도_과제명> — 루트 ACTIVE_PROJECT.txt 참조)
 *   node build_hwpx.mjs --md <P>/drafts/99_full.md --template <P>/sources/gdi_template.hwpx --out <P>/output/report.hwpx
 *   node build_hwpx.mjs --md <P>/drafts/99_full.md --out <P>/output/report.hwpx   # 번들 기본 양식 사용
 *   node build_hwpx.mjs --template <P>/sources/foo.hwpx --print-styles             # 양식 스타일 목록만 출력
 *
 * 옵션:
 *   --md <path>         변환할 마크다운 (필수, --print-styles 제외)
 *   --template <path>   양식 .hwpx (생략 시 templates/gdi-default.hwpx)
 *   --out <path>        출력 .hwpx (필수, --print-styles 제외)
 *   --mapping <path>    매핑 오버라이드 JSON ({patternKey: styleId}) — 자동추천보다 우선
 *   --print-styles      양식의 스타일 목록만 출력(매핑 작성용). 빌드 안 함
 *   --no-fit-columns    표 열폭 내용 최적화 끄기(균등 분배)
 *   --quiet             상세 로그 최소화
 *
 * 표준 출처: KS X 6101:2018(OWPML), 한컴 HWP 5.0. 엔진 = md2hwpx(검증본 스냅샷).
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadEngine } from './loader.mjs';
import { resolveMapping } from './automap.mjs';

const HERE = path.dirname(fileURLToPath(import.meta.url));
const DEFAULT_TEMPLATE = path.join(HERE, 'templates', 'gdi-default.hwpx');

// ── arg 파싱 (외부 의존 없음) ──
function parseArgs(argv) {
  const a = { fitColumns: true, quiet: false, printStyles: false };
  for (let i = 0; i < argv.length; i++) {
    const t = argv[i];
    if (t === '--md') a.md = argv[++i];
    else if (t === '--template') a.template = argv[++i];
    else if (t === '--out') a.out = argv[++i];
    else if (t === '--mapping') a.mapping = argv[++i];
    else if (t === '--print-styles') a.printStyles = true;
    else if (t === '--no-fit-columns') a.fitColumns = false;
    else if (t === '--quiet') a.quiet = true;
    else if (t === '-h' || t === '--help') a.help = true;
    else throw new Error(`알 수 없는 옵션: ${t}`);
  }
  return a;
}

const USAGE = `gdi-hwpx — 마크다운 + 양식(.hwpx) → 보고서 .hwpx
  node build_hwpx.mjs --md <md> --template <hwpx> --out <hwpx> [--mapping <json>] [--no-fit-columns] [--quiet]
  node build_hwpx.mjs --template <hwpx> --print-styles`;

function fileToArrayBuffer(p) {
  const buf = fs.readFileSync(p);
  return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}

const log = (q, ...m) => { if (!q) console.log(...m); };

async function main() {
  const args = parseArgs(process.argv.slice(2));
  if (args.help) { console.log(USAGE); return; }

  const eng = loadEngine();
  const { MdParser, PresetManager, HwpxBuilder, JSZip } = eng;

  // 양식 경로 결정
  const templatePath = args.template
    ? path.resolve(process.cwd(), args.template)
    : DEFAULT_TEMPLATE;
  if (!fs.existsSync(templatePath)) {
    throw new Error(`양식 .hwpx 없음: ${templatePath}\n  → --template 로 양식을 지정하거나 templates/gdi-default.hwpx 를 두세요.`);
  }

  // ── --print-styles: 양식 스타일만 출력하고 종료 ──
  if (args.printStyles) {
    const analysis = await PresetManager.analyzeHwpx(fileToArrayBuffer(templatePath));
    console.log(`# 양식 스타일 목록 (${path.basename(templatePath)}) — 총 ${analysis.styles.length}개`);
    console.log('id\t사용\t글머리\t스타일이름');
    for (const s of analysis.styles) {
      const bc = (s.para && s.para.bulletChar) ? s.para.bulletChar : '';
      console.log(`${s.id}\t${s.usage || 0}\t${bc}\t${s.name}${s.external ? '  (외부추정:' + s.externalSource + ')' : ''}`);
    }
    return;
  }

  // ── 빌드 경로 ──
  if (!args.md) throw new Error('--md 필요\n' + USAGE);
  if (!args.out) throw new Error('--out 필요\n' + USAGE);
  const mdPath = path.resolve(process.cwd(), args.md);
  const outPath = path.resolve(process.cwd(), args.out);
  if (!fs.existsSync(mdPath)) throw new Error(`마크다운 없음: ${mdPath}`);

  const md = fs.readFileSync(mdPath, 'utf8');

  // 1) 마크다운 → IR
  const ir = MdParser.parse(md, { fitColumns: args.fitColumns });
  ir.options = { ...(ir.options || {}), fitColumns: args.fitColumns };
  log(args.quiet, `[1/4] 파싱: 블록 ${ir.blocks.length}개 · 패턴 ${JSON.stringify(ir.patterns)}`);

  // 2) 양식 분석
  const analysis = await PresetManager.analyzeHwpx(fileToArrayBuffer(templatePath));
  log(args.quiet, `[2/4] 양식 분석: 스타일 ${analysis.styles.length}개 (${path.basename(templatePath)})`);

  // 3) 매핑 (오버라이드 JSON → 자동추천)
  //    우선순위: --mapping > (기본 양식일 때) 번들 mapping.gdi.json > automap.
  //    오버라이드에 없는 패턴키는 automap 이 이름·글머리 매칭으로 보충.
  let overrides = {};
  let overrideSrc = null;
  if (args.mapping) {
    const mp = path.resolve(process.cwd(), args.mapping);
    if (!fs.existsSync(mp)) throw new Error(`매핑 JSON 없음: ${mp}`);
    overrides = JSON.parse(fs.readFileSync(mp, 'utf8'));
    overrideSrc = args.mapping;
  } else if (!args.template) {
    // 번들 기본 양식 → 전용 정밀 매핑 자동 적용
    const bundled = path.join(HERE, 'mapping.gdi.json');
    if (fs.existsSync(bundled)) {
      overrides = JSON.parse(fs.readFileSync(bundled, 'utf8'));
      overrideSrc = 'mapping.gdi.json (번들 기본)';
    }
  }
  delete overrides._comment;
  if (overrideSrc) log(args.quiet, `      매핑 오버라이드: ${overrideSrc}`);
  const { mapping, report, unmatched } = resolveMapping(analysis.styles, ir.patterns, overrides);
  if (!args.quiet) {
    console.log('[3/4] 매핑 (마크다운 → 양식 스타일):');
    for (const r of report) console.log(`      ${r.key.padEnd(14)} → #${r.styleId} "${r.styleName}"  (${r.by})`);
  }
  if (unmatched.length) {
    console.log('  ⚠ 자동매핑 경고 (필요시 --mapping JSON 으로 교정):');
    for (const u of unmatched) console.log(`      - ${u.key}: ${u.reason}`);
  }

  // 4) 빌드 → 쓰기
  const blob = await HwpxBuilder.buildHwpx({
    ir, analysis, mapping,
    hwpxArrayBuffer: fileToArrayBuffer(templatePath),
    imageRegistry: null,
  });
  const outBuf = Buffer.from(await blob.arrayBuffer());
  fs.mkdirSync(path.dirname(outPath), { recursive: true });
  fs.writeFileSync(outPath, outBuf);
  log(args.quiet, `[4/4] 출력: ${outPath} (${outBuf.length.toLocaleString()} bytes)`);

  // ── 자가검증 ──
  const v = await validate(JSZip, outBuf);
  if (!args.quiet) {
    console.log('검증:');
    console.log(`      mimetype: ${v.mimetype} ${v.mimeOk ? '✓' : '✗ (application/hwp+zip 아님)'}`);
    console.log(`      zip 엔트리 ${v.entries}개 · section0 ${v.sectionLen.toLocaleString()}자`);
    console.log(`      표 태그 균형 hp:tbl ${v.tbl.open}/${v.tbl.close} · 셀 hp:tc ${v.tc.open}/${v.tc.close} · 단락 hp:p ${v.p.open}/${v.p.close} ${v.balanced ? '✓' : '✗ 불균형'}`);
  }
  const ok = v.mimeOk && v.balanced && v.hasHeader && v.hasSection;
  console.log(ok ? `✅ 완료: ${path.relative(process.cwd(), outPath)}` : `❌ 검증 실패 — 출력 파일을 한컴에서 열어 확인 필요`);
  if (!ok) process.exitCode = 2;
}

async function validate(JSZip, buf) {
  const z = await JSZip.loadAsync(buf);
  const names = Object.keys(z.files);
  const hasHeader = !!z.file('Contents/header.xml');
  const hasSection = !!z.file('Contents/section0.xml');
  let mimetype = '';
  try { mimetype = await z.file('mimetype').async('string'); } catch {}
  const sec = hasSection ? await z.file('Contents/section0.xml').async('string') : '';
  const count = (re) => (sec.match(re) || []).length;
  const tbl = { open: count(/<hp:tbl\b/g), close: count(/<\/hp:tbl>/g) };
  const tc = { open: count(/<hp:tc\b/g), close: count(/<\/hp:tc>/g) };
  const p = { open: count(/<hp:p\b/g), close: count(/<\/hp:p>/g) };
  const balanced = tbl.open === tbl.close && tc.open === tc.close && p.open === p.close;
  return {
    entries: names.length, mimetype, mimeOk: mimetype === 'application/hwp+zip',
    sectionLen: sec.length, tbl, tc, p, balanced, hasHeader, hasSection,
  };
}

main().catch((e) => { console.error('❌ 오류:', e.message); process.exitCode = 1; });
