/**
 * 진료내역 오류 매핑 현황 기준 데이터 (정적).
 * 출처: insuniverse-server 코드 분석 + docs/진료내역_오류규격_마스터시트.xlsx
 * /mapping-status 에서 라이브 catalog(테스트로 관측된 코드)와 합쳐 진척도를 보여준다.
 */

export type GapStatus = '처리' | '미흡' | '없음';

/** 통합 택소노미: category(SNAKE_CASE) → 유저 노출 메시지 (단일 출처) */
export const TAXONOMY: Record<string, string> = {
  INPUT_INVALID: '입력하신 정보를 다시 확인해 주세요',
  AUTH_APP_MISSING: '앱이 없거나 인증서를 발급받지 않으셨어요',
  AUTH_CERT: '인증서를 확인해 주세요',
  AUTH_NOT_APPROVED: '인증 앱에서 인증을 완료해 주세요',
  AUTH_CODE_WRONG: '인증번호를 다시 확인해 주세요',
  AUTH_TIMEOUT: '인증 시간이 초과되었어요',
  AUTH_EXPIRED: '인증 시간이 만료되었어요. 다시 시도해 주세요',
  AGE_RESTRICTED: '연령 제한으로 조회할 수 없어요',
  VENDOR_ERROR: '해당 기관 오류로 실패했어요. 잠시 후 다시 시도해 주세요',
  NETWORK_ERROR: '네트워크 오류예요. 잠시 후 다시 시도해 주세요',
  NO_DATA: '조회된 진료내역이 없어요',
  DUPLICATE: '이미 처리 중인 요청이에요',
  RATE_LIMIT: '잠시 후 다시 시도해 주세요',
  BUSY: '잠시 후 다시 시도해 주세요(또는 대기)',
  REQUEST_INVALID: '요청이 올바르지 않아요',
  SYSTEM_ERROR: '시스템 오류가 발생했어요',
  UNKNOWN: '진료정보 인증에 실패했어요',
};

/**
 * 노출 대상(audience): 'user'=유저에게 구체 메시지 노출 / 'ops'=유저엔 generic, 상세는 로그·DB·관리자(추적/분석)만.
 * ⚠️ audience 무관하게 모든 에러는 (vendor, nativeCode, category, message, traceId) 로 로깅되어야 함(운영 추적).
 */
export const AUDIENCE: Record<string, 'user' | 'ops'> = {
  INPUT_INVALID: 'user', AUTH_APP_MISSING: 'user', AUTH_CERT: 'user', AUTH_NOT_APPROVED: 'user',
  AUTH_CODE_WRONG: 'user', AUTH_TIMEOUT: 'user', AUTH_EXPIRED: 'user', AGE_RESTRICTED: 'user',
  VENDOR_ERROR: 'user', NETWORK_ERROR: 'user', NO_DATA: 'user', DUPLICATE: 'user',
  RATE_LIMIT: 'user', BUSY: 'user',
  //* 운영전용 — 유저엔 generic("시스템 오류/잠시 후"), 상세는 개발자·관리자 로그 추적용
  REQUEST_INVALID: 'ops', SYSTEM_ERROR: 'ops', UNKNOWN: 'ops',
};

/** ① 우리 서비스가 현재 처리하는 에러핸들링 (codefErrorMessage switch 기준) */
export const OUR_HANDLING: { code: string; category: string; userMessage: string }[] = [
  { code: 'CF-00016', category: 'DUPLICATE', userMessage: '동일한 요청으로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-00025', category: 'AUTH_EXPIRED', userMessage: '이미 응답이 완료되어 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-01004', category: 'AUTH_TIMEOUT', userMessage: '응답 대기시간이 초과되어 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12001', category: 'AUTH_TIMEOUT', userMessage: '응답 대기시간이 초과되어 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12003', category: 'VENDOR_ERROR', userMessage: '기관 오류로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12050', category: 'VENDOR_ERROR', userMessage: '기관 오류로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12051', category: 'AGE_RESTRICTED', userMessage: '나이제한으로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12100', category: 'VENDOR_ERROR', userMessage: '기관 오류로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12200', category: 'NETWORK_ERROR', userMessage: '네트워크 오류로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12701', category: 'VENDOR_ERROR', userMessage: '기관 오류로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12702', category: 'VENDOR_ERROR', userMessage: '기관 오류로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12804', category: 'INPUT_INVALID', userMessage: '잘못된 개인정보로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12835', category: 'INPUT_INVALID', userMessage: '잘못된 개인정보로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12837', category: 'INPUT_INVALID', userMessage: '잘못된 개인정보로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12871', category: 'AUTH_APP_MISSING', userMessage: '앱이 없거나 인증서가 미설치로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-13000', category: 'INPUT_INVALID', userMessage: '잘못된 개인정보로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-13002', category: 'REQUEST_INVALID', userMessage: '잘못된 요청으로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-13300', category: 'AUTH_CODE_WRONG', userMessage: '잘못된 인증번호로 진료정보분석인증이 실패되었어요.' },
  { code: 'CF-12872 / default', category: 'UNKNOWN', userMessage: '진료정보분석인증이 실패되었어요. (구분 없음)' },
];

/**
 * ② 쿠콘(전수 코드 보유) 기준으로 본, 우리가 부족한 핸들링.
 * ⚠️ 전제: 현재 비-codef 벤더는 native코드가 CF-* switch에 안 잡혀 default로 수렴 →
 *    아래 '처리'로 표시된 카테고리도 provider=coocon/octover/hyphen 일 땐 실제로는 노출 안 됨.
 */
export const COOCON_GAPS: {
  category: string;
  cooconExample: string; // 쿠콘 대표 코드/메시지
  ourStatus: GapStatus;
  note: string;
}[] = [
  { category: 'AUTH_CERT', cooconExample: '80004100 인증서 없음 / 80004104 만료 / 80004111 폐기 / 80004101 비번오류', ourStatus: '미흡', note: 'CF-12871로 "앱없음/인증서"만 뭉뚱그림 — 미보유·만료·폐기·비번오류 구분 없음' },
  { category: 'AUTH_NOT_APPROVED', cooconExample: '80004132 미서명(앱 인증 미확인) / 80004128', ourStatus: '없음', note: '간편인증 미승인 별도 처리 없음 → default로 수렴' },
  { category: 'NO_DATA', cooconExample: '42110000 조회 결과 없음', ourStatus: '없음', note: '진료내역 없음을 성공/실패로 구분하지 않음' },
  { category: 'SYSTEM_ERROR', cooconExample: 'GWSM0003/0007/0011, 8000A000', ourStatus: '없음', note: '게이트웨이/입력검증 오류 분류 없음 → default' },
  { category: 'REQUEST_INVALID', cooconExample: '80004200 조회구분, 80007010 조회년월', ourStatus: '미흡', note: 'codef CF-13002만 존재, 비-codef는 미수렴' },
  { category: 'INPUT_INVALID', cooconExample: '80002234 주민-이름 불일치, 80007811 전화번호', ourStatus: '미흡', note: 'codef일 때만 CF-128xx로 처리; 비-codef는 default' },
  { category: 'AUTH_CODE_WRONG', cooconExample: '80004126 유효한 인증번호 아님', ourStatus: '미흡', note: 'codef CF-13300만; 비-codef는 default' },
  { category: 'AUTH_EXPIRED', cooconExample: '80004133 토큰 만료', ourStatus: '미흡', note: 'codef CF-00025만; 비-codef는 default' },
  { category: 'VENDOR_ERROR', cooconExample: '8000E200 무응답, 80002F30 점검', ourStatus: '미흡', note: 'codef CF-120xx만; 비-codef는 default' },
];

/** 매트릭스 대상 벤더 (datahub 미구현 제외) */
export const MATRIX_VENDORS = ['codef', 'coocon', 'octover', 'hyphen'];

/** 테스트 시나리오 (웹 Tester 드롭다운과 동일, '(선택 안함)'/'직접입력…' 제외) */
export const SCENARIOS = [
  '정상 승인 → 수집 성공',
  '간편 인증 거부/미승인',
  '인증 시간 초과(미응답)',
  '앱 미설치 계정',
  '이름 오입력',
  '주민번호 오입력',
  '휴대폰번호 오입력',
  '이름-주민번호 불일치',
  'SMS 인증번호 오입력',
  '통신사 불일치/미선택',
  '인증서 미보유',
  '인증서 비밀번호 오류',
  '만료/폐기 인증서',
  '진료내역 없는 계정',
  '미성년/연령 제한',
  '기관 점검시간대',
  '동일요청 중복',
  '2차 세션 만료 후',
];

/**
 * 중앙화된 사이클 단계 (유저 노출 기준, 벤더 무관 고정).
 * 벤더별 실제 API 호출 수는 달라도(쿠콘 SMS 5콜, codef 2콜) 이 단계로 정규화한다.
 */
export const CYCLE_STAGES = [
  { key: 'REQUEST', label: '인증요청', desc: '본인인증 시작 (앱푸시/SMS 발송)' },
  { key: 'CONFIRM', label: '인증확인', desc: '사용자 승인 또는 인증번호 제출' },
  { key: 'COLLECT', label: '자료수집', desc: '진료내역 조회' },
  { key: 'FINALIZE', label: '종료(비노출)', desc: '세션정리/로그아웃 (유저 비노출)' },
];

/** 벤더별 실제 API가 어느 중앙 단계에 속하는지 (보기용 매핑) */
export const VENDOR_STAGE_MAP: Record<string, { stage: string; apis: string[] }[]> = {
  coocon: [
    { stage: 'REQUEST', apis: ['본인인증', '휴대폰인증_1(SMS)'] },
    { stage: 'CONFIRM', apis: ['휴대폰인증_2(SMS)', 'OTP(간편)'] },
    { stage: 'COLLECT', apis: ['내진료정보열람'] },
    { stage: 'FINALIZE', apis: ['로그아웃'] },
  ],
  codef: [
    { stage: 'REQUEST', apis: ['certify(1차)'] },
    { stage: 'CONFIRM', apis: ['treat(2차)에 포함'] },
    { stage: 'COLLECT', apis: ['treat(2차)에 포함'] },
    { stage: 'FINALIZE', apis: ['—'] },
  ],
  octover: [
    { stage: 'REQUEST', apis: ['phoneVerify / simpleAuthLogin (1차)'] },
    { stage: 'CONFIRM', apis: ['phoneVerifyResult / simpleAuthLoginResult (체인)'] },
    { stage: 'COLLECT', apis: ['AB001 (체인)'] },
    { stage: 'FINALIZE', apis: ['—'] },
  ],
  hyphen: [
    { stage: 'REQUEST', apis: ['captchaPass(SMS) / EASY(간편) 1차'] },
    { stage: 'CONFIRM', apis: ['identityCheck(SMS) / step_data(간편) 2차'] },
    { stage: 'COLLECT', apis: ['2차 응답 outB0002'] },
    { stage: 'FINALIZE', apis: ['—'] },
  ],
};

/** 하니스 raw call의 step 문자열 → 중앙 단계 추정 (catalog/coverage 분류용) */
export function stepToStage(vendor: string, step: string): string {
  const s = (step || '').toLowerCase();
  if (s.includes('토큰')) return 'REQUEST';
  if (s.includes('로그아웃')) return 'FINALIZE';
  if (s.includes('certify') || s.includes('본인인증') || s.includes('휴대폰인증_1') || s.includes('phoneverify') || s.includes('simpleauthlogin') || s.includes('captchapass') || (s.includes('easy'))) {
    // 단, octover/hyphen은 1차 단어가 위에 포함됨
    if (!s.includes('result') && !s.includes('ab001') && !s.includes('identitycheck') && !s.includes('treat')) return 'REQUEST';
  }
  if (s.includes('ab001') || s.includes('내진료정보열람') || s.includes('treat')) return 'COLLECT';
  if (s.includes('휴대폰인증_2') || s.includes('otp') || s.includes('result') || s.includes('identitycheck') || s.includes('step_data')) return 'CONFIRM';
  return 'CONFIRM';
}

/**
 * 이미 "아는" 코드 (테스트 없이도 확보됨). (vendor, scenario) → 예상 native코드.
 *  - coocon: API 문서에 오류코드 전수 존재 → 다수 문서확보
 *  - codef : 우리 codefErrorMessage switch 에 이미 반영된 코드
 *  - octover/hyphen: 문서 공백 → 비움(테스트 필요)
 * ⚠️ "예상(문서기반)"이며, 테스트로 실제값/메시지를 확정한다.
 */
export const KNOWN_BY_DOC: Record<string, Record<string, string>> = {
  coocon: {
    '정상 승인 → 수집 성공': '00000000',
    '간편 인증 거부/미승인': '80004132',
    //* '인증 시간 초과' coocon 전용코드 불명확 → 확인필요(테스트). (80004133은 '2차 세션 만료'로 분리)
    '이름 오입력': '8000B001',
    '주민번호 오입력': '80002231',
    '휴대폰번호 오입력': '80007811',
    '이름-주민번호 불일치': '80002234',
    'SMS 인증번호 오입력': '80004126',
    '인증서 미보유': '80004100',
    '인증서 비밀번호 오류': '80004101',
    '만료/폐기 인증서': '80004104',
    '진료내역 없는 계정': '42110000',
    '기관 점검시간대': '80002F30',
    '기관 장애/세션종료': '80002F20', // 기관 세션종료=VENDOR_ERROR
    '비정상 로그아웃': '80002E40',
    '2차 세션 만료 후': '80004133', // 토큰 유효시간 만료=AUTH_EXPIRED (80002F20과 분리)
  },
  codef: {
    '정상 승인 → 수집 성공': 'CF-00000',
    '인증 시간 초과(미응답)': 'CF-01004',
    '앱 미설치 계정': 'CF-12871',
    '이름 오입력': 'CF-12804',
    '주민번호 오입력': 'CF-13000',
    '이름-주민번호 불일치': 'CF-12804',
    'SMS 인증번호 오입력': 'CF-13300',
    //* '만료/폐기 인증서' codef는 CF-12871(앱/인증서 통합)뿐 — 앱미설치와 코드충돌 → 확인필요
    '미성년/연령 제한': 'CF-12051',
    '기관 점검시간대': 'CF-12100',
    '동일요청 중복': 'CF-00016',
    '2차 세션 만료 후': 'CF-00025',
  },
  hyphen: {
    //* 서비스정의서 4.오류코드표 기반 (errMsg '[코드]' 접두)
    '정상 승인 → 수집 성공': '0000',
    '주민번호 오입력': 'LOGIN-V04',
    '휴대폰번호 오입력': 'I0001-001',
    //* '이름-주민번호 불일치' → I0001-002는 '본인인증 실패' 일반코드(간편 미승인과 충돌) → 확인필요
    '간편 인증 거부/미승인': 'I0001-002',
    //* 'SMS 인증번호 오입력' → I0001-010은 세션종료와 코드중복(메시지의존) → 확인필요로 두고 테스트 확정
    '2차 세션 만료 후': 'I0001-010',
    '진료내역 없는 계정': 'B0002-L01',
    '기관 장애/세션종료': 'I0001-005',
  },
};

/**
 * (vendor, nativeCode) → 통합 category(SNAKE_CASE) 매핑.
 * codef는 OUR_HANDLING 과 동일, coocon은 문서 딕셔너리 분류 기준.
 * octover/hyphen은 테스트 후 catalog.annotate 로 채움.
 */
export const CATEGORY_MAP: Record<string, Record<string, string>> = {
  codef: {
    'CF-00000': 'OK', 'CF-03002': 'OK', // CF-03002 = 1차 성공(추가인증 필요) — 실패 아님
    'CF-00016': 'DUPLICATE', 'CF-00025': 'AUTH_EXPIRED',
    'CF-01004': 'AUTH_TIMEOUT', 'CF-12001': 'AUTH_TIMEOUT',
    'CF-12003': 'VENDOR_ERROR', 'CF-12050': 'VENDOR_ERROR', 'CF-12100': 'VENDOR_ERROR',
    'CF-12701': 'VENDOR_ERROR', 'CF-12702': 'VENDOR_ERROR',
    'CF-12051': 'AGE_RESTRICTED', 'CF-12200': 'NETWORK_ERROR',
    'CF-12804': 'INPUT_INVALID', 'CF-12835': 'INPUT_INVALID', 'CF-12837': 'INPUT_INVALID', 'CF-13000': 'INPUT_INVALID',
    'CF-12871': 'AUTH_APP_MISSING', 'CF-13002': 'REQUEST_INVALID', 'CF-13300': 'AUTH_CODE_WRONG',
    'CF-12872': 'UNKNOWN',
  },
  coocon: {
    '00000000': 'OK',
    '80002230': 'INPUT_INVALID', '80002231': 'INPUT_INVALID', '80002234': 'INPUT_INVALID',
    '8000B001': 'INPUT_INVALID', '8000B122': 'INPUT_INVALID', '80007810': 'INPUT_INVALID', '80007811': 'INPUT_INVALID',
    '800033E0': 'INPUT_INVALID', '800033E2': 'INPUT_INVALID',
    '80002310': 'INPUT_INVALID', '80002311': 'INPUT_INVALID', '80002312': 'INPUT_INVALID', '80002313': 'INPUT_INVALID',
    '80002320': 'INPUT_INVALID', '80002321': 'INPUT_INVALID',
    '80004000': 'AUTH_CERT', '80004040': 'AUTH_CERT', '80004041': 'AUTH_CERT', '80004100': 'AUTH_CERT',
    '80004101': 'AUTH_CERT', '80004102': 'AUTH_CERT', '80004104': 'AUTH_CERT', '80004107': 'AUTH_CERT',
    '80004111': 'AUTH_CERT', '80004113': 'AUTH_CERT', '80004114': 'AUTH_CERT', '80004115': 'AUTH_CERT', '80004016': 'AUTH_CERT',
    '80004132': 'AUTH_NOT_APPROVED', '80004128': 'AUTH_NOT_APPROVED', '80004133': 'AUTH_EXPIRED',
    '80004126': 'AUTH_CODE_WRONG',
    '80004200': 'REQUEST_INVALID', '80004201': 'REQUEST_INVALID', '80007010': 'REQUEST_INVALID', '80007011': 'REQUEST_INVALID',
    '8000E200': 'VENDOR_ERROR', '8000F101': 'VENDOR_ERROR', '80002F10': 'VENDOR_ERROR', '80002F20': 'VENDOR_ERROR',
    '80002F30': 'VENDOR_ERROR', '80002E40': 'VENDOR_ERROR', '8000FFFF': 'VENDOR_ERROR', '80002123': 'VENDOR_ERROR',
    '80002470': 'VENDOR_ERROR', '80002F31': 'VENDOR_ERROR', // (80002F31~3F 페이지변경)
    '42110000': 'NO_DATA',
    'GWSM0001': 'SYSTEM_ERROR', 'GWSM0002': 'SYSTEM_ERROR',
    'GWSM0003': 'SYSTEM_ERROR', 'GWSM0004': 'SYSTEM_ERROR', 'GWSM0007': 'SYSTEM_ERROR', 'GWSM0008': 'SYSTEM_ERROR',
    'GWSM0009': 'SYSTEM_ERROR', 'GWSM0011': 'SYSTEM_ERROR', '8000A000': 'SYSTEM_ERROR', '80007220': 'SYSTEM_ERROR',
    '80002E34': 'SYSTEM_ERROR', '80003391': 'SYSTEM_ERROR',
  },
  octover: {
    '1000200': 'OK', // 성공(자동연동). 실패코드는 테스트로 확보 후 보강
  },
  hyphen: {
    //* (A) 업무 오류코드 — 서비스정의서 '4.오류코드표'. errMsg 에 '[코드]' 접두로 박혀옴.
    //*     ⚠️ 일부 코드(I0001-010, LOGIN-001/003)는 메시지별로 의미가 갈림 → 메시지 병행 확인 필요.
    'VALID-001': 'INPUT_INVALID', 'VALID-002': 'INPUT_INVALID',
    'LOGIN-V04': 'INPUT_INVALID',
    'LOGIN-001': 'AUTH_NOT_APPROVED',
    'LOGIN-002': 'VENDOR_ERROR', 'LOGIN-003': 'VENDOR_ERROR', 'LOGIN-004': 'VENDOR_ERROR', 'LOGIN-005': 'VENDOR_ERROR',
    'B0001-L01': 'NO_DATA', 'B0002-L01': 'NO_DATA',
    'I0001-001': 'INPUT_INVALID',
    'I0001-002': 'AUTH_NOT_APPROVED',
    'I0001-005': 'VENDOR_ERROR', 'I0001-201': 'VENDOR_ERROR',
    'I0001-010': 'AUTH_EXPIRED', // 세션종료 기본 (메시지에 따라 인증실패/요청오류로도 쓰임)
    //* (B) 전송계층 resCd (공통가이드 '(참고)응답코드 정의')
    '0000': 'OK',
    'E101': 'NO_DATA', 'E102': 'NO_DATA', 'V101': 'NO_DATA', 'V102': 'NO_DATA',
    'E201': 'DUPLICATE', 'E202': 'DUPLICATE', 'V201': 'DUPLICATE', 'V202': 'DUPLICATE',
    'E203': 'RATE_LIMIT', 'V203': 'RATE_LIMIT', 'V007': 'RATE_LIMIT',
    'E901': 'VENDOR_ERROR', 'E902': 'VENDOR_ERROR', 'E903': 'VENDOR_ERROR',
    'V901': 'VENDOR_ERROR', 'V902': 'VENDOR_ERROR', 'V903': 'VENDOR_ERROR', 'C021': 'VENDOR_ERROR',
    'E999': 'UNKNOWN',
    'K001': 'SYSTEM_ERROR', 'K002': 'SYSTEM_ERROR', 'V008': 'SYSTEM_ERROR',
    'V001': 'REQUEST_INVALID', 'V002': 'REQUEST_INVALID', 'V003': 'REQUEST_INVALID',
    'V005': 'REQUEST_INVALID', 'V006': 'REQUEST_INVALID',
  },
};

export function categoryOf(vendor: string, code: string): string | null {
  return CATEGORY_MAP[vendor]?.[code] ?? null;
}

/** ③ 테스트로 직접 얻어내야 할 벤더별 미완(테스트필요) 리스트 */
export const TEST_NEEDED: {
  vendor: string;
  what: string;
  detail: string;
}[] = [
  { vendor: 'octover', what: '실패코드 전부', detail: '문서엔 성공(1000200)만. 시나리오별 out.data.{method}.code/msg 수집 필요' },
  { vendor: 'hyphen', what: '업무코드 중복/메시지의존 실측', detail: '서비스정의서에 업무코드 존재(VALID/LOGIN/B000x/I0001, errMsg [코드] 접두). 단 I0001-010·LOGIN-001/003 은 코드중복(메시지별 의미 상이) → 실측으로 메시지↔category 확정 필요' },
  { vendor: 'codef', what: 'CF-* 포털 전체목록 대조', detail: '현 switch는 부분집합. 미관측 CF-* 코드 확인 필요' },
  { vendor: 'coocon', what: '실발생 코드 검증', detail: '문서 전수지만 "기술된 외 추가가능" 명시 → 실제 발생 코드/메시지 검증' },
  { vendor: 'datahub', what: '전체(미구현)', detail: '어댑터 미구현. 구현 후 ECODE/ETRACK 수집' },
];
