/**
 * FieldMapper 三能力自检(rename / normalize / zod canonical 校验)
 *
 * 跑:
 *   npx ts-node --transpile-only src/cli/verify-field-mapper.ts
 */
import {
  ContractDriftError,
  mapNormalizeValidate,
  normalizeCanonical,
  applyMapping,
} from '../modules/sync/assembler/field-mapper';

const PLATFORM_ID = 'demo-platform-uuid';

let pass = 0;
let fail = 0;

function check(label: string, condition: boolean, detail?: string) {
  if (condition) {
    console.log(`  ✅ ${label}`);
    pass++;
  } else {
    console.log(`  ❌ ${label}${detail ? ` — ${detail}` : ''}`);
    fail++;
  }
}

console.log('\n━━━ 1. applyMapping(rename)━━━');
{
  const raw = { pid: 'P001', mobile: '138...', real_name: '张三', sex: '男' };
  const mapping = { externalId: 'pid', phone: 'mobile', name: 'real_name', gender: 'sex' };
  const out = applyMapping(raw, mapping);
  check('host pid → canonical externalId', out.externalId === 'P001');
  check('host mobile → canonical phone', out.phone === '138...');
  check('host 字段已被消费,不再透传', !('mobile' in out) && !('pid' in out));
}

console.log('\n━━━ 2. normalize:boolean coerce ━━━');
{
  const raw = applyMapping(
    { pid: 'P001', is_blacklisted: '1', is_deceased: 'N' },
    { externalId: 'pid', doNotContact: 'is_blacklisted', deceased: 'is_deceased' },
  );
  const out = normalizeCanonical(raw, 'patient', {
    amountUnit: 'fen',
    timezone: 'Asia/Shanghai',
  });
  check('doNotContact "1" → true', out.doNotContact === true);
  check('deceased "N" → false', out.deceased === false);
}

console.log('\n━━━ 3. normalize:gender 男女 → M/F ━━━');
{
  const out1 = normalizeCanonical({ externalId: 'X', gender: '男', doNotContact: false }, 'patient', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  const out2 = normalizeCanonical({ externalId: 'X', gender: '女', doNotContact: false }, 'patient', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  const out3 = normalizeCanonical({ externalId: 'X', gender: 'M', doNotContact: false }, 'patient', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  check('gender "男" → "M"', out1.gender === 'M');
  check('gender "女" → "F"', out2.gender === 'F');
  check('gender "M" 直通', out3.gender === 'M');
}

console.log('\n━━━ 4. normalize:amountUnit yuan → cents ━━━');
{
  const raw = {
    externalId: 'co_001',
    patientExternalId: 'P001',
    clinicId: 'c_1',
    createdAt: '2026-05-22T16:00:00+08:00',
    items: [{ name: '根管', amount: 3200, currency: 'CNY' }],
    totalAmount: 3200,
    status: 'submitted',
  };
  const out = normalizeCanonical(raw, 'order', {
    amountUnit: 'yuan',
    timezone: 'Asia/Shanghai',
  });
  check('totalAmount 3200 yuan → 320000 cents', out.totalAmount === 320000);
  const items = out.items as Array<{ amount: number }>;
  check('items[0].amount 3200 yuan → 320000 cents', items[0]?.amount === 320000);

  // amountUnit=fen 应直通
  const out2 = normalizeCanonical(raw, 'order', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  check('amountUnit=fen 时金额直通', out2.totalAmount === 3200);
}

console.log('\n━━━ 5. normalize:timezone 无 tz → 加 offset ━━━');
{
  const raw = { externalId: 'pay_001', patientExternalId: 'P001', clinicId: 'c_1',
    paidAt: '2026-05-22 16:05:00', amount: 100 };
  const out = normalizeCanonical(raw, 'payment', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  check('paidAt "2026-05-22 16:05:00" → ...T16:05:00+08:00',
    out.paidAt === '2026-05-22T16:05:00+08:00',
    String(out.paidAt));

  // 已带 tz 直通
  const out2 = normalizeCanonical({ ...raw, paidAt: '2026-05-22T16:05:00+09:00' }, 'payment', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  check('已带 tz 直通(+09:00 不被覆盖)',
    out2.paidAt === '2026-05-22T16:05:00+09:00',
    String(out2.paidAt));
}

console.log('\n━━━ 6. normalize:array deserialization(CSV JSON 字符串)━━━');
{
  const raw = {
    externalId: 'enc_x', patientExternalId: 'P001', clinicId: 'c_1',
    startedAt: '2026-05-22T15:00:00+08:00',
    diagnoses: '[{"code":"K04.0","name":"x"}]', // CSV 入口形态
    treatments: '[{"category":"endo","status":"completed"}]',
  };
  const out = normalizeCanonical(raw, 'encounter', {
    amountUnit: 'fen', timezone: 'Asia/Shanghai',
  });
  check('diagnoses JSON string → array', Array.isArray(out.diagnoses));
  check('treatments JSON string → array', Array.isArray(out.treatments));
}

console.log('\n━━━ 7. ContractDriftError:必填缺失 ━━━');
{
  try {
    mapNormalizeValidate({
      raw: { externalId: 'P1' }, // 缺 doNotContact(patient 必填)
      resource: 'patient',
      mapping: undefined,
      normalize: { amountUnit: 'fen', timezone: 'Asia/Shanghai' },
      hostId: PLATFORM_ID,
    });
    check('drift 抛错', false, '应抛 ContractDriftError 但没抛');
  } catch (err) {
    check('缺失 doNotContact → ContractDriftError', err instanceof ContractDriftError);
    if (err instanceof ContractDriftError) {
      check('error.resource = "patient"', err.resource === 'patient');
      check('error.message 含字段路径', err.message.includes('doNotContact'),
        err.message);
    }
  }
}

console.log('\n━━━ 8. ContractDriftError:字段类型错 ━━━');
{
  try {
    mapNormalizeValidate({
      raw: {
        externalId: 'enc_x', patientExternalId: 'P001', clinicId: 'c_1',
        startedAt: '2026-05-22T15:00:00+08:00',
        treatments: 'not-an-array',
      },
      resource: 'encounter',
      mapping: undefined,
      normalize: { amountUnit: 'fen', timezone: 'Asia/Shanghai' },
      hostId: PLATFORM_ID,
    });
    check('drift 抛错', false);
  } catch (err) {
    check('treatments 非数组 → drift',
      err instanceof ContractDriftError);
  }
}

console.log('\n━━━ 9. 端到端:rename + normalize + 校验 全通 ━━━');
{
  const hostRaw = {
    pid: 'P00012345',
    real_name: '张三',
    mobile: '138...',
    sex: '男',
    dob: '1985-03-20',
    is_blacklisted: 0,
    is_deceased: 'N',
  };
  const mapping = {
    externalId: 'pid', name: 'real_name', phone: 'mobile', gender: 'sex',
    birthDate: 'dob', doNotContact: 'is_blacklisted', deceased: 'is_deceased',
  };
  try {
    const out = mapNormalizeValidate({
      raw: hostRaw,
      resource: 'patient',
      mapping,
      normalize: { amountUnit: 'yuan', timezone: 'Asia/Shanghai' },
      hostId: PLATFORM_ID,
    }) as Record<string, unknown>;
    check('externalId="P00012345"', out.externalId === 'P00012345');
    check('phone="138..."', out.phone === '138...');
    check('gender="M"(男转过来)', out.gender === 'M');
    check('birthDate 保持 YYYY-MM-DD', out.birthDate === '1985-03-20');
    check('doNotContact=false(0 转过来)', out.doNotContact === false);
    check('deceased=false(N 转过来)', out.deceased === false);
    check('status 默认 "active"', out.status === 'active');
  } catch (err) {
    check('全链路无错', false, err instanceof Error ? err.message : String(err));
  }
}

console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`  PASS: ${pass}    FAIL: ${fail}`);
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
process.exit(fail === 0 ? 0 : 1);
