/**
 * 患者 upsert 双语义 — push 单表部分更新 vs cold-import 全量覆盖。
 *
 * 坐实(2026-07-20 patient push 支持的核心约束):
 *   - full(cold-import):历史行为 —— 缺失字段显式落 null/默认,phone 缺失合成 demo 假号;
 *   - partial(push 形态 A 单表):canonical 未提供的字段**不进 update 集合**(prisma
 *     undefined 不动列)—— 宿主只推 customer_basic_info 时 contacts lookup 缺席,
 *     full 语义会把存量真号覆盖成假号(此前 push 不支持患者主档的根因)。
 *
 * 纯函数测试,不跑 DB。
 */
import {
  buildPatientUpsertData,
  buildProfileUpsertData,
  synthesizeDemoPhone,
} from '../src/modules/sync/cold-import/patient-upsert.util';

describe('patient upsert | full(cold-import 历史语义)', () => {
  test('缺失字段显式落 null;phone 缺失合成确定性 demo 假号', () => {
    const d = buildPatientUpsertData({}, 'P001', { partial: false });
    expect(d.name).toBeNull();
    expect(d.phone).toBe(synthesizeDemoPhone('P001'));
    expect(String(d.phone)).toMatch(/^138\d{8}$/);
    expect(d.gender).toBeNull();
    expect(d.active).toBe(true); // status 缺省 = active
  });

  test('真值让位:host 给 phone 时不合成', () => {
    const d = buildPatientUpsertData({ phone: '13900001111' }, 'P001', { partial: false });
    expect(d.phone).toBe('13900001111');
  });

  test('profile 缺失字段落默认(tags=[] / notes=null)', () => {
    const p = buildProfileUpsertData({}, { partial: false });
    expect(p.tags).toEqual([]);
    expect(p.notes).toBeNull();
    expect(p.doNotContact).toBe(false);
  });
});

describe('patient upsert | partial(push 单表)', () => {
  test('未提供的字段不进 update 集合(不覆盖存量)', () => {
    const d = buildPatientUpsertData({ name: '王五' }, 'P001', { partial: true });
    expect(d.name).toBe('王五');
    expect('phone' in d).toBe(false); // ⭐ 关键:phone 缺席 → 不动存量,更不合成假号
    expect('gender' in d).toBe(false);
    expect('birthDate' in d).toBe(false);
    expect('active' in d).toBe(false); // status 未提供 → 不动 active
  });

  test('显式提供才更新;null 视为未提供(push 无清空语义)', () => {
    const d = buildPatientUpsertData(
      { phone: '13800002222', gender: null },
      'P001',
      { partial: true },
    );
    expect(d.phone).toBe('13800002222');
    expect('gender' in d).toBe(false);
  });

  test('status 提供时归一 active', () => {
    const d = buildPatientUpsertData({ status: 'archived' }, 'P001', { partial: true });
    expect(d.active).toBe(false);
  });

  test('profile partial:tags 空数组不清空存量;提供才落', () => {
    const p1 = buildProfileUpsertData({ tags: [] }, { partial: true });
    expect('tags' in p1).toBe(false);
    const p2 = buildProfileUpsertData({ tags: ['VIP'], notes: '备注' }, { partial: true });
    expect(p2.tags).toEqual(['VIP']);
    expect(p2.notes).toBe('备注');
    expect('doNotContact' in p2).toBe(false);
  });

  test('preferences 天然 partial-safe:无可写偏好 → undefined 不动列', () => {
    const d = buildPatientUpsertData({}, 'P001', { partial: true });
    expect(d.preferences).toBeUndefined();
    const d2 = buildPatientUpsertData({ phoneRelationshipCode: '3', phoneRelationship: 'mother' }, 'P001', {
      partial: true,
    });
    expect(d2.preferences).toMatchObject({
      contactPhone: { relationshipCode: '3', relationship: 'mother', isSelf: false },
    });
  });
});
