/*
 * LG_SPANTB.pgc - lg (원장/복식부기) 모듈 온라인 서비스 LG_SPANTB.
 * 기간 시산표: 계정의 기간 차변/대변 합계와 차액을 산출해 시산표에 upsert.
 * 복식부기 원칙: 차변(D)합 = 대변(C)합, 계정잔액 = 차변-대변.
 * 심화판: 입력검증→정책/상태조회→업무규칙 분기→본처리(원계약 보존)→이력/집계→응답.
 * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 tpadvertise 로 광고.
 */
#include "LG_SPANTB.h"

void LG_SPANTB(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    char acct[32], fromd[16], endd[16];
    EXEC SQL BEGIN DECLARE SECTION;
    char h_acct[32], h_from[16], h_end[16];
    long h_dr, h_cr;
    EXEC SQL END DECLARE SECTION;
    /* --- 심화 처리용 호스트변수 (원본과 이름 충돌 없음) --- */
    EXEC SQL BEGIN DECLARE SECTION;
    char  gx_bd[16], gx_mid[64], gx_svc[24], gx_lvl[4], gx_msg[192], gx_mst[24];
    long  gx_dr = 0, gx_cr = 0, gx_bal = 0, gx_cnt = 0, gx_mbps = 0, gx_lim = 0;
    long  gx_prev = 0, gx_max = 0, gx_amt = 0, gx_diff = 0, gx_in_id = 0;
    EXEC SQL END DECLARE SECTION;
    long gx_in_amt = 0, gx_delta = 0, gx_band = 0, gx_rc = 0, gx_ok = 0;
    char gx_in_bd[16], gx_in_mid[64];

    strcpy(gx_svc, "LG_SPANTB");
    strcpy(gx_lvl, "I"); strcpy(gx_mst, "");
    gx_in_amt = getl(b, T_AMOUNT);
    gx_in_id  = getl(b, T_ID1);
    gx_delta  = getl(b, T_DELTA);
    gets_(b, T_BIZDATE, gx_in_bd, sizeof(gx_in_bd));
    gets_(b, T_MERCHANT, gx_in_mid, sizeof(gx_in_mid));
    strncpy(gx_bd, gx_in_bd, sizeof(gx_bd) - 1); gx_bd[sizeof(gx_bd) - 1] = 0;
    strncpy(gx_mid, gx_in_mid, sizeof(gx_mid) - 1); gx_mid[sizeof(gx_mid) - 1] = 0;
    gx_ok = (gx_bd[0] != 0 && strlen(gx_bd) == 10) ? 1 : 0;  /* 유효 영업일에서만 부가 SQL */

    /* ============ 1) 입력 검증부 (범위/필수/형식) ============ */
    if (gx_in_amt < 0) {
        setl(b, T_RC, 9101); Bchg(b, T_MSG, 0, "계상액 음수 불가", 0L);
        userlog("LG_SPANTB 검증실패 rc=9101 amt=%ld", gx_in_amt); FAIL(b);
    }
    if (gx_delta > 1000000000000000L || gx_delta < -1000000000000000L) {
        setl(b, T_RC, 9102); Bchg(b, T_MSG, 0, "조정액 허용범위 초과", 0L);
        userlog("LG_SPANTB 검증실패 rc=9102 delta=%ld", gx_delta); FAIL(b);
    }
    if (gx_bd[0] != 0 && strlen(gx_bd) != 10) {
        setl(b, T_RC, 9103); Bchg(b, T_MSG, 0, "영업일 형식 오류(YYYY-MM-DD)", 0L);
        userlog("LG_SPANTB 검증실패 rc=9103 bd=%s", gx_bd); FAIL(b);
    }

    /* ============ 2) 마스터/정책/상태 조회부 (advisory, 유효일에서만) ============ */
    if (gx_ok) {
        EXEC SQL SELECT coalesce(sum(CASE WHEN dc='D' THEN amount ELSE 0 END),0),
                        coalesce(sum(CASE WHEN dc='C' THEN amount ELSE 0 END),0),
                        count(*)
            INTO :gx_dr, :gx_cr, :gx_cnt
            FROM lg_ledger_line WHERE biz_date = :gx_bd;
        if (sqlca.sqlcode == 100) { gx_dr = 0; gx_cr = 0; gx_cnt = 0; }
        else if (sqlca.sqlcode < 0) userlog("LG_SPANTB 라인집계 조회 경고 [%d]", sqlca.sqlcode);
        EXEC SQL SELECT coalesce(debit_total,0), coalesce(credit_total,0), coalesce(balance,0)
            INTO :gx_prev, :gx_amt, :gx_bal FROM lg_account_bal
            WHERE acct = 'RECV' AND biz_date = :gx_bd;
        if (sqlca.sqlcode == 100) { gx_prev = 0; gx_amt = 0; gx_bal = 0; }
        EXEC SQL SELECT coalesce(count(DISTINCT acct),0) INTO :gx_max
            FROM lg_ledger_line WHERE biz_date = :gx_bd;
        if (sqlca.sqlcode == 100) gx_max = 0;
    }

    /* ============ 3) 업무규칙 분기부 (금액구간/상태/한도) ============ */
    gx_amt = (gx_in_amt != 0) ? gx_in_amt : (gx_delta >= 0 ? gx_delta : -gx_delta);
    if (gx_amt >= 100000000L) {
        gx_band = 3;   /* 1억원 이상 - 고액 전기(RETAINED/SUSP) */
        userlog("LG_SPANTB 고액구간 amt=%ld", gx_amt);
    } else if (gx_amt >= 10000000L) {
        gx_band = 2;   /* 1천만~1억 - 중액 전기 */
    } else if (gx_amt > 0) {
        gx_band = 1;   /* 소액 전기 */
    } else {
        gx_band = 0;   /* 금액 미기재 - 조회/집계형 처리 */
    }
    gx_diff = gx_dr - gx_cr;
    if (gx_diff != 0) {
        userlog("LG_SPANTB 당일 대차 불균형 diff=%ld", gx_diff);
    } else if (gx_max == 0) {
        gx_rc = 3;   /* 무거래일 */
    }

    /* ============ 4) 본처리부 (원 서비스 계약 보존 - I/O·체인 불변) ============ */
    userlog("LG_SPANTB 본처리 진입 bd=%s id=%ld amt=%ld", gx_bd, gx_in_id, gx_in_amt);
    gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV");
    gets_(b, T_BIZDATE, endd, sizeof(endd));
    gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd);
    strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0;
    strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0;
    strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0;
    EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0),
                    coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0)
        INTO :h_dr, :h_cr FROM lg_ledger_line
        WHERE acct = :h_acct AND biz_date BETWEEN :h_from AND :h_end;
    if (sqlca.sqlcode < 0) FAIL(b);
    EXEC SQL INSERT INTO lg_trial_bal (acct, biz_date, debit_total, credit_total, diff)
        VALUES (:h_acct, :h_end, :h_dr, :h_cr, :h_dr - :h_cr)
        ON CONFLICT (acct, biz_date) DO UPDATE
        SET debit_total = EXCLUDED.debit_total, credit_total = EXCLUDED.credit_total,
            diff = EXCLUDED.diff, updated_at = now();
    if (sqlca.sqlcode < 0) FAIL(b);
    setl(b, T_AMT2, h_dr); setl(b, T_AMT3, h_cr); setl(b, T_DELTA, h_dr - h_cr);
    userlog("LG_SPANTB %s %s~%s 차 %ld 대 %ld", acct, fromd, endd, h_dr, h_cr);

    /* ============ 5) 이력/감사 적재부 (cm_log, advisory) ============ */
    if (gx_ok) {
        snprintf(gx_msg, sizeof(gx_msg), "LG_SPANTB band=%ld amt=%ld dr=%ld cr=%ld cnt=%ld rc=%ld",
                 gx_band, gx_amt, gx_dr, gx_cr, gx_cnt, gx_rc);
        strcpy(gx_lvl, (gx_dr != gx_cr) ? "W" : "I");
        EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
            VALUES (nextval('cm_log_seq'), :gx_svc, :gx_lvl, :gx_msg, :gx_bd);
        if (sqlca.sqlcode < 0) userlog("LG_SPANTB 이력적재 경고 [%d]", sqlca.sqlcode);
    }

    /* ============ 6) 집계/요약 재검증부 (advisory) ============ */
    if (gx_ok) {
        EXEC SQL SELECT coalesce(sum(debit_total),0), coalesce(sum(credit_total),0)
            INTO :gx_dr, :gx_cr FROM lg_account_bal WHERE biz_date = :gx_bd;
        if (sqlca.sqlcode == 100) { gx_dr = 0; gx_cr = 0; }
        gx_diff = gx_dr - gx_cr;
    }

    /* ==== 8) 응답부 (부가 산출필드 - 원 출력 불변, 스크래치만 추가) ==== */
    setl(b, T_ARG7, gx_dr);
    setl(b, T_ARG8, gx_cr);
    setl(b, T_ARG5, gx_cnt);
    setl(b, T_ARG6, gx_diff);
    /* ==== 9) 로깅(완료 요약) ==== */
    userlog("LG_SPANTB 완료 bd=%s 차=%ld 대=%ld cnt=%ld rc=%ld", gx_bd, gx_dr, gx_cr, gx_cnt, gx_rc);

    /* ==== 10) 업무흐름 체인: VL_LEDGER 연동 (구간 시산표 산출 전 원장 정합성 검증) ====
     * 동일 글로벌 트랜잭션이지만 VL_LEDGER 는 별도 XA 브랜치에서 수행된다.
     * 위에서 적재/갱신한 행은 아직 미커밋이라 하위 브랜치에서 보이지 않으므로
     * 판단에 필요한 값은 전문(UBF)에 실어 전달한다(owner-writes 규율).
     * 호출 실패는 보상 없이 FAIL(b) -> 글로벌 트랜잭션 전체 롤백. */
    {
        long cx0_rlen = 0, cx0_sub = 0;
        long cx0_rc, cx0_id1, cx0_id2, cx0_amt;
        long cx0_a5, cx0_a6, cx0_a7, cx0_a8, cx0_m2, cx0_m3;
        char cx0_bd[16], cx0_mid[64], cx0_st[16], cx0_msg[192];

        /* 응답 계약 스냅샷 (하위 서비스가 덮어쓴 뒤 복원하기 위함) */
        cx0_rc  = getl(b, T_RC);   cx0_id1 = getl(b, T_ID1);  cx0_id2 = getl(b, T_ID2);
        cx0_amt = getl(b, T_AMOUNT);
        cx0_a5  = getl(b, T_ARG5); cx0_a6  = getl(b, T_ARG6);
        cx0_a7  = getl(b, T_ARG7); cx0_a8  = getl(b, T_ARG8);
        cx0_m2  = getl(b, T_AMT2); cx0_m3  = getl(b, T_AMT3);
        gets_(b, T_STATUS, cx0_st, sizeof(cx0_st));
        gets_(b, T_MSG, cx0_msg, sizeof(cx0_msg));
        gets_(b, T_BIZDATE, cx0_bd, sizeof(cx0_bd));
        gets_(b, T_MERCHANT, cx0_mid, sizeof(cx0_mid));

        /* 하위 서비스가 쓸 값을 전문에 적재 (미커밋 행 조회 의존 금지) */
        setl(b, T_ID1, cx0_id1);
        setl(b, T_AMOUNT, cx0_amt);
        if (cx0_bd[0] != 0)  Bchg(b, T_BIZDATE, 0, cx0_bd, 0L);
        if (cx0_mid[0] != 0) Bchg(b, T_MERCHANT, 0, cx0_mid, 0L);
        Bchg(b, T_SVCNAME, 0, "LG_SPANTB", 0L);

        userlog("[LG_SPANTB] 체인 호출 -> VL_LEDGER bd=%s id=%ld amt=%ld",
                cx0_bd, cx0_id1, cx0_amt);
        if (tpcall("VL_LEDGER", (char *)b, 0L, (char **)&b, &cx0_rlen, 0L) < 0) {
            userlog("[LG_SPANTB] tpcall(VL_LEDGER) 실패: %s", tpstrerror(tperrno));
            setl(b, T_RC, 9436);
            Bchg(b, T_MSG, 0, "구간시산 원장검증 체인 실패", 0L);
            FAIL(b);
        }
        cx0_sub = getl(b, T_RC);
        if (cx0_sub != 0)
            userlog("[LG_SPANTB] VL_LEDGER 경고 통보 rc=%ld — 상위 판단 계속", cx0_sub);
        else
            userlog("[LG_SPANTB] VL_LEDGER 체인 정상 완료 bd=%s", cx0_bd);

        /* 응답 계약 복원: 원 서비스의 출력 필드는 하위 결과로 바뀌지 않는다 */
        setl(b, T_RC, cx0_rc);   setl(b, T_ID1, cx0_id1);  setl(b, T_ID2, cx0_id2);
        setl(b, T_AMOUNT, cx0_amt);
        setl(b, T_ARG5, cx0_a5); setl(b, T_ARG6, cx0_a6);
        setl(b, T_ARG7, cx0_a7); setl(b, T_ARG8, cx0_a8);
        setl(b, T_AMT2, cx0_m2); setl(b, T_AMT3, cx0_m3);
        if (cx0_st[0]  != 0) Bchg(b, T_STATUS, 0, cx0_st, 0L);
        if (cx0_msg[0] != 0) Bchg(b, T_MSG, 0, cx0_msg, 0L);
    }
    OK(b);
}

/* vim: set ts=4 sw=4 et smartindent: */
