/*
 * LIMIT_INQ.pgc - au 모듈 서비스 LIMIT_INQ (one service = one file).
 * 한도 조회 (가용한도 계산).
 * Moved verbatim from the former inline au_svr.pgc; runs on the caller's XA
 * branch (no EXEC SQL CONNECT). Advertised by au_svr at tpsvrinit.
 */
#include "LIMIT_INQ.h"

/*
 * 심화 구현 (LIMIT_INQ): 한도 조회 계약(T_KEY1 -> T_AMT1/T_AMT2/T_AMT3)은
 * 원형 그대로 보존하고, 그 위에 입력검증/상태점검/월누적/사용률/경보 구간을
 * 덧대어 조회 응답을 풍부하게 한다. 원장 미존재·비활성 카드도 구분 처리한다.
 */
void LIMIT_INQ(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *svcname = "LIMIT_INQ";
    char card[24];
    long lim = 0, used = 0, avail = 0, rc = 0, util_bps = 0;
    long pertx = 0, est_txn = 0;
    char tier[16];
    EXEC SQL BEGIN DECLARE SECTION;
    char h_card[24], h_status[16], h_bizdate[16];
    long h_temp = 0, h_month = 0, h_cnt = 0, h_daily = 0, h_used = 0;
    long h_black = 0, h_vel = 0, h_risk = 0, h_decl = 0, h_avg = 0;
    EXEC SQL END DECLARE SECTION;

    memset(h_status, 0, sizeof(h_status));
    memset(tier, 0, sizeof(tier));
    gets_(b, T_KEY1, card, sizeof(card));
    userlog("[%s] 진입: 한도 조회 card=%s", svcname, card);

    /* 1) 입력 검증부 */
    if (card[0] == 0) { setl(b, T_RC, 8801L);
        Bchg(b, T_MSG, 0, "카드번호(T_KEY1) 필수", 0L);
        userlog("[%s] 입력검증 실패: 카드번호 누락", svcname); FAIL(b); }
    strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0;
    gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));

    /* 2) 마스터 조회부: 원장 상세(상태/임시한도 포함) */
    EXEC SQL SELECT daily_limit, used_amount, temp_limit, status
        INTO :h_daily, :h_used, :h_temp, :h_status
        FROM card_limit WHERE card_no = :h_card;
    if (sqlca.sqlcode == 100) {
        /* 원장이 없으면 원형 dbio 폴백(계약 보존) */
        if (audb_limit_get(card, &lim, &used) < 0) { setl(b, T_RC, 41L); FAIL(b); }
        setl(b, T_AMT1, lim); setl(b, T_AMT2, used); setl(b, T_AMT3, lim - used);
        setl(b, T_RC, 0);
        userlog("[%s] 원장 미존재 폴백 card=%s", svcname, card);
        OK(b);
    }
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 42L); FAIL(b); }
    lim = h_daily; used = h_used;

    /* 3) 업무규칙 분기부: 상태/소진율 구간 */
    avail = h_daily + h_temp - h_used;         /* 임시한도 포함 가용 */
    util_bps = (h_daily > 0) ? (h_used * 10000L / h_daily) : 0L;
    if (strcmp(h_status, "ACTIVE") != 0) { rc = 8; }
    else if (avail <= 0) { rc = 2; }
    else if (util_bps >= 9000) { rc = 7; }     /* 90% 이상 경보 */
    else { rc = 0; }

    /* 4) 본처리부: 당월 누적/건수 재집계 */
    EXEC SQL SELECT coalesce(sum(amount),0), count(*) INTO :h_month, :h_cnt
        FROM au_authorization WHERE card_no = :h_card AND status IN ('A','M','S')
          AND (:h_bizdate = '' OR date_trunc('month', biz_date)
                                 = date_trunc('month', :h_bizdate::date));
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 43L); FAIL(b); }

    /* 5) 이력/재집계부: 당일 승인 건수 참고 */
    EXEC SQL SELECT count(*) INTO :h_cnt FROM au_authorization
        WHERE card_no = :h_card AND biz_date = current_date;
    if (sqlca.sqlcode < 0) h_cnt = 0;

    /* 6) 요약부: 사용률/월누적 부가필드 */
    setl(b, T_ARG1, util_bps);
    setl(b, T_GROSS, h_month);
    setl(b, T_COUNT, h_cnt);
    Bchg(b, T_STATUS, 0, h_status, 0L);

    /* ---------------------------------------------------------------
     * 6-a) 부가 판정부 (LIMIT_INQ): 한도 조회 응답에 실무에서 함께 보는
     *      리스크 지표를 덧붙인다. 모두 SELECT 전용이라 한도 원장에는 어떤
     *      영향도 주지 않으며, 보존 계약 필드(T_AMT1/2/3)는 건드리지 않는다.
     * --------------------------------------------------------------- */

    /* 부가 판정 (1): 한도 등급 산정 - 일한도 구간으로 BASIC~PLATINUM */
    if (h_daily >= 5000000L)      strcpy(tier, "PLATINUM");
    else if (h_daily >= 2000000L) strcpy(tier, "GOLD");
    else if (h_daily >= 1000000L) strcpy(tier, "SILVER");
    else                          strcpy(tier, "BASIC");
    Bchg(b, T_STR1, 0, tier, 0L);

    /* 부가 판정 (2): 건별(1회) 한도 - 일한도의 20%를 상한으로 환산 */
    pertx = h_daily / 5L;
    setl(b, T_ARG2, pertx);

    /* 부가 판정 (3): 블랙리스트 등재 여부 */
    EXEC SQL SELECT count(*) INTO :h_black FROM au_blacklist WHERE card_no = :h_card;
    if (sqlca.sqlcode < 0) h_black = 0;
    if (h_black > 0) {
        rc = 91;
        userlog("[%s] 블랙리스트 등재 card=%s", svcname, card);
    }

    /* 부가 판정 (4): velocity - 직전 1시간 승인 시도 건수 */
    EXEC SQL SELECT count(*) INTO :h_vel FROM au_authorization
        WHERE card_no = :h_card AND created_at > now() - interval '1 hour';
    if (sqlca.sqlcode < 0) h_vel = 0;
    if (h_vel >= 10) {
        if (rc == 0) rc = 93;
        userlog("[%s] velocity 경보 card=%s 1시간 %ld건", svcname, card, h_vel);
    }

    /* 부가 판정 (5): 이상거래 누적 최고점수 */
    EXEC SQL SELECT coalesce(max(score),0) INTO :h_risk
        FROM au_fraud_log WHERE card_no = :h_card;
    if (sqlca.sqlcode < 0) h_risk = 0;
    if (h_risk >= 90 && rc == 0) rc = 94;
    setl(b, T_ARG3, h_risk);
    setl(b, T_ARG4, h_vel);

    /* 부가 판정 (6): 당일 거절 건수 - 한도부족 반복 여부 파악 */
    EXEC SQL SELECT count(*) INTO :h_decl FROM au_authorization
        WHERE card_no = :h_card AND biz_date = current_date AND status = 'D';
    if (sqlca.sqlcode < 0) h_decl = 0;
    setl(b, T_ARG5, h_decl);
    if (h_decl >= 5 && rc == 0) rc = 95;

    /* 부가 판정 (7): 잔여 소진 예상 - 당일 평균 승인단가 대비 잔여 건수 */
    EXEC SQL SELECT coalesce(avg(amount),0)::bigint INTO :h_avg
        FROM au_authorization
        WHERE card_no = :h_card AND biz_date = current_date AND status IN ('A','M','S');
    if (sqlca.sqlcode < 0) h_avg = 0;
    est_txn = (h_avg > 0) ? (avail / h_avg) : 0L;
    setl(b, T_ARG6, est_txn);
    userlog("[%s] 부가판정 tier=%s 건별상한=%ld 잔여예상건수=%ld 위험=%ld",
            svcname, tier, pertx, est_txn, h_risk);

    /* 7) 예외/보상: 조회 전용이라 보상 트랜잭션이 필요 없다. 위 SELECT 가
     *    실패해도 한도 계약 필드는 이미 확정되어 있으므로 0 으로 낮춰 담고
     *    응답을 계속한다(조회 서비스의 가용성 우선). */
    /* 8) 응답부: 보존 계약 필드(T_AMT1/2/3) 세팅 */
    setl(b, T_AMT1, lim);                 /* 한도 (보존) */
    setl(b, T_AMT2, used);                /* 사용액 (보존) */
    setl(b, T_AMT3, avail);               /* 가용한도 (보존) */
    setl(b, T_RC, rc);
    /* 9) 완료 로깅 */
    userlog("[%s] 완료 card=%s lim=%ld used=%ld avail=%ld util=%ldbps",
            svcname, card, lim, used, avail, util_bps);
    OK(b);
}
