// 순수함수 회귀 테스트. 실행(로컬, 의존성 0): node --test src/lib/utils/headingPath.test.ts
// (Node ≥23 또는 22.6+ --experimental-strip-types — TS 타입 네이티브 strip.)
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
  cleanHeading,
  pathSegments,
  collapseWindows,
  groupOrFlat,
  sectionTypeLabel,
  type DocumentSection,
} from './headingPath.ts';

let _id = 0;
function sec(p: Partial<DocumentSection>): DocumentSection {
  return {
    chunk_id: ++_id,
    section_title: null,
    heading_path: null,
    level: null,
    node_type: null,
    is_leaf: true,
    section_type: null,
    summary: null,
    confidence: null,
    ...p,
  };
}

test('cleanHeading: 마크다운/HTML 잔재 strip', () => {
  assert.equal(cleanHeading('**UG-5 PLATE**<sup>2</sup>'), 'UG-5 PLATE');
  assert.equal(cleanHeading('  **DESIGN**  '), 'DESIGN');
  assert.equal(cleanHeading('a   b\tc'), 'a b c');
  assert.equal(cleanHeading(null), '');
  assert.equal(cleanHeading(''), '');
});

test('pathSegments: > 분할 + 정제', () => {
  assert.deepEqual(pathSegments('**A** > **B**<sup>1</sup> > C'), ['A', 'B', 'C']);
  assert.deepEqual(pathSegments(null), []);
  assert.deepEqual(pathSegments('  '), []);
});

test('sectionTypeLabel: 한글 매핑 + passthrough', () => {
  assert.equal(sectionTypeLabel('requirement'), '요건');
  assert.equal(sectionTypeLabel('unknown_type'), 'unknown_type');
  assert.equal(sectionTypeLabel(null), null);
});

test('collapseWindows: 연속 동일 heading window 만 dedupe, 순서 유지', () => {
  const input = [
    sec({ heading_path: 'Intro', node_type: null }),
    sec({ heading_path: 'Pearson', node_type: 'window' }),
    sec({ heading_path: 'Pearson', node_type: 'window' }),
    sec({ heading_path: 'Pearson', node_type: 'window' }),
    sec({ heading_path: 'Conf', node_type: null }),
    sec({ heading_path: 'Pearson', node_type: 'window' }), // 비연속 → 새 항목
  ];
  const out = collapseWindows(input);
  assert.equal(out.length, 4);
  assert.equal(out[0].fragmentCount, 1); // Intro
  assert.equal(out[1].fragmentCount, 3); // Pearson ×3 합침
  assert.equal(out[2].fragmentCount, 1); // Conf
  assert.equal(out[3].fragmentCount, 1); // 비연속 Pearson
  // 순서 보존
  assert.deepEqual(
    out.map((o) => cleanHeading(o.section.heading_path)),
    ['Intro', 'Pearson', 'Conf', 'Pearson'],
  );
});

test('[C2] collapseWindows: split-parent + window 들 → rail 1행, 대표=split-parent(char_start 보유)', () => {
  const input = [
    sec({ section_title: 'Article 5', heading_path: 'Article 5', node_type: 'chapter_split', is_leaf: false, char_start: 120 }),
    sec({ section_title: 'Article 5', heading_path: 'Article 5', node_type: 'window', is_leaf: true, char_start: null }),
    sec({ section_title: 'Article 5', heading_path: 'Article 5', node_type: 'window', is_leaf: true, char_start: null }),
  ];
  const out = collapseWindows(input);
  assert.equal(out.length, 1, 'split-parent + 2 window → rail 1행');
  // 대표 = split-parent (char_start 보유) → jump 성립
  assert.equal(out[0].section.node_type, 'chapter_split');
  assert.equal(out[0].section.char_start, 120);
  assert.equal(out[0].fragmentCount, 2, 'window 조각 수 = 2 (split-parent 자신 제외)');
});

test('groupOrFlat: 적은 그룹 + 낮은 기타% → group (5140-류)', () => {
  // 3 top segment × 4 = 12절, window 없음 → group_count 3, 기타 0%
  const sections: DocumentSection[] = [];
  for (const top of ['장1', '장2', '장3']) {
    for (let i = 0; i < 4; i++) sections.push(sec({ heading_path: `${top} > 절${i}` }));
  }
  const layout = groupOrFlat(sections);
  assert.equal(layout.mode, 'group');
  assert.equal(layout.groups.length, 3);
  assert.deepEqual(layout.groups.map((g) => g.key), ['장1', '장2', '장3']); // 등장순서
  assert.equal(layout.groups[0].items.length, 4);
});

test('groupOrFlat: 기타% ≥ 50 → flat 강등 (5186/5225-류)', () => {
  const sections: DocumentSection[] = [
    sec({ heading_path: 'A > a1' }),
    sec({ heading_path: 'B > b1' }),
    sec({ node_type: 'window', heading_path: 'W1' }),
    sec({ node_type: 'window', heading_path: 'W2' }),
    sec({ node_type: 'section_split', heading_path: 'S1' }),
    sec({ node_type: 'window', heading_path: 'W3' }), // 기타 4/6 = 66.7%
  ];
  const layout = groupOrFlat(sections);
  assert.equal(layout.mode, 'flat');
  assert.ok(layout.items.length > 0);
});

test('groupOrFlat: group_count > 30 → flat 강등', () => {
  const sections: DocumentSection[] = [];
  for (let i = 0; i < 31; i++) sections.push(sec({ heading_path: `seg${i} > x` }));
  const layout = groupOrFlat(sections);
  assert.equal(layout.mode, 'flat');
});

test('groupOrFlat: 빈 입력 → flat, 항목 0', () => {
  const layout = groupOrFlat([]);
  assert.equal(layout.mode, 'flat');
  assert.equal(layout.items.length, 0);
});
