import {
  DDX_BUCKET_ADJUNCT,
  DDX_BUCKET_PENDING,
  DDX_BUCKET_PRIMARY,
  DDX_BUCKET_UNRELATED,
  DDX_UNKNOWN_DX_NAME,
  dosingDays,
  toDiagnosisGroupsView,
} from './ddx-view';

//* 이 창 화면 계약의 **유일한 실행 검사**다.
//*
//* `pending` 구획은 합성 사례로 덮는다 — 마일스톤 1 의 실응답은 `pending_drugs` 가 0건이라
//* 실측이 없다. 그 한계는 `#10` 이 출력에 적는다.

const drug = (over: Record<string, unknown> = {}) => ({
  drug_name: '예시약',
  ingredient: '예시성분',
  drug_class: '해열,진통,소염제',
  bucket: DDX_BUCKET_PRIMARY,
  role: '주치료',
  label: '해당',
  rationale: '허가사항에 포함된다',
  days_supplied: 30,
  ...over,
});

const bucketOf = (
  group: ReturnType<typeof toDiagnosisGroupsView>['groups'][number],
  name: string,
) => group.buckets.find((b) => b.bucket === name);

describe('toDiagnosisGroupsView', () => {
  it('구획 이름 4종을 항상 낸다 — 비어 있어도 자리가 남는다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [drug()],
          pending_drugs: [],
        },
      ],
    });

    expect(view.groups[0].buckets.map((b) => b.bucket)).toEqual([
      DDX_BUCKET_PRIMARY,
      DDX_BUCKET_ADJUNCT,
      DDX_BUCKET_UNRELATED,
      DDX_BUCKET_PENDING,
    ]);
  });

  it('bucket·role·label 셋이 서로 다른 값으로 보존된다 (UI3)', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'M170',
          dx_name: '양쪽 원발성 무릎관절증',
          diag_drugs: [
            drug({
              drug_name: '란스톤',
              bucket: DDX_BUCKET_ADJUNCT,
              role: '대증완화',
              label: '비해당',
            }),
          ],
          pending_drugs: [],
        },
      ],
    });

    const row = bucketOf(view.groups[0], DDX_BUCKET_ADJUNCT)!.drugs[0];
    expect(row.bucket).toBe(DDX_BUCKET_ADJUNCT);
    expect(row.role).toBe('대증완화');
    expect(row.label).toBe('비해당');
    expect(new Set([row.bucket, row.role, row.label]).size).toBe(3);
  });

  it('합성 pending 사례 — 판정 대기는 그룹 안의 네 번째 칸이다 (UI4·D4)', () => {
    const view = toDiagnosisGroupsView({
      status: 'partial',
      groups: [
        {
          dx_code: 'J00',
          dx_name: '급성 비인두염',
          diag_drugs: [drug()],
          pending_drugs: [
            drug({
              drug_name: '대기약',
              bucket: undefined,
              role: null,
              label: null,
            }),
          ],
        },
      ],
    });

    const pending = bucketOf(view.groups[0], DDX_BUCKET_PENDING)!;
    expect(pending.drugs).toHaveLength(1);
    expect(pending.drugs[0].drugName).toBe('대기약');
    //* 판단불가와 같은 목록에 섞이지 않는다.
    expect(bucketOf(view.groups[0], DDX_BUCKET_UNRELATED)!.drugs).toHaveLength(
      0,
    );
  });

  it('상병 미상 그룹은 이름을 얻고 목록 맨 뒤에 선다 (D6)', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        { dx_code: '', dx_name: null, diag_drugs: [drug()], pending_drugs: [] },
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [drug()],
          pending_drugs: [],
        },
      ],
    });

    expect(view.groups).toHaveLength(2);
    expect(view.groups[0].dxCode).toBe('E119');
    expect(view.groups[1].isUnknownDx).toBe(true);
    expect(view.groups[1].dxName).toBe(DDX_UNKNOWN_DX_NAME);
  });

  it('행정 목적 상병은 일반 상병 뒤, 상병 미상 앞에 선다 (D5)', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        { dx_code: '', dx_name: null, diag_drugs: [], pending_drugs: [] },
        {
          dx_code: 'Z000',
          dx_name: '일반건강검진',
          is_administrative: true,
          diag_drugs: [],
          pending_drugs: [],
        },
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [],
          pending_drugs: [],
        },
      ],
    });

    expect(view.groups.map((g) => g.dxCode)).toEqual(['E119', 'Z000', '']);
  });

  it('모르는 bucket 값은 버리지도 무관으로 밀지도 않고 뒤에 덧붙인다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [drug({ bucket: '새로운구획' })],
          pending_drugs: [],
        },
      ],
    });

    const names = view.groups[0].buckets.map((b) => b.bucket);
    expect(names).toHaveLength(5);
    expect(names[4]).toBe('새로운구획');
    expect(bucketOf(view.groups[0], '새로운구획')!.drugs).toHaveLength(1);
  });

  it('실명 필드를 어떤 경로로도 내보내지 않는다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      person_name: '홍길동',
      groups: [
        {
          person: '홍길동',
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [drug()],
          pending_drugs: [],
        },
      ],
    });

    const text = JSON.stringify(view);
    expect(text).not.toContain('홍길동');
    expect(text).not.toContain('person_name');
    expect(text).not.toContain('"person"');
  });

  it('적재본이 없으면 none 이다 — 화면은 판정을 기다리지 않는다', () => {
    //* 수정요청5 키가 더해졌다 — 적재본이 없으면 새 키도 빈 값이다(옛 키 `status` · `groups` 는 그대로).
    expect(toDiagnosisGroupsView(null)).toEqual({
      status: 'none',
      ingestRevision: null,
      notice: null,
      groups: [],
      drugClasses: [],
    });
  });

  //* ── 화면이 읽는 행의 실재 — whitelist 가 좁아 11개가 버려지던 자리 ──────────────
  //*
  //* 이 뷰는 원응답 약 객체의 키 19개 중 8개만 통과시켰고, 화면은 그래서 진료시작일·병의원·
  //* 1회투약량 같은 판단 근거를 못 받았다. 아래 셋이 그 통로가 실제로 열렸는지를 문다.

  it('처방 회차를 전달한다 — 진료시작일·병의원·투약량이 화면에 도달한다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [
            drug({
              indication: '당뇨병 혈당 조절',
              n_rows: 9,
              prescriptions: [
                {
                  treatment_start_date: '2026-04-13',
                  provider: '좋은문화병원',
                  dose_per_administration: 1,
                  administrations_per_day: 2,
                  days_supplied: 527,
                },
              ],
            }),
          ],
          pending_drugs: [],
        },
      ],
    });

    const d = bucketOf(view.groups[0], DDX_BUCKET_PRIMARY)!.drugs[0];
    expect(d.indication).toBe('당뇨병 혈당 조절');
    expect(d.nRows).toBe(9);
    expect(d.prescriptions).toEqual([
      {
        treatmentStartDate: '2026-04-13',
        provider: '좋은문화병원',
        dosePerAdministration: 1,
        administrationsPerDay: 2,
        daysSupplied: 527,
      },
    ]);
  });

  it('회차는 가장 최근이 앞이다 — 화면이 prescriptions[0] 하나를 편다', () => {
    const at = (date: string | null) => ({
      treatment_start_date: date,
      provider: '의원',
    });
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [
            drug({
              prescriptions: [at('2024-01-02'), at(null), at('2026-04-13')],
            }),
          ],
          pending_drugs: [],
        },
      ],
    });

    const d = bucketOf(view.groups[0], DDX_BUCKET_PRIMARY)!.drugs[0];
    //* 날짜 없는 회차는 뒤로만 민다 — 버리면 그 처방이 화면에서 사라진다.
    expect(d.prescriptions.map((p) => p.treatmentStartDate)).toEqual([
      '2026-04-13',
      '2024-01-02',
      null,
    ]);
  });

  //* 카드의 기간 줄이 읽는 값. **회차 전건이 모수다** — 구획 하나나 약 하나만 보면
  //* 「보조치료로만 처방된 달」이 기간에서 빠지고, 그 누락은 화면에서만 보인다.
  it('그룹의 기간은 구획을 가로질러 회차 날짜의 최소·최대다', () => {
    const at = (date: string | null) => ({
      treatment_start_date: date,
      provider: '의원',
    });
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [
            drug({ prescriptions: [at('2025-06-10'), at('2024-01-02')] }),
            //* **다른 구획의 회차가 양 끝을 민다.** 주치료만 보면 기간이 좁아진다.
            drug({
              bucket: DDX_BUCKET_ADJUNCT,
              prescriptions: [at('2023-05-01'), at('2026-04-13'), at(null)],
            }),
          ],
          pending_drugs: [],
        },
      ],
    });

    expect(view.groups[0].startDate).toBe('2023-05-01');
    expect(view.groups[0].endDate).toBe('2026-04-13');
  });

  //* **날짜가 하나도 없는 약이 날짜 있는 약보다 앞에 서는 표본.**
  //*
  //* 이 자리가 모수인 이유: `null` 은 어느 비교도 이기지 못하므로, 걸러내기를 비교 안으로
  //* 접으면 맨 앞의 `null` 이 최소·최대의 시드로 남아 기간이 통째로 빈다. **약 안에 섞인
  //* `null` 로는 그 결함이 안 보인다** — `byRecentFirst` 가 약 안에서는 날짜 없는 회차를
  //* 이미 뒤로 밀기 때문이다. 그 정렬이 닿지 않는 유일한 경계가 **약과 약 사이**다.
  it('날짜 없는 약이 앞에 서도 기간은 뒤 약의 날짜에서 나온다', () => {
    const at = (date: string | null) => ({
      treatment_start_date: date,
      provider: '의원',
    });
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [
            //* 회차는 있고 날짜만 없는 약. 정렬해도 `null` 이 앞이다 — 그것뿐이므로.
            drug({ drug_name: '날짜없는약', prescriptions: [at(null)] }),
            drug({
              drug_name: '날짜있는약',
              prescriptions: [at('2025-06-10')],
            }),
          ],
          pending_drugs: [],
        },
      ],
    });

    expect(view.groups[0].startDate).toBe('2025-06-10');
    expect(view.groups[0].endDate).toBe('2025-06-10');
  });

  //* 구적재본 — 적재 whitelist 가 `prescriptions` 를 버리던 시절의 판정이다. 그 판정은
  //* 되살릴 수 없으므로(적재 직후 제공자 쪽 원본을 지운다) 화면이 그 부재를 견뎌야 한다.
  it('날짜를 가진 회차가 없으면 기간은 둘 다 null 이다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          //* 회차 배열 자체가 없는 약과, 있지만 날짜가 없는 약 — 두 형태 모두.
          diag_drugs: [drug(), drug({ prescriptions: [{ provider: '의원' }] })],
          pending_drugs: [],
        },
      ],
    });

    expect(view.groups[0].startDate).toBeNull();
    expect(view.groups[0].endDate).toBeNull();
  });

  it('약이 자기 상병을 실어 보내면 그것을 쓰고, 아니면 그룹의 것으로 채운다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          //* 약효분류 축에서는 그룹 키가 drugClass 라 약마다 상병이 다르다. 그 축이 여기서 산다.
          diag_drugs: [
            drug({ dx_code: 'E1120', dx_name: '2형 당뇨병' }),
            drug({ drug_name: '무상병약' }),
          ],
          pending_drugs: [],
        },
      ],
    });

    const drugs = bucketOf(view.groups[0], DDX_BUCKET_PRIMARY)!.drugs;
    expect([drugs[0].dxCode, drugs[0].dxName]).toEqual(['E1120', '2형 당뇨병']);
    expect([drugs[1].dxCode, drugs[1].dxName]).toEqual(['E119', '당뇨병']);
  });

  it('처방 회차가 없거나 배열이 아니어도 빈 배열이다 — 화면이 그 부재를 견딘다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [
            drug(),
            drug({ drug_name: '깨진약', prescriptions: '배열이 아니다' }),
          ],
          pending_drugs: [],
        },
      ],
    });

    const drugs = bucketOf(view.groups[0], DDX_BUCKET_PRIMARY)!.drugs;
    expect(drugs[0].prescriptions).toEqual([]);
    expect(drugs[0].indication).toBeNull();
    expect(drugs[0].nRows).toBeNull();
    expect(drugs[1].prescriptions).toEqual([]);
  });

  it('처방 회차에도 실명이 실리지 않는다 — whitelist 가 새 키를 통과시키지 않는다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        {
          dx_code: 'E119',
          dx_name: '당뇨병',
          diag_drugs: [
            drug({
              prescriptions: [
                {
                  treatment_start_date: '2026-04-13',
                  provider: '의원',
                  patient_name: '홍길동',
                },
              ],
            }),
          ],
          pending_drugs: [],
        },
      ],
    });

    const text = JSON.stringify(view);
    expect(text).not.toContain('홍길동');
    expect(text).not.toContain('patient_name');
  });

  //* ── 수정요청5(v0.6) ──────────────────────────────────────────────────
  const v06Stored = (over: Record<string, unknown> = {}) => ({
    status: 'ready',
    ingest_rev: 2,
    notice: '합성 안내 — 참고용으로 보세요',
    groups: [
      {
        dx_code: 'K760',
        dx_name: '합성 간질환',
        medication_days: 60,
        diag_drugs: [drug()],
        pending_drugs: [],
      },
      {
        dx_code: 'I109',
        dx_name: '합성 고혈압',
        medication_days: null,
        diag_drugs: [drug()],
        pending_drugs: [],
      },
      {
        dx_code: 'E119',
        dx_name: '합성 당뇨',
        diag_drugs: [drug()],
        pending_drugs: [],
      },
    ],
    drug_classes: [
      {
        name: '간장질환용제',
        source: 'site',
        atc_code: 'A05',
        total_days: 60,
        period_start: '2022-07-04',
        period_end: '2022-07-04',
        n_prescriptions: 2,
        n_diagnoses: 1,
        drugs: [drug()],
        by_date: [
          {
            date: '2021-01-01',
            provider: '합성의원',
            dx_codes: ['K760'],
            dx_names: ['합성 간질환'],
            days: 30,
            drugs: [
              { drug_name: '합성약가', indication: null, days_supplied: 30 },
            ],
          },
          {
            date: null,
            provider: '합성의원',
            dx_codes: ['K760'],
            dx_names: ['합성 간질환'],
            days: 10,
            drugs: [],
          },
          {
            date: '2022-07-04',
            provider: '합성병원',
            dx_codes: ['K760', 'I109'],
            dx_names: ['합성 간질환', '합성 고혈압'],
            days: 60,
            drugs: [
              {
                drug_name: '합성약나',
                indication: '합성 효능',
                days_supplied: 60,
              },
            ],
          },
          {
            date: '2022-07-04',
            provider: '합성의원',
            dx_codes: ['K760'],
            dx_names: ['합성 간질환'],
            days: 60,
            drugs: [],
          },
        ],
      },
      { name: '', total_days: 90, drugs: [] },
      { name: '동맥경화용제', total_days: 30, drugs: [], by_date: [] },
    ],
    ...over,
  });

  it('v0.6 — medicationDays 는 적재 그룹에 키가 있을 때만 선다', () => {
    const view = toDiagnosisGroupsView(v06Stored());
    const by = (code: string) => view.groups.find((g) => g.dxCode === code)!;
    expect(['medicationDays' in by('K760'), by('K760').medicationDays]).toEqual(
      [true, 60],
    );
    expect(['medicationDays' in by('I109'), by('I109').medicationDays]).toEqual(
      [true, null],
    );
    expect('medicationDays' in by('E119')).toBe(false);
    //* 형이 맞지 않는 값은 키는 서고 값은 `null` 이다. 상속된 키는 자기 속성이 아니라 「없다」.
    const odd = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        { dx_code: 'A01', medication_days: '60' },
        Object.assign(Object.create({ medication_days: 9 }), {
          dx_code: 'A02',
        }),
      ],
    });
    expect([
      odd.groups[0].medicationDays,
      'medicationDays' in odd.groups[1],
    ]).toEqual([null, false]);
    expect([view.ingestRevision, view.notice]).toEqual([
      2,
      '합성 안내 — 참고용으로 보세요',
    ]);
  });

  it('v0.6 — byDate 는 키가 없으면 null, 비었으면 빈 배열이다', () => {
    const view = toDiagnosisGroupsView(v06Stored());
    expect(view.drugClasses.map((c) => c.name)).toEqual([
      '간장질환용제',
      '-',
      '동맥경화용제',
    ]);
    expect(view.drugClasses[1].byDate).toBeNull();
    expect(view.drugClasses[2].byDate).toEqual([]);
    expect(view.drugClasses[0].byDate).toHaveLength(4);
    //* 빈 약품명은 「-」 — `drugName` 선례와 같다.
    const blank = toDiagnosisGroupsView({
      status: 'ready',
      drug_classes: [{ name: 'x', by_date: [{ drugs: [{ drug_name: '' }] }] }],
    });
    expect(blank.drugClasses[0].byDate?.[0].drugs[0].drugName).toBe('-');
    //* 분류 값은 그대로 옮긴다.
    const c = view.drugClasses[0];
    expect([
      c.source,
      c.atcCode,
      c.totalDays,
      c.periodStart,
      c.periodEnd,
      c.nPrescriptions,
      c.nDiagnoses,
    ]).toEqual(['site', 'A05', 60, '2022-07-04', '2022-07-04', 2, 1]);
  });

  it('v0.6 — byDate 는 최근 날짜 먼저이고 날짜 없는 묶음은 뒤다', () => {
    const bd = toDiagnosisGroupsView(v06Stored()).drugClasses[0].byDate!;
    expect(bd.map((e) => [e.date, e.provider])).toEqual([
      //* 같은 날 두 묶음은 입력 순서 그대로다(안정 정렬) — 병원이 의원보다 앞에 들어왔다.
      ['2022-07-04', '합성병원'],
      ['2022-07-04', '합성의원'],
      ['2021-01-01', '합성의원'],
      [null, '합성의원'],
    ]);
    //* 묶음 안의 진단 연결과 약은 그대로다 — 화면이 `dxCodes` 로 거른다.
    expect(bd[0].dxCodes).toEqual(['K760', 'I109']);
    expect(bd[0].drugs).toEqual([
      { drugName: '합성약나', indication: '합성 효능', daysSupplied: 60 },
    ]);
  });

  it('v0.6 — 약효분류 뷰도 실명 필드를 내보내지 않는다', () => {
    const stored = v06Stored({
      person_name: '홍길동',
      under_30_days: [
        {
          dx_code: 'J209',
          dx_name: '__U30__',
          person: '홍길동',
          diag_drugs: [drug({ drug_name: '__U30__' })],
        },
      ],
    }) as Record<string, any>;
    stored.drug_classes[0].person = '홍길동';
    stored.drug_classes[0].by_date[0].person_name = '홍길동';
    stored.drug_classes[0].by_date[0].drugs[0].prescriber_name = '홍길동';
    const text = JSON.stringify(toDiagnosisGroupsView(stored));
    expect(text).not.toContain('홍길동');
    //* `under_30_days` 는 읽지 않는다 — 심은 파수 값이 응답 어디에도 없다.
    expect(text).not.toContain('__U30__');
    expect(text).not.toContain('J209');
  });

  it('v0.6 — 이전 정제기를 거친 v0.6 판정은 ingestRevision 이 null 이고 byDate 는 선다', () => {
    //* 배포 시차 재현 — 이전 정제기는 `drug_classes` 를 원소째 두고 `medication_days` · `notice` · 표지를 버렸다.
    const legacy = v06Stored() as Record<string, any>;
    delete legacy.ingest_rev;
    delete legacy.notice;
    for (const g of legacy.groups) delete g.medication_days;
    //* 원소째 적재된 옛 행에는 정제되지 않은 객체가 섞일 수 있다 — 뷰가 유일한 방어선이다.
    legacy.drug_classes[0].by_date[0].dx_codes.push({ person_name: '홍길동' });
    legacy.drug_classes[0].by_date[0].drugs.push('문자열 원소', {
      person_name: '홍길동',
    });
    legacy.drug_classes.push('문자열 분류');
    const view = toDiagnosisGroupsView(legacy);
    expect([view.ingestRevision, view.notice]).toEqual([null, null]);
    for (const g of view.groups) expect('medicationDays' in g).toBe(false);
    expect(Array.isArray(view.drugClasses[0].byDate)).toBe(true);
    expect(view.drugClasses).toHaveLength(3);
    const old = view.drugClasses[0].byDate!.find(
      (e) => e.date === '2021-01-01',
    )!;
    expect(old.dxCodes).toEqual(['K760']);
    expect(old.drugs.map((d) => d.drugName)).toEqual(['합성약가', '-']);
    expect(JSON.stringify(view)).not.toContain('홍길동');
  });

  //* ── 2026-10-13 고객사 「하나의 상병 내에서 30일 이상 투약건이 있을 경우에만 조회」 ──────────────
  //* 기준은 상병 누적(같은 날 처방은 한 번 — 그날 가장 긴 일수). 제공자 v0.6 은 이미 `groups` 를 그 기준으로 거르지만,
  //* 그 전에 적재된 판정에는 컷이 없어 30일 미만 상병이 화면에 섰다. 화면 목록을 내는 이 자리에서 한 번 더 거른다.

  const dxGroup = (code: string, over: Record<string, unknown> = {}) => ({
    dx_code: code,
    dx_name: `합성 ${code}`,
    diag_drugs: [],
    pending_drugs: [],
    ...over,
  });
  const rx = (date: string | null, days: number | null) => ({
    treatment_start_date: date,
    provider: '합성의원',
    days_supplied: days,
  });

  it('상병 30일 — 적재 그룹의 medication_days 가 30 미만이면 목록에서 뺀다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        dxGroup('K297', { medication_days: 29, diag_drugs: [drug()] }),
        dxGroup('K210', { medication_days: 30, diag_drugs: [drug()] }),
        //* 일수를 모르는 상병(`null`)은 빼지 않는다 — 제공자와 같은 규칙. 「30일 미만」 이라고 단정할 수 없다.
        dxGroup('E119', { medication_days: null, diag_drugs: [drug()] }),
      ],
    });
    expect(view.groups.map((g) => g.dxCode)).toEqual(['K210', 'E119']);
  });

  it('상병 30일 — 옛 판정(키 없음)은 처방 회차를 날짜별 최대로 더해 30 미만이면 뺀다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        //* 같은 날 두 약 20일 · 15일 → 그날은 20일. 누적 20일 → 빠진다.
        dxGroup('K297', {
          diag_drugs: [
            drug({ drug_name: '가', prescriptions: [rx('2025-01-02', 20)] }),
            drug({ drug_name: '나', prescriptions: [rx('2025-01-02', 15)] }),
          ],
        }),
        //* 다른 날 20일 + 15일 → 35일 → 남는다. 서로 다른 약효분류여도 한 상병이면 더한다.
        dxGroup('M5456', {
          diag_drugs: [
            drug({
              drug_name: '다',
              drug_class: '해열,진통,소염제',
              prescriptions: [rx('2025-01-02', 20)],
            }),
            drug({
              drug_name: '라',
              drug_class: '골격근이완제',
              prescriptions: [rx('2025-02-02', 15)],
            }),
          ],
        }),
      ],
    });
    expect(view.groups.map((g) => g.dxCode)).toEqual(['M5456']);
  });

  it('상병 30일 — 셀 수 없으면(일수 없는 회차, 회차 없는 약, 판정 대기 약) 빼지 않는다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        dxGroup('A01', {
          diag_drugs: [drug({ prescriptions: [rx('2025-01-02', null)] })],
        }),
        //* 날짜 없는 회차는 겹침을 몰라 따로 더한다(제공자와 같은 규칙) — 10일이 상한이라 30 미만이 확실하다.
        dxGroup('A02', {
          diag_drugs: [drug({ prescriptions: [rx(null, 10)] })],
        }),
        dxGroup('A03', { diag_drugs: [drug({ prescriptions: [] })] }),
        dxGroup('A04', {
          diag_drugs: [drug({ prescriptions: [rx('2025-01-02', 10)] })],
          pending_drugs: [{ drug_name: '대기약' }],
        }),
      ],
    });
    expect(view.groups.map((g) => g.dxCode)).toEqual(['A01', 'A03', 'A04']);
  });

  //* ── 2026-10-13 수정요청 — 겹치는 처방 기간은 한 번(고객사 S337: 04-20 28일 + 04-21 7일 = 28일) ──────────

  //* 2026-10-14 사용자 결정 — 같은 날은 가장 긴 일수 하나, 날짜가 다르면 겹쳐도 더한다.
  it.each([
    [
      [
        ['2025-04-20', 28],
        ['2025-04-21', 7],
      ],
      35,
    ],
    [
      [
        ['2022-07-04', 60],
        ['2022-07-04', 60],
      ],
      60,
    ],
    [
      [
        ['2022-07-04', 60],
        ['2022-07-04', 30],
      ],
      60,
    ],
    [
      [
        ['2025-01-01', 30],
        ['2025-01-31', 30],
      ],
      60,
    ],
    [
      [
        ['2022-07-04', 60],
        ['2022-09-01', 30],
      ],
      90,
    ],
    [
      [
        ['2025-01-06', 10],
        ['2025-01-01', 10],
      ],
      20,
    ],
    [
      [
        ['2025-01-01', 90],
        ['2025-02-01', 1],
      ],
      91,
    ],
    [
      [
        [null, 10],
        ['2025-01-01', 5],
      ],
      15,
    ],
    [[['2025-02-30', 10]], 10],
    [
      [
        ['2025-01-01', null],
        ['2025-01-01', 0],
      ],
      0,
    ],
  ] as Array<[Array<[string | null, number | null]>, number]>)(
    'dosingDays — 같은 날 최대 · 날짜끼리 합 %j → %d일',
    (spans, days) => {
      expect(dosingDays(spans.map(([start, d]) => ({ start, days: d })))).toBe(
        days,
      );
    },
  );

  it('상병 30일 — 옛 판정(키 없음)도 회차로 셀 수 있으면 medicationDays 를 싣는다(화면 · AI 인사이트가 같은 수)', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        dxGroup('M5456', {
          diag_drugs: [
            drug({
              prescriptions: [rx('2025-01-02', 20), rx('2025-02-02', 15)],
            }),
          ],
        }),
        //* 셀 수 없고 키도 없으면 싣지 않는다 — 「미상」 과 「제공자가 안 보냄」 을 가르는 자리다.
        dxGroup('A01', { diag_drugs: [drug({ prescriptions: [] })] }),
      ],
    });
    expect(
      view.groups.map((g) => [
        g.dxCode,
        'medicationDays' in g,
        g.medicationDays,
      ]),
    ).toEqual([
      ['M5456', true, 35],
      ['A01', false, undefined],
    ]);
  });

  it('상병 30일 — 적재된 medication_days 보다 회차로 다시 센 값이 먼저다', () => {
    const view = toDiagnosisGroupsView({
      status: 'ready',
      groups: [
        //* 10-13 ~ 10-14 판정(기간 합집합)이 28일로 적재했다. 회차로 다시 세면 날짜끼리 더해 35일 → 남는다.
        dxGroup('S337', {
          medication_days: 28,
          diag_drugs: [
            drug({ drug_name: '가', prescriptions: [rx('2025-04-20', 28)] }),
            drug({ drug_name: '나', prescriptions: [rx('2025-04-21', 7)] }),
          ],
        }),
        //* 적재 50일, 회차는 같은 날 30일 · 20일 → 그날 가장 긴 30일 → 30 미만이 아니라 남고 화면 값은 30일.
        dxGroup('M1995', {
          medication_days: 50,
          diag_drugs: [
            drug({
              prescriptions: [rx('2025-01-01', 30), rx('2025-01-01', 20)],
            }),
          ],
        }),
        //* 같은 날 20일 · 15일 → 20일 → 빠진다.
        dxGroup('K297', {
          medication_days: 35,
          diag_drugs: [
            drug({ drug_name: '가', prescriptions: [rx('2025-01-02', 20)] }),
            drug({ drug_name: '나', prescriptions: [rx('2025-01-02', 15)] }),
          ],
        }),
      ],
    });
    expect(view.groups.map((g) => [g.dxCode, g.medicationDays])).toEqual([
      ['S337', 35],
      ['M1995', 30],
    ]);
  });
});
