import { resolveTreatError } from './treat-error-resolver';
import { TreatErrorCategory as C } from './treat-error-category';

describe('resolveTreatError 통합 벤더 에러 분류', () => {
  describe('codef — 기존 codefErrorMessage 동작 보존', () => {
    //* 기존 treat.event-handler.ts codefErrorMessage switch의 (코드→알림톡메시지) 스냅샷.
    //*   resolve의 alimtalkMessage가 이와 동일해야 교체 시 회귀 없음(trailing space는 정리).
    const LEGACY: [string, C, string][] = [
      ['CF-00016', C.DUPLICATE, '동일한 요청으로 진료정보분석인증이 실패되었어요.'],
      ['CF-00025', C.AUTH_EXPIRED, '이미 응답이 완료되어 진료정보분석인증이 실패되었어요.'],
      ['CF-01004', C.AUTH_TIMEOUT, '응답 대기시간이 초과되어 진료정보분석인증이 실패되었어요.'],
      ['CF-12001', C.AUTH_TIMEOUT, '응답 대기시간이 초과되어 진료정보분석인증이 실패되었어요.'],
      ['CF-12003', C.VENDOR_ERROR, '기관 오류로 진료정보분석인증이 실패되었어요.'],
      ['CF-12050', C.VENDOR_ERROR, '기관 오류로 진료정보분석인증이 실패되었어요.'],
      ['CF-12100', C.VENDOR_ERROR, '기관 오류로 진료정보분석인증이 실패되었어요.'],
      ['CF-12051', C.AGE_RESTRICTED, '나이제한으로 진료정보분석인증이 실패되었어요.'],
      ['CF-12200', C.NETWORK_ERROR, '네트워크 오류로 진료정보분석인증이 실패되었어요.'],
      ['CF-12804', C.INPUT_INVALID, '잘못된 개인정보로 진료정보분석인증이 실패되었어요.'],
      ['CF-12835', C.INPUT_INVALID, '잘못된 개인정보로 진료정보분석인증이 실패되었어요.'],
      ['CF-13000', C.INPUT_INVALID, '잘못된 개인정보로 진료정보분석인증이 실패되었어요.'],
      ['CF-12871', C.AUTH_APP_MISSING, '앱이 없거나 인증서가 미설치로 진료정보분석인증이 실패되었어요.'],
      ['CF-13002', C.REQUEST_INVALID, '잘못된 요청으로 진료정보분석인증이 실패되었어요.'],
      ['CF-13300', C.AUTH_CODE_WRONG, '잘못된 인증번호로 진료정보분석인증이 실패되었어요.'],
      ['CF-12872', C.UNKNOWN, '진료정보분석인증이 실패되었어요.'],
    ];
    it.each(LEGACY)('%s → %s, 메시지 보존', (code, cat, msg) => {
      const r = resolveTreatError('codef', code);
      expect(r.category).toBe(cat);
      expect(r.alimtalkMessage.trim()).toBe(msg);
    });

    it('CF-00000 / CF-03002 → OK (실패 아님)', () => {
      expect(resolveTreatError('codef', 'CF-00000').category).toBe(C.OK);
      expect(resolveTreatError('codef', 'CF-03002').category).toBe(C.OK);
    });
  });

  describe('★ retryable — 일시오류와 진짜실패 구분 (갭 A 해결)', () => {
    it('일시/시스템 사유 → retryable true', () => {
      expect(resolveTreatError('codef', 'CF-12200').retryable).toBe(true); // NETWORK
      expect(resolveTreatError('codef', 'CF-12100').retryable).toBe(true); // VENDOR
      expect(resolveTreatError('codef', 'CF-01004').retryable).toBe(true); // TIMEOUT
      expect(resolveTreatError('codef', 'CF-00025').retryable).toBe(true); // EXPIRED
    });
    it('사용자 입력·조건 사유 → retryable false', () => {
      expect(resolveTreatError('codef', 'CF-13000').retryable).toBe(false); // INPUT
      expect(resolveTreatError('codef', 'CF-13300').retryable).toBe(false); // CODE_WRONG
      expect(resolveTreatError('codef', 'CF-12051').retryable).toBe(false); // AGE
      expect(resolveTreatError('codef', 'CF-12871').retryable).toBe(false); // APP_MISSING
    });
  });

  describe('coocon — native(8자리) 직접 매핑', () => {
    it.each([
      ['00000000', C.OK],
      ['80002231', C.INPUT_INVALID], // 실측: 주민번호 형식오류
      ['8000B122', C.INPUT_INVALID], // 실측: 정보 불일치
      ['800033E2', C.INPUT_INVALID], // 실측: 통신사 미선택
      ['80004133', C.AUTH_EXPIRED],
      ['80004126', C.AUTH_CODE_WRONG],
      ['80004100', C.AUTH_CERT],
      ['42110000', C.NO_DATA],
      ['GWSM0005', C.SYSTEM_ERROR],
      ['80002F20', C.VENDOR_ERROR],
    ])('%s → %s', (code, cat) => {
      expect(resolveTreatError('coocon', code).category).toBe(cat);
    });
  });

  describe('octover — DOZN 공통 오류코드 매핑', () => {
    it('1000200 → OK', () => {
      expect(resolveTreatError('octover', '1000200').category).toBe(C.OK);
    });
    it('1000035(조회내역 없음) → NO_DATA', () => {
      expect(resolveTreatError('octover', '1000035').category).toBe(C.NO_DATA);
    });
    it('1000111(IP 차단) → RATE_LIMIT', () => {
      expect(resolveTreatError('octover', '1000111').category).toBe(C.RATE_LIMIT);
    });
    it('사전 미수록 코드 → UNKNOWN', () => {
      expect(resolveTreatError('octover', '9999999').category).toBe(C.UNKNOWN);
    });
  });

  describe('hyphen — 통신코드 + errMsg [업무코드] 보정', () => {
    it('통신 resCd 직접 매핑', () => {
      expect(resolveTreatError('hyphen', 'E201').category).toBe(C.DUPLICATE);
      expect(resolveTreatError('hyphen', 'K001').category).toBe(C.SYSTEM_ERROR);
      expect(resolveTreatError('hyphen', 'E203').category).toBe(C.RATE_LIMIT);
    });
    it('★ 통신성공(CF-99999/0000)이어도 errMsg [업무코드]를 끌어올려 분류', () => {
      //* 문서 3-4 핵심: errYn=Y 업무오류가 result.code엔 안 실리고 errMsg에 묻힘
      const r1 = resolveTreatError('hyphen', 'CF-99999', { errMsg: '[LOGIN-V04] 사용자정보 미일치' });
      expect(r1.category).toBe(C.INPUT_INVALID);
      expect(r1.nativeCode).toBe('LOGIN-V04');

      const r2 = resolveTreatError('hyphen', '0000', { errMsg: '[I0001-002] 인증 미승인' });
      expect(r2.category).toBe(C.AUTH_NOT_APPROVED);

      const r3 = resolveTreatError('hyphen', '0000', { errMsg: '[B0002-L01] 진료내역 없음' });
      expect(r3.category).toBe(C.NO_DATA);

      //* prod 실측(2026-06-08): resCd=0000 errYn=Y, errMsg에 보안문자 불일치 코드가 묻힘
      const r4 = resolveTreatError('hyphen', 'CF-99999', {
        errMsg: '[I0001-004] 보안문자가 일치하지 않습니다.',
      });
      expect(r4.category).toBe(C.INPUT_INVALID);
      expect(r4.nativeCode).toBe('I0001-004');
    });
    it('errMsg에 매핑 안 되는 코드면 통신코드 유지', () => {
      const r = resolveTreatError('hyphen', 'E201', { errMsg: '[ZZZ-999] 알수없음' });
      expect(r.category).toBe(C.DUPLICATE); // 통신코드 E201 유지
    });
  });

  describe('미수록 코드 폴백 + 메타 일관성', () => {
    it('어떤 벤더든 미수록 코드 → UNKNOWN (ops generic)', () => {
      expect(resolveTreatError('codef', 'CF-99999').category).toBe(C.UNKNOWN);
      expect(resolveTreatError('coocon', 'ZZZZ').category).toBe(C.UNKNOWN);
      const r = resolveTreatError('codef', 'CF-99999');
      expect(r.audience).toBe('ops');
      expect(r.userMessage).toBeTruthy(); // 유저엔 generic이라도 메시지는 있음
    });
    it('null/undefined/공백 코드도 안전하게 UNKNOWN', () => {
      expect(resolveTreatError('codef', null).category).toBe(C.UNKNOWN);
      expect(resolveTreatError('codef', undefined).category).toBe(C.UNKNOWN);
      expect(resolveTreatError('codef', '  ').category).toBe(C.UNKNOWN);
    });
  });
});
