/*
 * ACQ_DATECHG.pgc - ac 모듈 서비스 ACQ_DATECHG (one service = one file).
 * 영업일 변경: 매입의 귀속 영업일을 요청일로 옮긴다. 정산이 끝난 건은
 * 이동할 수 없으며, 이동 전후 일자를 모두 로그에 남긴다.
 * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
 */
#include "ACQ_DATECHG.h"

void ACQ_DATECHG(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *SVC = "ACQ_DATECHG";
    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) 입력 검증부: 전 필드 필수/범위/형식 점검 후 위반 시 거절 */
    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_STR1, bizdate, sizeof(bizdate));
    strncpy(h_d1, bizdate, sizeof(h_d1)-1); h_d1[sizeof(h_d1)-1] = 0;
    if (h_d1[0] == 0) { Bchg(b, T_MSG, 0, "변경 대상 영업일 누락", 0L); setl(b, T_RC, 107); FAIL(b); }
    bizdate[0] = 0;
    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, merchant_id, amount, fee, net INTO :h_prev, :h_merch, :h_gross, :h_fee, :h_net FROM purchase WHERE purchase_id = :h_pid;
    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, 111); FAIL(b); }
    userlog("%s 원매입: pid=%ld status=%s amount=%ld", SVC, h_pid, h_prev, h_gross);
    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_prev, "A") == 0 || strcmp(h_prev, "M") == 0 || strcmp(h_prev, "H") == 0)) {
        userlog("%s 거절: 상태 %s 에서 전이 불가", SVC, h_prev);
        Bchg(b, T_MSG, 0, "상태 전이 불가", 0L); setl(b, T_RC, 120); FAIL(b);
    }
    /* 이동 전 원 영업일을 확보하고 이동 폭을 판정 */
    EXEC SQL SELECT biz_date::text INTO :h_d2 FROM purchase WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 원영업일 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    if (strcmp(h_d1, h_d2) == 0) { Bchg(b, T_MSG, 0, "동일 영업일", 0L); setl(b, T_RC, 137); FAIL(b); }
    else if (strcmp(h_d1, h_d2) < 0) { h_val = 3; Bchg(b, T_MSG, 0, "과거일 소급 이동", 0L); }
    else { h_val = 1; Bchg(b, T_MSG, 0, "미래일 이월", 0L); }

    /* 4) 본처리부: 부수행 기록 및 상태 갱신 */
    EXEC SQL UPDATE purchase SET biz_date = :h_d1::date, updated_at = now() WHERE purchase_id = :h_pid;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 영업일변경 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    h_rem = sqlca.sqlerrd[2];
    h_aff = 1;

    /* 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), "영업일변경 pid=%ld %s -> %s 등급=%ld", h_pid, h_d2, h_d1, 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_DATECHG', '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_DATECHG' 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);

    /* 8) 응답부 */
    Bchg(b, T_STR1, 0, h_d1, 0L);
    Bchg(b, T_STR2, 0, h_d2, 0L);
    setl(b, T_ARG1, h_rem);
    setl(b, T_ARG2, h_val);
    userlog("%s 완료: pid=%ld 반영=%ld", SVC, h_pid, h_aff);
    setl(b, T_RC, 0);
    OK(b);
}
