/*
 * ACQ_HOLD.pgc - ac 모듈 서비스 ACQ_HOLD (one service = one file).
 * 매입 보류: 접수/대사(A,M) 상태만 보류로 전이한다. 보류 사유를 미매입 원장에
 * 남기고 가맹점 보류 적체 현황을 함께 회신한다.
 * 입출력 계약 보존: 입력 T_PURCHASE_ID, 출력 T_STATUS='H' 및
 *                   T_RC (반영 0 / 미반영 1, 미반영도 정상 응답).
 * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
 */
#include "ACQ_HOLD.h"

void ACQ_HOLD(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *SVC = "ACQ_HOLD";
    char reason[128], logmsg[256];
    long grade = 0;
    EXEC SQL BEGIN DECLARE SECTION;
    long h_pid, h_aff;
    long h_amt = 0, h_cnt = 0, h_sum = 0, h_id = 0, h_logid = 0, h_ctx = 0, h_unm = 0;
    char h_prev[8], h_merch[64], h_bizdate[16], h_reason[128], h_logmsg[256];
    EXEC SQL END DECLARE SECTION;

    userlog("%s 진입: 매입 보류 요청", SVC);

    /* 1) 입력 검증부 */
    h_pid = getl(b, T_PURCHASE_ID);
    if (h_pid <= 0) {
        Bchg(b, T_MSG, 0, "매입 ID 오류", 0L); setl(b, T_RC, 102); FAIL(b);
    }
    gets_(b, T_REASON, reason, sizeof(reason));
    if (reason[0] == 0) strcpy(reason, "HOLD");
    strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0;

    gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
    if (h_bizdate[0] == 0) {
        EXEC SQL SELECT param_val INTO :h_bizdate FROM cm_param WHERE param_key = 'BIZ_DATE';
        if (sqlca.sqlcode != 0) strcpy(h_bizdate, "2026-07-19");
    }

    /* 2) 마스터/상태 조회부 */
    EXEC SQL SELECT status, merchant_id, amount INTO :h_prev, :h_merch, :h_amt
        FROM purchase WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 매입원장 조회 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    if (sqlca.sqlcode == 100) {
        Bchg(b, T_MSG, 0, "매입 미존재", 0L); setl(b, T_RC, 111); FAIL(b);
    }
    userlog("%s 컨텍스트: pid=%ld merch=%s 상태=%s 금액=%ld", SVC, h_pid, h_merch, h_prev, h_amt);

    /* 3) 업무규칙 분기부: 금액/상태 구간별 보류 등급 (3+ 분기) */
    if (h_amt >= 10000000L) {
        grade = 3; Bchg(b, T_MSG, 0, "고액 매입 보류", 0L);
    } else if (strcmp(h_prev, "M") == 0) {
        grade = 2; Bchg(b, T_MSG, 0, "대사완료건 보류", 0L);
    } else if (strcmp(h_prev, "A") == 0) {
        grade = 1; Bchg(b, T_MSG, 0, "접수건 보류", 0L);
    } else {
        grade = 0; Bchg(b, T_MSG, 0, "보류 불가 상태", 0L);
    }

    /* 4) 본처리부: only an approved(A)/matched(M) purchase may be put on hold */
    EXEC SQL UPDATE purchase SET status = 'H', updated_at = now()
        WHERE purchase_id = :h_pid AND status IN ('A', 'M');
    if (sqlca.sqlcode < 0) {
        userlog("ACQ_HOLD FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    h_aff = sqlca.sqlerrd[2];

    /* 반영된 경우에만 보류 사유를 미매입 원장에 남긴다 */
    if (h_aff > 0) {
        EXEC SQL SELECT nextval('ac_unmatch_seq') INTO :h_id;
        if (sqlca.sqlcode < 0) { setl(b, T_RC, 90); FAIL(b); }
        EXEC SQL INSERT INTO ac_unmatch (unmatch_id, purchase_id, reason, biz_date)
            VALUES (:h_id, :h_pid, :h_reason, :h_bizdate);
        if (sqlca.sqlcode < 0) {
            userlog("%s: 보류사유 적재 실패 [%d]", SVC, sqlca.sqlcode);
            setl(b, T_RC, 90); FAIL(b);
        }
    }

    /* 6) 집계/요약 갱신부: 가맹점 보류 적체 재집계 */
    EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_sum
        FROM purchase WHERE merchant_id = :h_merch AND status = 'H';
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 90); FAIL(b); }

    /* 누적 보류 사유 건수 확인 */
    EXEC SQL SELECT count(*) INTO :h_unm FROM ac_unmatch WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 90); FAIL(b); }

    /* 7) 예외/보상부 */
    if (h_aff == 0)
        userlog("%s 미반영: pid=%ld 상태=%s (보류 대상 아님)", SVC, h_pid, h_prev);
    if (h_cnt >= 10)
        userlog("%s 경보: 가맹점 %s 보류 적체 %ld 건", SVC, h_merch, h_cnt);
    if (h_unm >= 5)
        userlog("%s 경보: pid=%ld 보류 사유 %ld 건 누적", SVC, h_pid, h_unm);
    if (h_sum > 0 && h_amt > 0 && h_sum / h_amt >= 20)
        userlog("%s 경보: 보류 총액이 단건의 20배 초과 merch=%s", SVC, h_merch);

    /* 5) 이력/감사 적재부 */
    snprintf(logmsg, sizeof(logmsg), "매입보류 pid=%ld 직전=%s 반영=%ld 적체=%ld 등급=%ld",
             h_pid, h_prev, h_aff, h_cnt, grade);
    strncpy(h_logmsg, logmsg, sizeof(h_logmsg)-1); h_logmsg[sizeof(h_logmsg)-1] = 0;
    EXEC SQL SELECT nextval('cm_log_seq') INTO :h_logid;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 90); FAIL(b); }
    EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
        VALUES (:h_logid, 'ACQ_HOLD', 'I', :h_logmsg, :h_bizdate);
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 90); FAIL(b); }

    /* 9) 사후 검증부: 당일 처리 이력 자기 점검 */
    EXEC SQL SELECT count(*) INTO :h_ctx FROM cm_log
        WHERE svc_name = 'ACQ_HOLD' AND biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 90); FAIL(b); }
    if (h_ctx <= 0) {
        userlog("%s 경보: 보류 이력이 적재되지 않음", SVC);
        Bchg(b, T_MSG, 0, "이력 적재 확인 실패", 0L);
        setl(b, T_RC, 89); FAIL(b);
    }
    if (h_ctx > 1000)
        userlog("%s 경보: 당일 보류 처리 %ld 회로 과다", SVC, h_ctx);

    /* --- 업무 체인: 매입보류 -> 지급보류(PAY_HOLD) 연동 ---
     * 하위 서비스는 별도 XA 브랜치라 이 서비스가 방금 INSERT/UPDATE 한
     * 미커밋 행을 볼 수 없다. 판정에 필요한 값은 전부 UBF 전문으로 실어 보낸다.
     * 하위 실패 시 같은 글로벌 트랜잭션이므로 FAIL 로 전체 롤백한다(보상 불필요).
     * 자기 응답 계약은 이 블록 뒤 응답부에서 다시 확정한다. */
    {
        long rlen = 0, chain_rc = 0;
        char keep_status[16];

        keep_status[0] = 0;
        gets_(b, T_STATUS, keep_status, sizeof(keep_status));
        Bchg(b, T_SVCNAME, 0, "ACQ_HOLD", 0L);
        setl(b, T_PURCHASE_ID, h_pid);
        setl(b, T_AMOUNT, h_amt);
        Bchg(b, T_MERCHANT, 0, h_merch, 0L);
        Bchg(b, T_BIZDATE, 0, h_bizdate, 0L);

        /* 1단계: 보류된 매입의 정산 지급을 지급계에서 함께 잠근다 */
        Bchg(b, T_REASON, 0, h_reason, 0L);
        setl(b, T_ARG3, h_id);
        if (tpcall("PAY_HOLD", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) {
            userlog("%s: tpcall(PAY_HOLD) 실패: %s", SVC, tpstrerror(tperrno));
            Bchg(b, T_MSG, 0, "지급 보류 연동 실패", 0L); setl(b, T_RC, 201);
            FAIL(b);
        }
        chain_rc = getl(b, T_RC);
        userlog("%s 체인: PAY_HOLD 응답 rc=%ld", SVC, chain_rc);

        /* 하위가 덮어쓴 자기 상태 계약값 복원 */
        if (keep_status[0] != 0) Bchg(b, T_STATUS, 0, keep_status, 0L);
        setl(b, T_RC, 0);
    }

    /* 8) 응답부 (계약 보존: 반영 시 T_STATUS='H', T_RC 는 0/1) */
    if (h_aff > 0) Bchg(b, T_STATUS, 0, "H", 0L);
    else           Bchg(b, T_STATUS, 0, h_prev, 0L);
    Bchg(b, T_MERCHANT, 0, h_merch, 0L);
    setl(b, T_AMOUNT, h_amt);
    setl(b, T_COUNT, h_cnt);
    setl(b, T_GROSS, h_sum);
    setl(b, T_ARG1, grade);
    setl(b, T_ARG2, h_aff);
    setl(b, T_ARG3, h_unm);
    setl(b, T_ARG4, h_ctx);
    setl(b, T_ID1, h_id);
    setl(b, T_RC, h_aff > 0 ? 0 : 1);
    userlog("%s 완료: pid=%ld 반영=%ld 적체=%ld", SVC, h_pid, h_aff, h_cnt);
    OK(b);
}
