/**
 * automap.mjs — 양식 스타일 자동 매핑 (md2hwpx 휴리스틱 충실 포팅)
 *
 * 입력: analysis.styles (양식의 스타일 목록), ir.patterns (마크다운에 실제 등장한 패턴키)
 * 출력: { mapping: {patternKey: styleId}, report: [{key, styleId, styleName, by}] }
 *
 * 포팅 원칙(룰 #12 — 정통): md2hwpx.js 의 BULLET_ALIAS / HEADING_HINTS / PARAGRAPH_HINTS
 * 와 "글머리 실제 문자가 1순위 정체성"(v1.3.0) 규칙을 그대로 재현. 양식별 정밀 매핑을
 * 별도 JSON(overrides)으로 덮어쓸 수 있게 해 자동 추천 실패 시에도 정확성 보장.
 */

// 글머리 기호 동치 그룹 — pattern 문자 ↔ 양식 스타일의 실제 bulletChar 매칭용
const BULLET_GROUPS = [
  ['▮', '■'],
  ['□'],
  ['❍', '○', '◯', 'o', 'O'],
  ['●'],
  ['◎'],
  ['◆', '◇'],
  ['※'],
  ['-', '–', '—'],
  ['*'],
  ['+'],
  ['▶', '▷'],
];
function bulletGroup(ch) {
  return BULLET_GROUPS.find((g) => g.includes(ch)) || [ch];
}

// 글머리 → 양식 스타일 이름 alias (md2hwpx BULLET_ALIAS)
const BULLET_NAME_HINTS = {
  '▮': [/^▮$|^■$/, /네모|사각|채운\s*네모/i],
  '■': [/^▮$|^■$/, /네모|사각|채운\s*네모/i],
  '□': [/^□$/, /빈\s*네모|empty/i],
  '❍': [/^본문\s*[○o❍◯]$|^[❍○◯]$/, /동그라미/],
  '○': [/^본문\s*[○o❍◯]$|^[❍○◯]$/, /동그라미/],
  '◯': [/^본문\s*[○o❍◯]$|^[❍○◯]$/, /동그라미/],
  '●': [/^본문\s*[●]$|^●$/, /채운\s*동그라미|black/i],
  '◎': [/^◎$/, /이중원/],
  '◆': [/^◆$/, /다이아|마름모/],
  '◇': [/^◇$/, /다이아|마름모/],
  '※': [/^※$/, /주석|주\)/],
  '-': [/^본문-$|^[-–—]$|대시/],
  '*': [/^본문[*]$|^[*]$|아스타/],
  '+': [/^본문[+]$|^[+]$/],
  '1.': [/^1\.$/, /번호|숫자|outline\s*1/i],
  '1)': [/^1\)$/, /괄호|outline\s*1/i],
  '▶': [/^▶$|화살/],
  '▷': [/^▷$|빈\s*화살/],
};

// 헤딩 → 양식 스타일 이름/영문명 hint (md2hwpx HEADING_HINTS)
const HEADING_HINTS = {
  h1: [/^장$|^제\d?장$/, /^제목$/, /^Outline\s*2$/i, /heading\s*1|제목\s*1/i],
  h2: [/^제\d?절$|^절$/, /heading\s*2|제목\s*2/i],
  h3: [/^1\.$/, /^Outline\s*1$/i, /heading\s*3|제목\s*3/i],
  h4: [/^▮$|^■$|^네모$/, /^Outline\s*3$/i, /heading\s*4|제목\s*4/i],
  h5: [/^본문\s*[○o❍◯]$|^[❍○◯]$/, /heading\s*5/i],
  h6: [/^본문-$|^본문[-–—]$/, /heading\s*6/i],
};

const PARAGRAPH_HINTS = [/^바탕글$|^Normal$|^Body$|^본문$/i];
const TABLECELL_HINTS = [/^표\s*내용$|^표내용$|^표안$|^표\s*안/i, /표.*내용|cell|table.*body/i];
const CAPTION_TABLE_HINTS = [/^표\s*제목$|^표제목$/i, /표.*제목|caption.*tab|tab.*caption/i, /caption/i];
const CAPTION_FIGURE_HINTS = [/^그림\s*제목$|^그림제목$/i, /그림.*제목|caption.*fig|fig.*caption/i, /caption/i];
const NOTE_HINTS = [/^자료$|^자료\s*[,，]|^주[\)）]?$|출처|note|source/i];
const FOOTNOTE_HINTS = [/각주|footnote/i];

function styleName(s) {
  // 양식 스타일명에 후행 공백이 흔함("1. ", "1) ") → 매칭 시 trim
  return String(s.name || '').trim();
}
function styleEng(s) {
  return String(s.engName || s.engineerName || '').trim();
}

// hint 리스트(우선순위 순)로 styles 에서 첫 매칭 반환. (styles 는 이미 usage desc 정렬)
function matchByHints(styles, hints) {
  for (let h = 0; h < hints.length; h++) {
    const re = hints[h];
    const hit = styles.find((s) => re.test(styleName(s)) || re.test(styleEng(s)));
    if (hit) return { style: hit, rank: h };
  }
  return null;
}

// 글머리 매칭 — 1순위: 양식 스타일의 실제 bulletChar 동치, 2순위: 이름 alias
function matchBullet(styles, sym) {
  const group = bulletGroup(sym);
  // 1) 실제 글머리 문자 동치 (가장 강한 신호)
  const byChar = styles.find((s) => s.para && s.para.bulletChar && group.includes(s.para.bulletChar));
  if (byChar) return { style: byChar, by: `글머리문자 '${byChar.para.bulletChar}'` };
  // 2) 이름 alias
  const hints = BULLET_NAME_HINTS[sym];
  if (hints) {
    const m = matchByHints(styles, hints);
    if (m) return { style: m.style, by: `이름매칭 '${styleName(m.style)}'` };
  }
  return null;
}

/**
 * @param {Array} styles  analysis.styles
 * @param {Object} patterns  ir.patterns ({h1:1, 'bullet:■':2, table:1, ...})
 * @param {Object} [overrides]  {patternKey: styleId} — 강제 지정(자동추천 무시)
 * @returns {{mapping:Object, report:Array, unmatched:Array}}
 */
export function resolveMapping(styles, patterns = {}, overrides = {}) {
  const mapping = {};
  const report = [];
  const unmatched = [];
  const sset = new Set(styles.map((s) => String(s.id)));

  const put = (key, styleId, by) => {
    if (styleId == null) return;
    mapping[key] = String(styleId);
    const st = styles.find((s) => String(s.id) === String(styleId));
    report.push({ key, styleId: String(styleId), styleName: st ? styleName(st) : '(?)', by });
  };

  // 0) overrides 먼저 — 양식밖 id 는 무시(엔진 거부 방지)
  for (const [k, v] of Object.entries(overrides || {})) {
    if (sset.has(String(v))) put(k, v, '오버라이드');
    else unmatched.push({ key: k, reason: `오버라이드 styleId ${v} 가 양식에 없음` });
  }

  // 1) paragraph (바탕글) — 거의 모든 키의 fallback 이므로 먼저 확정
  let paraId = mapping['paragraph'];
  if (!paraId) {
    const m = matchByHints(styles, PARAGRAPH_HINTS);
    paraId = m ? String(m.style.id) : String(styles[styles.length - 1]?.id ?? styles[0]?.id ?? '0');
    put('paragraph', paraId, m ? `이름매칭 '${styleName(m.style)}'` : '폴백(마지막 스타일)');
  }

  // 2) 헤딩 h1~h6 — patterns 에 등장한 것 + h1~h3 는 기본 보장
  const headKeys = new Set(Object.keys(patterns).filter((k) => /^h[1-6]$/.test(k)));
  ['h1', 'h2', 'h3'].forEach((k) => headKeys.add(k));
  for (const k of [...headKeys].sort()) {
    if (mapping[k]) continue;
    const m = matchByHints(styles, HEADING_HINTS[k] || []);
    if (m) put(k, m.style.id, `이름매칭 '${styleName(m.style)}'`);
    else {
      put(k, paraId, '폴백(바탕글)');
      unmatched.push({ key: k, reason: '헤딩 스타일 미발견 → 바탕글로 폴백' });
    }
  }

  // 3) 글머리 bullet:* — patterns 에 등장한 것만
  for (const key of Object.keys(patterns)) {
    if (!key.startsWith('bullet:') || mapping[key]) continue;
    const sym = key.slice('bullet:'.length);
    const m = matchBullet(styles, sym);
    if (m) put(key, m.style.id, m.by);
    else {
      put(key, paraId, '폴백(바탕글)');
      unmatched.push({ key, reason: `글머리 '${sym}' 매칭 스타일 미발견 → 바탕글로 폴백` });
    }
  }

  // 4) 표 셀 / 캡션 / 자료주 / 각주 — 있으면 매핑(없으면 생략, 엔진 자체 폴백)
  const opt = [
    ['tableCell', TABLECELL_HINTS],
    ['caption-table', CAPTION_TABLE_HINTS],
    ['caption-figure', CAPTION_FIGURE_HINTS],
    ['note', NOTE_HINTS],
    ['source', NOTE_HINTS],
    ['footnote', FOOTNOTE_HINTS],
  ];
  for (const [key, hints] of opt) {
    if (mapping[key]) continue;
    const m = matchByHints(styles, hints);
    if (m) put(key, m.style.id, `이름매칭 '${styleName(m.style)}'`);
  }
  // tableCell 은 표가 있으면 반드시 — 못 찾으면 바탕글
  if (patterns.table && !mapping['tableCell']) {
    put('tableCell', paraId, '폴백(바탕글)');
  }

  return { mapping, report, unmatched };
}
