/*
 * CARD_STOLEN_REG.pgc - au 모듈 서비스 CARD_STOLEN_REG (one service = one file).
 * 도난 등록: 원장 상태를 STOLEN 으로 전환하고 블랙리스트를 STOLEN 사유로
 * 갱신한 뒤, 최고점(999) 부정거래 로그를 남겨 사후추적 근거를 만든다.
 * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr.
 */
#include "CARD_STOLEN_REG.h"

/*
 * 심화 구현 노트 (CARD_STOLEN_REG):
 *   입력검증→마스터/정책조회→업무규칙분기→본처리→이력/재집계→예외/보상→응답
 *   의 9부 구조로 전개한다. 승인/한도 원장(au_authorization/card_limit)과
 *   집계(au_summary)만 사용하며 새 테이블은 만들지 않는다.
 */
void CARD_STOLEN_REG(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *svcname = "CARD_STOLEN_REG";
    long rc = 0, outv = 0, avail = 0, total = 0, buckets = 0, peak = 0;
    char outs[64], res_status[16], outmsg[128], peakkey[24];
    long fee_amt = 0, vat_amt = 0;
    char nextbiz[16];
    EXEC SQL BEGIN DECLARE SECTION;
    char h_card[24], h_card2[24], h_merch[64], h_bizdate[16], h_status[16];
    char h_reason[160], h_type[16], h_appr[16], h_grp[24], h_ccy[8];
    char h_channel[16], h_category[16], h_issuer[16], h_docno[40], h_mstat[16];
    long h_amount = 0, h_limit = 0, h_used = 0, h_temp = 0, h_cnt = 0;
    long h_sum = 0, h_appr_cnt = 0, h_dec_cnt = 0, h_auth = 0, h_id = 0;
    long h_delta = 0, h_install = 0, h_score = 0, h_v1 = 0, h_v2 = 0;
    long h_v3 = 0, h_gcnt = 0, h_gsum = 0, h_mdr = 0, h_dlim = 0;
    long h_black = 0, h_vel = 0, h_risk = 0, h_hist = 0, h_prev = 0;
    char h_cstat[16];
    EXEC SQL END DECLARE SECTION;

    memset(outs, 0, sizeof(outs)); memset(res_status, 0, sizeof(res_status));
    memset(outmsg, 0, sizeof(outmsg)); memset(peakkey, 0, sizeof(peakkey));
    memset(h_card, 0, sizeof(h_card)); memset(h_merch, 0, sizeof(h_merch));
    memset(h_bizdate, 0, sizeof(h_bizdate)); memset(h_status, 0, sizeof(h_status));
    memset(h_reason, 0, sizeof(h_reason)); memset(h_mstat, 0, sizeof(h_mstat));
    memset(h_grp, 0, sizeof(h_grp)); memset(h_appr, 0, sizeof(h_appr));
    memset(nextbiz, 0, sizeof(nextbiz)); memset(h_cstat, 0, sizeof(h_cstat));
    userlog("[%s] 진입: 도난 등록: 원장 상태를 STOLEN 으로 전환하고 블랙리스트를 STOL", svcname);

    /* 공통 컨텍스트 로드 (요청에 없으면 공백/0) */
    gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
    h_amount = getl(b, T_AMOUNT);

    /* 1) 입력 검증부: 이 서비스가 읽는 필드를 개별 코드로 점검 */
    gets_(b, T_KEY1, h_card, sizeof(h_card));
    gets_(b, T_REASON, h_reason, sizeof(h_reason));
    gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
    if (h_card[0] == 0) { setl(b, T_RC, 8801L); Bchg(b, T_MSG, 0, "카드번호 필수 누락", 0L); userlog("[%s] 입력검증 실패: 카드번호 누락", svcname); FAIL(b); }
    if (h_reason[0] == 0) { setl(b, T_RC, 8802L); Bchg(b, T_MSG, 0, "사유 필수 누락", 0L); userlog("[%s] 입력검증 실패: 사유 누락", svcname); FAIL(b); }
    if (h_bizdate[0] == 0) { setl(b, T_RC, 8803L); Bchg(b, T_MSG, 0, "영업일 필수 누락", 0L); userlog("[%s] 입력검증 실패: 영업일 누락", svcname); FAIL(b); }
    if (strlen(h_bizdate) < 8) { setl(b, T_RC, 8804L); Bchg(b, T_MSG, 0, "영업일 형식 오류(YYYY-MM-DD)", 0L); FAIL(b); }

    /* 2) 카드 상태 종합 조회부: 원장 + 블랙리스트 + 당일 승인 */
    EXEC SQL SELECT l.daily_limit, l.used_amount, l.temp_limit, l.status,
        (SELECT count(*) FROM au_blacklist k WHERE k.card_no = l.card_no),
        (SELECT count(*) FROM au_authorization a WHERE a.card_no = l.card_no
            AND a.biz_date = current_date)
        INTO :h_limit, :h_used, :h_temp, :h_status, :h_v1, :h_cnt
        FROM card_limit l WHERE l.card_no = :h_card;
    if (sqlca.sqlcode == 100) { setl(b, T_RC, 61L);
        Bchg(b, T_MSG, 0, "카드 원장 미존재", 0L);
        userlog("[%s] 원장없음 card=%s", svcname, h_card); FAIL(b); }
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 62L); FAIL(b); }
    avail = h_limit + h_temp - h_used;
    strncpy(res_status, h_status, sizeof(res_status)-1);

    /* 3) 전이 규칙 분기부: 현재 상태에서 STOLEN 로의 전이 허용판정 */
    if (strcmp(h_status, "STOLEN") == 0) { rc = 9; strcpy(outmsg, "이미 해당 상태"); goto card_done; }

    /* 4) 본처리부: 원장 상태 전이 */
    EXEC SQL UPDATE card_limit SET status = 'STOLEN', updated_at = now()
        WHERE card_no = :h_card;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 64L); FAIL(b); }
    if (sqlca.sqlerrd[2] == 0) { setl(b, T_RC, 65L); FAIL(b); }
    strcpy(res_status, "STOLEN"); rc = 0;
    /* 분실/도난: 블랙리스트에 사유와 함께 등재 */
    EXEC SQL INSERT INTO au_blacklist (card_no, reason) VALUES (:h_card, 'STOLEN')
        ON CONFLICT (card_no) DO UPDATE SET reason = EXCLUDED.reason;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 66L); FAIL(b); }
card_done:
    /* 5) 재집계부: 카드 상태별 승인 분포 */
    EXEC SQL SELECT count(*) FILTER (WHERE status='A'), count(*) FILTER (WHERE status='D')
        INTO :h_appr_cnt, :h_dec_cnt FROM au_authorization WHERE card_no = :h_card;
    if (sqlca.sqlcode < 0) { h_appr_cnt = 0; h_dec_cnt = 0; }
    /* 6) 요약부 */
    h_sum = h_appr_cnt + h_dec_cnt; outv = avail;
    /* 7) 예외/보상: 원장/블랙 갱신 실패는 XA 롤백으로 정합 유지 */
    userlog("[%s] 카드처리 card=%s status=%s rc=%ld", svcname, h_card, res_status, rc);

    /* ---------------------------------------------------------------
     * 5) 이력/감사 적재부 (CARD_STOLEN_REG)
     *    본 스키마에는 범용 이력(cm_log) 테이블이 없으므로, 스펙에 따라
     *    SELECT 기반 재집계로 감사 요약을 만들고 userlog 로 처리 이력을
     *    남긴다. 승인 원장 자체가 이력원장 역할을 겸한다.
     * --------------------------------------------------------------- */
    if (h_card[0]) {
        EXEC SQL SELECT count(*), coalesce(sum(amount),0)
            INTO :h_hist, :h_prev FROM au_authorization
            WHERE card_no = :h_card AND (:h_bizdate = '' OR biz_date = :h_bizdate::date);
        if (sqlca.sqlcode < 0) { h_hist = 0; h_prev = 0; }
        userlog("[%s] 감사요약 card=%s 누적건수=%ld 누적금액=%ld",
                svcname, h_card, h_hist, h_prev);
    }

    /* 추가 업무규칙 분기부 (1): 블랙리스트 등재 여부 - 등재분은 경보코드로 */
    if (h_card[0]) {
        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) {
            if (rc == 0) rc = 91;
            strcpy(outmsg, "블랙리스트 등재 카드");
            userlog("[%s] 블랙리스트 적중 card=%s rc=%ld", svcname, h_card, rc);
        }
    }

    /* 추가 업무규칙 분기부 (2): 카드 상태 검증 (한도원장 status) */
    if (h_card[0]) {
        EXEC SQL SELECT status INTO :h_cstat
            FROM card_limit WHERE card_no = :h_card;
        if (sqlca.sqlcode == 100) strcpy(h_cstat, "NONE");
        else if (sqlca.sqlcode < 0) strcpy(h_cstat, "ERR");
        if (strcmp(h_cstat, "ACTIVE") != 0 && strcmp(h_cstat, "NONE") != 0) {
            if (rc == 0) rc = 92;
            userlog("[%s] 카드상태 비정상 card=%s status=%s", svcname, h_card, h_cstat);
        }
    }

    /* 추가 업무규칙 분기부 (3): velocity 판정 - 직전 1시간 승인 시도 건수 */
    if (h_card[0]) {
        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;
            strcpy(outmsg, "단시간 승인시도 과다(velocity)");
            userlog("[%s] velocity 경보 card=%s 1시간 %ld건", svcname, h_card, h_vel);
        }
    }

    /* 추가 업무규칙 분기부 (4): 이상거래 누적 점수(최고점) 조회 */
    if (h_card[0]) {
        EXEC SQL SELECT coalesce(max(score),0), count(*) INTO :h_risk, :h_v3
            FROM au_fraud_log WHERE card_no = :h_card;
        if (sqlca.sqlcode < 0) { h_risk = 0; }
        if (h_risk >= 90) {
            strcpy(outmsg, "고위험 카드(부정 적발이력)");
            userlog("[%s] 고위험 카드 card=%s 최고점수=%ld", svcname, h_card, h_risk);
        }
    }

    /* 6) 집계 갱신부: 가맹점 당일 승인 집계를 재계산해 au_summary 로 upsert */
    if (h_merch[0] && h_bizdate[0]) {
        EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'A'),
                        count(*) FILTER (WHERE status = 'D'), coalesce(sum(amount),0)
            INTO :h_gcnt, :h_appr_cnt, :h_dec_cnt, :h_gsum
            FROM au_authorization
            WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
        if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 108L);
            Bchg(b, T_MSG, 0, "가맹점 집계 재계산 실패", 0L);
            userlog("[%s] 집계 재계산 실패 merch=%s", svcname, h_merch);
            FAIL(b);
        }
        if (audb_upsert_auth_summary(h_merch, h_bizdate, h_gcnt,
                                     h_appr_cnt, h_dec_cnt, h_gsum) < 0) {
            setl(b, T_RC, 109L);
            Bchg(b, T_MSG, 0, "가맹점 집계 upsert 실패", 0L);
            FAIL(b);
        }
        userlog("[%s] 집계갱신 merch=%s 건수=%ld 승인=%ld 거절=%ld 금액=%ld",
                svcname, h_merch, h_gcnt, h_appr_cnt, h_dec_cnt, h_gsum);
    }

    /* 정산 참고치: 매입 수수료/부가세/차기 영업일 (libacqcommon 공통유틸) */
    if (h_amount > 0) {
        fee_amt = acq_fee(h_amount, (h_mdr > 0 ? h_mdr : 25L));
        vat_amt = acq_vat(fee_amt);
    }
    if (h_bizdate[0] && acq_next_bizday(h_bizdate, nextbiz) == 0) {
        userlog("[%s] 정산참고 차기영업일=%s 수수료=%ld 부가세=%ld",
                svcname, nextbiz, fee_amt, vat_amt);
    }

    /* 7) 예외/보상부: 위 각 구간의 EXEC SQL 실패는 그 지점에서 T_RC/T_MSG 를
     *    세팅한 뒤 FAIL(b) → tpreturn(TPFAIL) 로 되돌린다. 서비스가 호출자와
     *    같은 XA 브랜치에서 돌기 때문에 TPFAIL 은 브랜치 롤백을 유발하고,
     *    따라서 원장/한도/집계가 부분만 반영되는 상태가 남지 않는다. */
    if (outmsg[0]) Bchg(b, T_MSG, 0, outmsg, 0L);
    if (fee_amt > 0) setl(b, T_FEE, fee_amt);

    /* ---------------------------------------------------------------
     * 업무체인: CARD_STOLEN_REG → MG_SEND (도난등록 통보 전문 송신)
     * XA 규율: 하위 서비스는 같은 글로벌 트랜잭션의 '다른 브랜치'라서 이 서비스가
     *   방금 INSERT/UPDATE 한 미커밋 행을 볼 수 없다. 그러므로 하위가 필요로 하는
     *   값은 모두 전문(T_*)에 실어 넘기고, 하위 실패 시에는 T_RC/T_MSG 를 채운 뒤
     *   FAIL(b) → tpreturn(TPFAIL) 로 체인 전체를 롤백한다(별도 보상 불필요).
     * --------------------------------------------------------------- */
    if (h_card[0] && strlen(h_bizdate) >= 8) {
        long ch_len = 0, ch_rc = 0;
        char ch_str1[64], ch_chan[16];
        ch_str1[0] = 0; ch_chan[0] = 0;
        gets_(b, T_STR1, ch_str1, sizeof(ch_str1));      /* 원 전문값 보존 */
        gets_(b, T_CHANNEL, ch_chan, sizeof(ch_chan));
        if (strlen(ch_chan) < 2) strcpy(ch_chan, "CH01");   /* mg 기본 채널 */
        Bchg(b, T_CHANNEL, 0, ch_chan, 0L);
        Bchg(b, T_STR1, 0, "0304", 0L);                 /* MTI (전문유형) */
        if (h_merch[0]) Bchg(b, T_MERCHANT, 0, h_merch, 0L);
        Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);
        setl(b, T_AMOUNT, (h_amount > 0 ? h_amount : 0L));
        userlog("[%s] 체인 호출 → MG_SEND 채널=%s MTI=0304", svcname, ch_chan);
        if (tpcall("MG_SEND", (char *)b, 0L, (char **)&b, &ch_len, 0L) < 0) {
            userlog("[%s] tpcall(MG_SEND) 실패: %s", svcname, tpstrerror(tperrno));
            setl(b, T_RC, 7136L);
            Bchg(b, T_MSG, 0, "도난등록 통보 전문 체인 실패", 0L);
            FAIL(b);
        }
        ch_rc = getl(b, T_RC);                 /* 게이트웨이 응답 반영 */
        Bchg(b, T_STR1, 0, ch_str1, 0L);       /* 원 전문값 복원 */
        setl(b, T_AMOUNT, h_amount);
        if (ch_rc != 0 && rc == 0) rc = 7136L;
        userlog("[%s] 도난등록 통보 전문 송신 요청 rc=%ld", svcname, ch_rc);
    }

    /* 8) 응답부: 성공 결과코드 + 계약상 출력 필드 세팅 */
    setl(b, T_ID1, h_auth);
    setl(b, T_RC, rc);
    userlog("[%s] 완료: rc=%ld cnt=%ld sum=%ld id=%ld", svcname, rc, h_cnt, h_sum, (long)(h_auth?h_auth:h_id));
    OK(b);
}
