/*
 * CL_APPRFINAL.pgc - cl 모듈 서비스 CL_APPRFINAL (one service = one file).
 * 최종 마감 승인: 2차 승인(APPR:L2) 마감을 최종 승인(status=A)으로 확정.
 * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다.
 */
#include "CL_APPRFINAL.h"

void CL_APPRFINAL(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    char svc[24];
    char logmsg[192];
    char o_msg[128];
    long o_rc = 0, o_count = 0, o_amount = 0;
    char st = 0;
    char newmemo[128];
    int z_sev = 0, z_comp = 0;
    char z_msg[64];
    char z_buf[192];
    EXEC SQL BEGIN DECLARE SECTION;
    char h_bizdate[16];
    char h_period[24];
    char h_svc[24];
    char h_logmsg[192];
    long h_cid, h_cnt;
    char h_status[4], h_memo[128];
    long h_zn, h_zcnt, h_zamt;
    long h_zopen, h_zsev;
    long h_kcid;
    char h_zbuf[192];
    EXEC SQL END DECLARE SECTION;
    /* 체인 하위 호출 표식/위임 상태 (섹션4 의 행소유권 판정에 사용) */
    char ch_mark[16], ch_ustat[8];
    int  in_chain = 0;
    h_bizdate[0] = 0; h_period[0] = 0; h_zbuf[0] = 0;
    z_msg[0] = 0; z_buf[0] = 0; h_zopen = 0; h_zsev = 0;
    ch_mark[0] = 0; ch_ustat[0] = 0;


    /* --- 섹션9: 진입 로깅 --- */
    strcpy(svc, "CL_APPRFINAL");
    userlog("CL_APPRFINAL 진입: 마감처리 시작 (최종 승인(A))", svc);

    h_cid = getl(b, T_ID1);
    gets_(b, T_STR1, h_memo, sizeof(h_memo));

    /* 체인 하위 호출 판별 (T_STR8 = "CHAIN"): 상위 서비스(CL_APPRL2)가 이미 자기
     * XA 브랜치에서 cl_close_log(close_id) 행을 잠근 채 이 서비스를 부른 상태다.
     * 같은 글로벌 트랜잭션의 다른 XA 브랜치가 그 행을 다시 UPDATE 하면 서로의
     * 미커밋 행락을 기다려 트랜잭션 타임아웃까지 교착된다. 게다가 상위가 방금
     * 전이시킨 상태는 이 브랜치에서 보이지 않아 상태판정도 틀어진다.
     * 따라서 체인 하위에서는 (1) 상태판정을 전문(T_STATUS)값 기준으로 하고,
     * (2) 행 UPDATE 는 생략한 뒤 확정할 상태/메모를 전문에 실어 소유자(상위)에게
     * 위임한다. 업무 판정/집계/이력 적재는 그대로 수행한다. */
    gets_(b, T_STR8, ch_mark, sizeof(ch_mark));
    if (strcmp(ch_mark, "CHAIN") == 0) {
        in_chain = 1;
        userlog("CL_APPRFINAL 체인 하위 호출 - cl_close_log(%ld) 행 쓰기는 상위 브랜치에 위임", h_cid);
    }

    /* --- 섹션1: 입력 검증부 --- */
    if (h_cid <= 0) {
        setl(b, T_RC, 5601);
        Bchg(b, T_MSG, 0, "마감ID(T_ID1) 필수", 0L); FAIL(b);
    }

    /* --- 섹션2: 현재 상태/집계 조회부 --- */
    EXEC SQL SELECT status, txn_count, memo
        INTO :h_status, :h_cnt, :h_memo
        FROM cl_close_log WHERE close_id = :h_cid;
    if (sqlca.sqlcode == 100) {
        setl(b, T_RC, 5602);
        Bchg(b, T_MSG, 0, "마감이력 미존재", 0L); FAIL(b);
    }
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 5603); FAIL(b); }
    st = h_status[0];
    /* 체인 하위: 상위가 방금 전이시킨 상태는 미커밋이라 위 SELECT 에 안 보인다.
     * 상위가 전문에 실어 준 T_STATUS 를 유효 현재상태로 삼는다(owner-writes 규율). */
    if (in_chain) {
        char ch_cur[8]; ch_cur[0] = 0;
        gets_(b, T_STATUS, ch_cur, sizeof(ch_cur));
        if (ch_cur[0] != 0) {
            st = ch_cur[0];
            strncpy(h_status, ch_cur, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
            userlog("CL_APPRFINAL 체인 하위 - 전문 상태(%c)를 유효 현재상태로 사용", st);
        }
    }

    /* --- 섹션3: 상태전이 가능여부 분기부 --- */
    if (!(st == 'C')) {
        setl(b, T_RC, 5604);
        snprintf(o_msg, sizeof(o_msg), "현재상태(%c)에서 최종 승인(A) 불가", st);
        Bchg(b, T_MSG, 0, o_msg, 0L); FAIL(b);
    }
    if (h_cnt == 0) {
        setl(b, T_RC, 5605);
        Bchg(b, T_MSG, 0, "집계 0건 - 승인 대상 아님", 0L); FAIL(b);
    }

    /* --- 섹션4: 상태전이 본처리부 --- */
    if (h_memo[0]) snprintf(newmemo, sizeof(newmemo), "APPR:FINAL|%s", h_memo);
    else strcpy(newmemo, "APPR:FINAL");
    { char nm[128]; strncpy(nm, newmemo, sizeof(nm)-1); nm[sizeof(nm)-1]=0;
      strncpy(h_memo, nm, sizeof(h_memo)-1); h_memo[sizeof(h_memo)-1]=0; }
    strcpy(ch_ustat, "A");
    if (!in_chain) {
        /* 행 소유자 브랜치: 직접 전이한다 */
        EXEC SQL UPDATE cl_close_log
            SET status = 'A', memo = :h_memo, updated_at = now()
            WHERE close_id = :h_cid;
        if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 5606);
            Bchg(b, T_MSG, 0, "상태전이 실패", 0L); FAIL(b);
        }
        if (sqlca.sqlerrd[2] != 1) {
            setl(b, T_RC, 5607);
            Bchg(b, T_MSG, 0, "전이 대상행 수 이상", 0L); FAIL(b);
        }
    } else {
        /* 체인 하위 브랜치: 행 UPDATE 를 하지 않고 확정할 상태/메모를 상위에 위임한다.
         * 상위(행 소유자)가 tpcall 복귀 후 자기 브랜치에서 한 번만 반영한다. */
        Bchg(b, T_STR7, 0, ch_ustat, 0L);
        Bchg(b, T_STR6, 0, h_memo, 0L);
        userlog("CL_APPRFINAL 체인 하위 - 전이(status=%s memo=%s) 를 상위 소유자에 위임 close_id=%ld",
                ch_ustat, h_memo, h_cid);
    }

    /* --- 섹션6: 전이 후 상태 재확인부 --- */
    if (!in_chain) {
        EXEC SQL SELECT status INTO :h_status FROM cl_close_log WHERE close_id = :h_cid;
        if (sqlca.sqlcode < 0) { setl(b, T_RC, 5608); FAIL(b); }
    } else {
        /* 위임분은 아직 미반영이므로 확정 예정 상태로 응답 계약을 채운다 */
        strncpy(h_status, ch_ustat, sizeof(h_status)-1); h_status[sizeof(h_status)-1] = 0;
    }
    Bchg(b, T_STATUS, 0, h_status, 0L);
    o_count = h_cnt; o_rc = 0;
    snprintf(o_msg, sizeof(o_msg), "최종 승인(A) 처리완료 상태=%c", h_status[0]);

    /* --- 섹션6b: 마감 집계/요약 재산출부 --- */
    EXEC SQL SELECT count(*), coalesce(sum(txn_count),0),
                    coalesce(sum(total_amount),0)
        INTO :h_zn, :h_zcnt, :h_zamt
        FROM cl_close_log
        WHERE status <> 'X'
          AND (:h_period = '' OR period_key = :h_period);
    if (sqlca.sqlcode < 0) {
        userlog("CL_APPRFINAL 마감요약 재집계 실패[%d] - 요약 생략", sqlca.sqlcode);
        h_zn = 0; h_zcnt = 0; h_zamt = 0;
    }
    setl(b, T_ARG6, h_zcnt);
    setl(b, T_ARG7, h_zn);

    /* --- 섹션3c: 마감 규모별 추가 업무규칙 분기부 --- */
    if (h_zn == 0) {
        z_sev = 0;
        strcpy(z_msg, "마감이력 없음");
    } else if (h_zamt >= 1000000000L) {
        z_sev = 3;
        strcpy(z_msg, "대규모 마감기간");
    } else if (h_zamt >= 100000000L) {
        z_sev = 2;
        strcpy(z_msg, "중규모 마감기간");
    } else {
        z_sev = 1;
        strcpy(z_msg, "소규모 마감기간");
    }
    h_zsev = (long)z_sev;

    /* --- 섹션7: 예외/보상 판정부 (부분처리 방지) --- */
    if (o_rc != 0) {
        z_comp = 1;
        EXEC SQL SELECT count(*) INTO :h_zopen FROM cl_close_log
            WHERE status = 'O'
              AND (:h_bizdate = '' OR period_key = :h_bizdate);
        if (sqlca.sqlcode < 0) h_zopen = 0;
        if (h_zopen > 0)
            userlog("CL_APPRFINAL 경고: 미완결 개시(O) %ld건 잔존 - 보상대상", h_zopen);
        userlog("CL_APPRFINAL 예외분기 rc=%ld sev=%d - 상태전이 실패 이력 적재",
                o_rc, z_sev);
    } else {
        z_comp = 0;
        h_zopen = 0;
    }

    /* --- 섹션5b: 처리 요약 이력 적재부 --- */
    snprintf(z_buf, sizeof(z_buf), "%s sev=%d comp=%d open=%ld %s",
             svc, z_sev, z_comp, h_zopen, z_msg);
    strncpy(h_zbuf, z_buf, sizeof(h_zbuf)-1);
    h_zbuf[sizeof(h_zbuf)-1] = 0;
    strncpy(h_svc, svc, sizeof(h_svc)-1); h_svc[sizeof(h_svc)-1] = 0;
    EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
        VALUES (nextval('cm_log_seq'), :h_svc,
                CASE WHEN :h_zsev > 2 THEN 'W' ELSE 'I' END, :h_zbuf,
                COALESCE(NULLIF(:h_bizdate,'')::date, CURRENT_DATE));
    if (sqlca.sqlcode < 0)
        userlog("CL_APPRFINAL 요약이력 적재 실패[%d] - 업무는 계속", sqlca.sqlcode);
    setl(b, T_ARG8, h_zsev);

    /* --- 섹션5: 이력/감사 적재 (cm_log, best-effort) --- */
    snprintf(logmsg, sizeof(logmsg), "CL_APPRFINAL 최종 승인(A) cnt=%ld amt=%ld rc=%ld",
             o_count, o_amount, o_rc);
    strncpy(h_logmsg, logmsg, sizeof(h_logmsg)-1);
    h_logmsg[sizeof(h_logmsg)-1] = 0;
    strncpy(h_svc, svc, sizeof(h_svc)-1); h_svc[sizeof(h_svc)-1] = 0;
    EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
        VALUES (nextval('cm_log_seq'), :h_svc, 'I', :h_logmsg,
                COALESCE(NULLIF(:h_bizdate,'')::date, CURRENT_DATE));
    if (sqlca.sqlcode < 0)
        userlog("CL_APPRFINAL 감사로그 적재 실패[%d] - 업무는 계속", sqlca.sqlcode);

    /* --- 섹션8: 응답부 --- */
    setl(b, T_RC, o_rc);
    setl(b, T_COUNT, o_count);
    setl(b, T_GROSS, o_amount);
    Bchg(b, T_MSG, 0, o_msg, 0L);
    userlog("CL_APPRFINAL 완료: rc=%ld count=%ld amount=%ld", o_rc, o_count, o_amount);
    OK(b);
}

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