/*
 * CL_DAILY.pgc - cl 모듈 서비스 CL_DAILY (one service = one file).
 * 일 마감: 당일 매입 집계 후 마감이력(DAILY) 생성.
 * 이전 인라인 cl_svr.pgc 에서 분리; 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음).
 * cl_svr 가 tpsvrinit 에서 tpadvertise 로 광고한다.
 */
#include "CL_DAILY.h"

void CL_DAILY(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;
    long cid = 0;
    char bizdate[16];
    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_pend, h_unmatch, h_locked, h_prevcnt;
    long h_zn, h_zcnt, h_zamt;
    long h_zopen, h_zsev;
    long h_kcid;
    char h_zbuf[192];
    EXEC SQL END DECLARE SECTION;
    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;


    /* --- 섹션9: 진입 로깅 --- */
    strcpy(svc, "CL_DAILY");
    userlog("CL_DAILY 진입: 마감처리 시작 (일 마감)", svc);

    gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
    strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1]=0;

    /* --- 섹션1: 입력 검증부 --- */
    if (bizdate[0] == 0) {
        setl(b, T_RC, 5101);
        Bchg(b, T_MSG, 0, "영업일 미입력 - 일마감 불가", 0L); FAIL(b);
    }
    if (strlen(bizdate) < 8) {
        setl(b, T_RC, 5102);
        Bchg(b, T_MSG, 0, "영업일 형식 오류(YYYY-MM-DD)", 0L); FAIL(b);
    }

    /* --- 섹션2: 마감 전 정책 점검부(참고, 차단 아님) --- */
    EXEC SQL SELECT count(*) INTO :h_locked FROM cl_period_lock
        WHERE period_key = :h_bizdate AND locked = true;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 5103); FAIL(b); }
    if (h_locked > 0) {
        setl(b, T_RC, 5104);
        Bchg(b, T_MSG, 0, "기간 잠금 - 일마감 불가", 0L); FAIL(b);
    }
    EXEC SQL SELECT count(*) INTO :h_pend FROM purchase
        WHERE biz_date = :h_bizdate AND status = 'H';
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 5105); FAIL(b); }
    EXEC SQL SELECT count(*) INTO :h_unmatch FROM ac_unmatch WHERE biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) h_unmatch = 0;

    /* --- 섹션3: 사전상태 분기부(참고 로깅) --- */
    if (h_pend > 0)
        userlog("CL_DAILY %s 보류 %ld건 잔존 - 마감 진행", bizdate, h_pend);
    if (h_unmatch > 0)
        userlog("CL_DAILY %s 미매칭 %ld건 잔존 - 마감 진행", bizdate, h_unmatch);
    EXEC SQL SELECT count(*) INTO :h_prevcnt FROM cl_close_log
        WHERE close_type = 'DAILY' AND period_key = :h_bizdate AND status <> 'X';
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 5106); FAIL(b); }
    if (h_prevcnt > 0)
        userlog("CL_DAILY %s 기마감 %ld건 - 신규 마감이력 추가생성", bizdate, h_prevcnt);

    /* --- 섹션4: 본처리부 (일 마감 집계+이력, I/O 보존) --- */
    if (cl_open_close(b, "DAILY", bizdate, bizdate, &cid) < 0) {
        setl(b, T_RC, 5107);
        Bchg(b, T_MSG, 0, "일마감 집계/이력 생성 실패", 0L); FAIL(b);
    }
    setl(b, T_ID1, cid);
    Bchg(b, T_STATUS, 0, "C", 0L);

    /* --- 섹션6: 마감 결과 재조회부 --- */
    h_kcid = cid;
    EXEC SQL SELECT txn_count, total_amount INTO :h_prevcnt, :h_pend
        FROM cl_close_log WHERE close_id = :h_kcid;
    if (sqlca.sqlcode < 0) { setl(b, T_RC, 5108); FAIL(b); }
    o_count = h_prevcnt; o_amount = h_pend; o_rc = 0;
    strcpy(o_msg, "일마감 완료");

    /* --- 섹션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_DAILY 마감요약 재집계 실패[%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 >= 500000000L) {
        z_sev = 3;
        strcpy(z_msg, "고액 마감구간");
    } else if (h_zamt >= 50000000L) {
        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_DAILY 경고: 미완결 개시(O) %ld건 잔존 - 보상대상", h_zopen);
        userlog("CL_DAILY 예외분기 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_DAILY 요약이력 적재 실패[%d] - 업무는 계속", sqlca.sqlcode);
    setl(b, T_ARG8, h_zsev);

    /* --- 섹션5: 이력/감사 적재 (cm_log, best-effort) --- */
    snprintf(logmsg, sizeof(logmsg), "CL_DAILY 일마감 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_DAILY 감사로그 적재 실패[%d] - 업무는 계속", sqlca.sqlcode);

    /* --- 섹션8: 응답부 (T_COUNT/T_GROSS 은 cl_open_close 가 세팅) --- */
    setl(b, T_RC, o_rc);
    Bchg(b, T_MSG, 0, o_msg, 0L);
    userlog("CL_DAILY cid=%ld bizdate=%s 일마감 완료", cid, bizdate);

    /* ==== 10) 업무흐름 체인: VL_SALES 연동 (일마감 착수 전 매출 정합성 검증) ====
     * 동일 글로벌 트랜잭션이지만 VL_SALES 는 별도 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, "CL_DAILY", 0L);

        userlog("[CL_DAILY] 체인 호출 -> VL_SALES bd=%s id=%ld amt=%ld",
                cx0_bd, cx0_id1, cx0_amt);
        if (tpcall("VL_SALES", (char *)b, 0L, (char **)&b, &cx0_rlen, 0L) < 0) {
            userlog("[CL_DAILY] tpcall(VL_SALES) 실패: %s", tpstrerror(tperrno));
            setl(b, T_RC, 9681);
            Bchg(b, T_MSG, 0, "일마감 사전 매출검증 체인 실패", 0L);
            FAIL(b);
        }
        cx0_sub = getl(b, T_RC);
        if (cx0_sub != 0)
            userlog("[CL_DAILY] VL_SALES 경고 통보 rc=%ld — 상위 판단 계속", cx0_sub);
        else
            userlog("[CL_DAILY] VL_SALES 체인 정상 완료 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);
    }

    /* ==== 11) 업무흐름 체인: LG_CLOSE_LINK 연동 (일마감 확정분의 원장 마감연계) ====
     * 동일 글로벌 트랜잭션이지만 LG_CLOSE_LINK 는 별도 XA 브랜치에서 수행된다.
     * 위에서 적재/갱신한 행은 아직 미커밋이라 하위 브랜치에서 보이지 않으므로
     * 판단에 필요한 값은 전문(UBF)에 실어 전달한다(owner-writes 규율).
     * 호출 실패는 보상 없이 FAIL(b) -> 글로벌 트랜잭션 전체 롤백. */
    {
        long cx1_rlen = 0, cx1_sub = 0;
        long cx1_rc, cx1_id1, cx1_id2, cx1_amt;
        long cx1_a5, cx1_a6, cx1_a7, cx1_a8, cx1_m2, cx1_m3;
        char cx1_bd[16], cx1_mid[64], cx1_st[16], cx1_msg[192];

        /* 응답 계약 스냅샷 (하위 서비스가 덮어쓴 뒤 복원하기 위함) */
        cx1_rc  = getl(b, T_RC);   cx1_id1 = getl(b, T_ID1);  cx1_id2 = getl(b, T_ID2);
        cx1_amt = getl(b, T_AMOUNT);
        cx1_a5  = getl(b, T_ARG5); cx1_a6  = getl(b, T_ARG6);
        cx1_a7  = getl(b, T_ARG7); cx1_a8  = getl(b, T_ARG8);
        cx1_m2  = getl(b, T_AMT2); cx1_m3  = getl(b, T_AMT3);
        gets_(b, T_STATUS, cx1_st, sizeof(cx1_st));
        gets_(b, T_MSG, cx1_msg, sizeof(cx1_msg));
        gets_(b, T_BIZDATE, cx1_bd, sizeof(cx1_bd));
        gets_(b, T_MERCHANT, cx1_mid, sizeof(cx1_mid));

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

        userlog("[CL_DAILY] 체인 호출 -> LG_CLOSE_LINK bd=%s id=%ld amt=%ld",
                cx1_bd, cx1_id1, cx1_amt);
        if (tpcall("LG_CLOSE_LINK", (char *)b, 0L, (char **)&b, &cx1_rlen, 0L) < 0) {
            userlog("[CL_DAILY] tpcall(LG_CLOSE_LINK) 실패: %s", tpstrerror(tperrno));
            setl(b, T_RC, 9682);
            Bchg(b, T_MSG, 0, "일마감 원장연계 체인 실패", 0L);
            FAIL(b);
        }
        cx1_sub = getl(b, T_RC);
        if (cx1_sub != 0)
            userlog("[CL_DAILY] LG_CLOSE_LINK 경고 통보 rc=%ld — 상위 판단 계속", cx1_sub);
        else
            userlog("[CL_DAILY] LG_CLOSE_LINK 체인 정상 완료 bd=%s", cx1_bd);

        /* 응답 계약 복원: 원 서비스의 출력 필드는 하위 결과로 바뀌지 않는다 */
        setl(b, T_RC, cx1_rc);   setl(b, T_ID1, cx1_id1);  setl(b, T_ID2, cx1_id2);
        setl(b, T_AMOUNT, cx1_amt);
        setl(b, T_ARG5, cx1_a5); setl(b, T_ARG6, cx1_a6);
        setl(b, T_ARG7, cx1_a7); setl(b, T_ARG8, cx1_a8);
        setl(b, T_AMT2, cx1_m2); setl(b, T_AMT3, cx1_m3);
        if (cx1_st[0]  != 0) Bchg(b, T_STATUS, 0, cx1_st, 0L);
        if (cx1_msg[0] != 0) Bchg(b, T_MSG, 0, cx1_msg, 0L);
    }
    OK(b);
}

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