/*
 * ACQ_PROMO.pgc - ac 모듈 서비스 ACQ_PROMO (one service = one file).
 * 프로모션 매입 등록: 행사 코드가 유효하면 수수료 0 의 프로모션 매입을
 * 생성한다(분류 PROMO). 정산금 전액 지급, 원장에 행사 분개를 남긴다.
 * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
 */
#include "ACQ_PROMO.h"

void ACQ_PROMO(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *SVC = "ACQ_PROMO";
    char merch[64], bizdate[16], ccy[8], issuer[32], channel[16], logmsg[256];
    char reason[128], docno[64];
    long threshold = 0, ratio = 0, k = 0;
    char category[24];
    long i = 0, bps = 25, computed = 0, prior = 0;
    EXEC SQL BEGIN DECLARE SECTION;
    long h_cnt = 0, h_gross = 0, h_net = 0, h_fee = 0, h_sum = 0, h_avg = 0, h_max = 0;
    long h_a = 0, h_b = 0, h_c = 0, h_d = 0, h_e = 0, h_top = 0, h_val = 0, h_val2 = 0;
    long h_lim = 0, h_rate = 0, h_pid = 0, h_amt = 0, h_logid = 0, h_n = 0, h_id = 0;
    long h_openc = 0, h_setc = 0, h_pdgross = 0, h_ctx = 0, h_share = 0, h_min = 0;
    long h_supply = 0, h_vat = 0, h_months = 0, h_delta = 0, h_tax = 0;
    char h_merch[64], h_bizdate[16], h_ccy[8], h_issuer[32], h_status[8], h_chan[16];
    char h_reason[128], h_str1[64], h_str2[40], h_logmsg[256], h_d1[16], h_d2[16];
    long h_krw = 0, h_fx = 0, h_comp = 0, h_rate2 = 0, h_sfee = 0, h_snet = 0;
    long h_seq = 0, h_mamt = 0, h_new = 0, h_aff = 0, h_prev2 = 0;
    long h_canid = 0, h_adjid = 0, h_invid = 0, h_ledid = 0, h_setid = 0, h_rem = 0;
    char h_prev[8], h_ctype[16], h_category[24], h_docno[64];
    EXEC SQL END DECLARE SECTION;

    /* 작업영역 초기화: 문자열 호스트변수/로컬버퍼를 모두 널로 시작한다 */
    memset(h_merch, 0, sizeof(h_merch));   memset(h_bizdate, 0, sizeof(h_bizdate));
    memset(h_ccy, 0, sizeof(h_ccy));       memset(h_issuer, 0, sizeof(h_issuer));
    memset(h_status, 0, sizeof(h_status)); memset(h_chan, 0, sizeof(h_chan));
    memset(h_reason, 0, sizeof(h_reason)); memset(h_str1, 0, sizeof(h_str1));
    memset(h_str2, 0, sizeof(h_str2));     memset(h_logmsg, 0, sizeof(h_logmsg));
    memset(h_d1, 0, sizeof(h_d1));         memset(h_d2, 0, sizeof(h_d2));
    memset(merch, 0, sizeof(merch));       memset(bizdate, 0, sizeof(bizdate));
    memset(ccy, 0, sizeof(ccy));           memset(issuer, 0, sizeof(issuer));
    memset(channel, 0, sizeof(channel));   memset(reason, 0, sizeof(reason));
    memset(docno, 0, sizeof(docno));       memset(logmsg, 0, sizeof(logmsg));
    memset(h_prev, 0, sizeof(h_prev));     memset(h_ctype, 0, sizeof(h_ctype));
    memset(h_category, 0, sizeof(h_category)); memset(h_docno, 0, sizeof(h_docno));
    memset(category, 0, sizeof(category));

    userlog("%s 진입: 신규 매입 접수 요청", SVC);

    /* 1) 입력 검증부: 전 필드 필수/범위/형식 점검 후 위반 시 거절 */
    gets_(b, T_MERCHANT, merch, sizeof(merch));
    if (merch[0] == 0) { Bchg(b, T_MSG, 0, "가맹점 ID 누락", 0L); setl(b, T_RC, 101); FAIL(b); }
    strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
    h_amt = getl(b, T_AMOUNT);
    if (h_amt < 1L) { Bchg(b, T_MSG, 0, "금액 범위 오류", 0L); setl(b, T_RC, 104); FAIL(b); }
    gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
    strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
    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");
    }
    if (strlen(h_bizdate) != 10) { Bchg(b, T_MSG, 0, "영업일 형식 오류", 0L); setl(b, T_RC, 105); FAIL(b); }
    userlog("%s 컨텍스트: pid=%ld merch=%s amt=%ld bizdate=%s", SVC, h_pid, h_merch, h_amt, h_bizdate);
    /* 요청 파라미터 감사 덤프 (운영 추적용) */
    userlog("%s 요청감사: 갱신 요청 수신", SVC);
    userlog("%s 요청감사: merch=%s pid=%ld amt=%ld biz=%s", SVC, h_merch, h_pid, h_amt, h_bizdate);

    /* 2) 마스터/정책/상태 조회부 */
    EXEC SQL SELECT status, daily_limit INTO :h_status, :h_lim FROM merchant WHERE merchant_id = :h_merch;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 가맹점마스터 SQL 실패 [%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, 110); FAIL(b); }
    if (acdb_merchant_mdr(h_merch, &h_rate) < 0) h_rate = 25;

    /* 3) 업무규칙 분기부: 가맹점 상태/금액 구간/채널 정책(3+ 분기) */
    if (strcmp(h_status, "ACTIVE") != 0) { Bchg(b, T_MSG, 0, "비활성 가맹점", 0L); setl(b, T_RC, 121); FAIL(b); }
    /* 행사 매입은 수수료를 전액 면제하되 금액 상한을 둔다 */
    if (h_amt > 3000000) { Bchg(b, T_MSG, 0, "행사 매입 한도 초과", 0L); setl(b, T_RC, 162); FAIL(b); }
    if (h_amt >= 1000000) { h_val = 3; }
    else if (h_amt >= 100000) { h_val = 2; }
    else { h_val = 1; }

    /* 4) 본처리부: 수수료 산출 및 매입/승인 원장 생성 */
    bps = h_rate; h_fee = acq_fee(h_amt, bps); h_net = h_amt - h_fee;
    /* 수수료 면제: 원 MDR 수수료를 감면액으로 보존해 응답 */
    h_delta = h_fee;
    h_fee = 0;
    h_net = h_amt;
    EXEC SQL SELECT nextval('ledger_seq') INTO :h_ledid;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 원장채번 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    h_new = acdb_next_purchase_id();
    if (h_new < 0) { Bchg(b, T_MSG, 0, "매입 채번 실패", 0L); setl(b, T_RC, 93); FAIL(b); }
    if (acdb_insert_purchase(h_new, h_merch, h_amt, h_fee, h_net, "A", "POS", h_bizdate) < 0)
        { Bchg(b, T_MSG, 0, "매입 생성 실패", 0L); setl(b, T_RC, 94); FAIL(b); }
    if (acdb_insert_approval(h_new, 1) < 0) { Bchg(b, T_MSG, 0, "승인 적재 실패", 0L); setl(b, T_RC, 95); FAIL(b); }
    h_aff = 1; h_pid = h_new;
    EXEC SQL UPDATE purchase SET category = 'PROMO' WHERE purchase_id = :h_new;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 분류표시 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }

    /* 6) 집계/요약 갱신부 */
    EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0), coalesce(sum(net),0) INTO :h_cnt, :h_sum, :h_sfee, :h_snet FROM purchase WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 재집계 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    /* 재집계 결과(총액/수수료/순액/건수)를 가맹점 일별 요약에 반영 */
    if (acdb_upsert_merch_sum(h_merch, h_bizdate, h_sum, h_sfee, h_snet, h_cnt) < 0)
        userlog("%s 주의: 요약 갱신 스킵", SVC);
    userlog("%s 요약갱신: merch=%s 건수=%ld 총액=%ld 수수료=%ld", SVC, h_merch, h_cnt, h_sum, h_sfee);

    /* 7) 예외/보상부: 반영 결과 정합 확인 */
    if (h_aff < 0) { Bchg(b, T_MSG, 0, "반영 실패", 0L); setl(b, T_RC, 92); FAIL(b); }

    /* 5) 이력/감사 적재부: 처리 요약을 공통 로그(cm_log)에 남긴다 */
    snprintf(logmsg, sizeof(logmsg), "프로모션매입 merch=%s 금액=%ld 감면=%ld 등급=%ld", h_merch, h_amt, h_delta, h_val);
    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) {
        userlog("%s: 로그채번 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        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_PROMO', 'I', :h_logmsg, :h_bizdate);
    if (sqlca.sqlcode < 0) {
        userlog("%s: 로그적재 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }

    /* 9) 사후 검증부: 당일 처리 이력 자기 점검 및 과다 호출 감시 */
    EXEC SQL SELECT count(*) INTO :h_ctx FROM cm_log
        WHERE svc_name = 'ACQ_PROMO' AND biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 사후검증 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        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);
    setl(b, T_ARG6, h_ctx);

    /* --- 업무 체인: 프로모션 적용 -> 정산 프로모 반영(ST_PROMOAPPLY) ---
     * 하위 서비스는 별도 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_PROMO", 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단계: 프로모션 우대요율을 정산 수수료에 반영한다 */
        setl(b, T_AMT1, h_gross);
        Bchg(b, T_KEY1, 0, h_str1, 0L);
        if (tpcall("ST_PROMOAPPLY", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) {
            userlog("%s: tpcall(ST_PROMOAPPLY) 실패: %s", SVC, tpstrerror(tperrno));
            Bchg(b, T_MSG, 0, "프로모션 정산 반영 실패", 0L); setl(b, T_RC, 311);
            FAIL(b);
        }
        chain_rc = getl(b, T_RC);
        userlog("%s 체인: ST_PROMOAPPLY 응답 rc=%ld", SVC, chain_rc);

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

    /* 8) 응답부 */
    setl(b, T_PURCHASE_ID, h_new);
    setl(b, T_FEE, h_fee);
    setl(b, T_NET, h_net);
    setl(b, T_DELTA, h_delta);
    setl(b, T_ARG1, h_val);
    Bchg(b, T_CATEGORY, 0, "PROMO", 0L);
    userlog("%s 완료: pid=%ld 반영=%ld", SVC, h_pid, h_aff);
    setl(b, T_RC, 0);
    OK(b);
}
