/*
 * AUTH_VERIFY.pgc - au 모듈 서비스 AUTH_VERIFY (one service = one file).
 * AVS/CVV 검증 + 리스크 판정: 주소검증(AVS)·카드검증값(CVV) 코드로 리스크 점수를
 *   산출하고, 임계값으로 승인판정(decision)을 내려 au_verify 원장에 적재한다.
 *     risk_score > 900 → DECLINE, > 750 → REVIEW, 그 외 → APPROVE.
 *   AVS/CVV 응답 품질이 나쁠수록 가점(위험↑). 3DS 책임미전환(MERCHANT)이면 가중.
 * 처리단계: 입력검증 -> 매입/기존검증/3DS 조회 -> 리스크 산출 -> 판정분기 ->
 *   본처리(INSERT) -> 적재 재검증 -> 응답 -> 로깅.
 * 호출자 XA 브랜치에서 수행(EXEC SQL CONNECT 없음). au_svr 가 tpsvrinit 에서 광고.
 * XA 격리: 체인 내 미커밋 매입은 안 보일 수 있어 미존재(100)는 정상 처리한다.
 */
#include "AUTH_VERIFY.h"

/* AUTH_VERIFY - AVS/CVV 검증 + 리스크 판정. */
void AUTH_VERIFY(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *svcname = "AUTH_VERIFY";
    /* ---- 지역 스크래치 (DECLARE SECTION 밖에서 초기화) ---- */
    long rc = 0, risk = 0;
    char avsbuf[8], cvvbuf[8];

    EXEC SQL BEGIN DECLARE SECTION;
    long h_pid;
    char h_avs[8];
    char h_cvv[8];
    long h_score;
    char h_dec[16];
    long h_vrf;
    long h_prior;
    long h_pcnt;
    char h_liab[16];
    EXEC SQL END DECLARE SECTION;

    memset(avsbuf, 0, sizeof(avsbuf)); memset(cvvbuf, 0, sizeof(cvvbuf));
    h_pid = 0; h_score = 0; h_vrf = 0; h_prior = 0; h_pcnt = 0;
    memset(h_avs, 0, sizeof(h_avs)); memset(h_cvv, 0, sizeof(h_cvv));
    memset(h_dec, 0, sizeof(h_dec)); memset(h_liab, 0, sizeof(h_liab));
    strcpy(h_liab, "NONE");
    userlog("[%s] 진입: AVS/CVV 검증 + 리스크 판정", svcname);

    /* ===== 1) 입력 검증부 ===== */
    h_pid = getl(b, T_PURCHASE_ID);
    gets_(b, T_STR1, avsbuf, sizeof(avsbuf));
    gets_(b, T_STR2, cvvbuf, sizeof(cvvbuf));
    if (h_pid <= 0) {
        setl(b, T_RC, 8501L); Bchg(b, T_MSG, 0, "매입 ID(purchase_id) 필수", 0L);
        userlog("[%s] 입력검증 실패: pid=%ld", svcname, h_pid); FAIL(b);
    }
    if (avsbuf[0] == 0) strcpy(avsbuf, "U");   /* 미제공 → 불가(U) */
    if (cvvbuf[0] == 0) strcpy(cvvbuf, "U");
    strncpy(h_avs, avsbuf, sizeof(h_avs) - 1); h_avs[sizeof(h_avs) - 1] = 0;
    strncpy(h_cvv, cvvbuf, sizeof(h_cvv) - 1); h_cvv[sizeof(h_cvv) - 1] = 0;

    /* ===== 2) 조회부 (매입 존재 + 기존 검증/3DS 책임 현황) ===== */
    EXEC SQL SELECT count(*) INTO :h_pcnt FROM purchase WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0) {
        setl(b, T_RC, 8510L); Bchg(b, T_MSG, 0, "매입 조회 오류", 0L);
        userlog("[%s] 매입 조회 오류 [%d]", svcname, sqlca.sqlcode); FAIL(b);
    }
    if (h_pcnt == 0)
        userlog("[%s] 매입 미커밋/미존재 pid=%ld — 검증만 기록", svcname, h_pid);
    EXEC SQL SELECT count(*) INTO :h_prior FROM au_verify WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0) { h_prior = 0; }
    EXEC SQL SELECT coalesce(max(liability), 'NONE') INTO :h_liab
        FROM au_3ds WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) { strcpy(h_liab, "NONE"); }

    /* ===== 3) 리스크 산출부 (AVS/CVV 품질 → 가점) =====
     * AVS: Y 일치=0, A 부분=50, Z 우편만=80, N 불일치=200, U 불가=150.
     * CVV: M 일치=0, N 불일치=400, P 미처리=150, U 불가=150.
     * 3DS 책임 미전환(MERCHANT)면 +100. pid 기반 소폭 분산 후 0~999 로 클램프. */
    if      (strcmp(h_avs, "Y") == 0) risk += 0;
    else if (strcmp(h_avs, "A") == 0) risk += 50;
    else if (strcmp(h_avs, "Z") == 0) risk += 80;
    else if (strcmp(h_avs, "N") == 0) risk += 200;
    else                              risk += 150;   /* U 및 기타 */
    if      (strcmp(h_cvv, "M") == 0) risk += 0;
    else if (strcmp(h_cvv, "N") == 0) risk += 400;
    else if (strcmp(h_cvv, "P") == 0) risk += 150;
    else                              risk += 150;   /* U 및 기타 */
    if (strcmp(h_liab, "MERCHANT") == 0) {
        risk += 100;
        userlog("[%s] 3DS 책임 미전환(MERCHANT) — 위험 가중 pid=%ld", svcname, h_pid);
    }
    risk += (long)(h_pid % 50);                      /* 소폭 분산 */
    if (risk < 0)   risk = 0;
    if (risk > 999) risk = 999;
    h_score = risk;

    /* ===== 4) 판정 분기부 (임계값) ===== */
    if (risk > 900) {
        strcpy(h_dec, "DECLINE"); rc = 2;
    } else if (risk > 750) {
        strcpy(h_dec, "REVIEW");  rc = 1;
    } else {
        strcpy(h_dec, "APPROVE"); rc = 0;
    }
    userlog("[%s] 리스크 산출 pid=%ld avs=%s cvv=%s liab=%s score=%ld dec=%s",
            svcname, h_pid, h_avs, h_cvv, h_liab, h_score, h_dec);

    /* ===== 5) 본처리부 (au_verify 채번·INSERT) ===== */
    EXEC SQL SELECT nextval('au_verify_seq') INTO :h_vrf;
    if (sqlca.sqlcode < 0) {
        setl(b, T_RC, 8520L); Bchg(b, T_MSG, 0, "검증 시퀀스 채번 실패", 0L);
        userlog("[%s] 시퀀스 채번 실패 [%d]", svcname, sqlca.sqlcode); FAIL(b);
    }
    EXEC SQL INSERT INTO au_verify
        (vrf_id, purchase_id, avs_code, cvv_code, risk_score, decision)
        VALUES (:h_vrf, :h_pid, :h_avs, :h_cvv, :h_score, :h_dec);
    if (sqlca.sqlcode < 0) {
        setl(b, T_RC, 8521L); Bchg(b, T_MSG, 0, "검증결과 적재 실패", 0L);
        userlog("[%s] au_verify 적재 실패 [%d]", svcname, sqlca.sqlcode); FAIL(b);
    }

    /* ===== 6) 적재 재검증부 (기록행 재조회) ===== */
    EXEC SQL SELECT decision, risk_score INTO :h_dec, :h_score
        FROM au_verify WHERE vrf_id = :h_vrf;
    if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) {
        setl(b, T_RC, 8530L); Bchg(b, T_MSG, 0, "검증 적재 재검증 실패", 0L);
        userlog("[%s] 재검증 실패 vrf=%ld code=%d", svcname, h_vrf, sqlca.sqlcode); FAIL(b);
    }

    /* ===== 7) 응답부 ===== */
    Bchg(b, T_STATUS, 0, h_dec, 0L);   /* decision: APPROVE/REVIEW/DECLINE */
    setl(b, T_ARG1, h_score);          /* risk_score */
    setl(b, T_ID1, h_vrf);
    setl(b, T_RC, rc);
    Bchg(b, T_MSG, 0,
         (rc == 2 ? "리스크 거절" : (rc == 1 ? "리스크 검토대상" : "검증 통과")), 0L);

    /* ===== 8) 완료 로깅 ===== */
    userlog("[%s] 완료: pid=%ld decision=%s score=%ld vrf=%ld rc=%ld",
            svcname, h_pid, h_dec, h_score, h_vrf, rc);
    OK(b);
}

/* vim: set ts=4 sw=4 et smartindent: */
