/*==========================================================================*/
/*  CL_DAILY.pc  -  일마감 배치 (As-Is 원본, Oracle Pro*C)                    */
/*--------------------------------------------------------------------------*/
/*  대상 : BC 매입시스템 / 일일 마감 배치 CLDAILY (야간 JOB)                  */
/*  DBMS : Oracle 19c   전처리 : Pro*C/C++ (proc)                            */
/*  실행 : cldaily <YYYYMMDD>   (인자 생략시 전영업일 = SYSDATE-1)            */
/*  ※ 이 파일은 **As-Is 역사 원본**이다. 현행 오픈스택 이식본은               */
/*     app/src/cl/svc/CL_DAILY.pgc (ECPG/PostgreSQL, Tuxedo 서비스화).        */
/*     원본은 순수 배치(main+커서루프) 였다. 컴파일 대상 아님(빌드 제외).     */
/*--------------------------------------------------------------------------*/
/*  변경이력                                                                  */
/*   1999.02.15  KIM K.H   최초작성 (가맹점별 당일 매입 집계/마감이력)         */
/*   2003.10.07  PARK J.S  기간잠금(cl_period_lock) 선점검 추가                */
/*   2009.05.22  LEE S.W   보류(H)/미매칭 잔존시 경고후 마감강행 정책 반영      */
/*   2015.08.11  JUNG M.K  마감 규모별 심각도(sev) 분류, cm_log 요약적재        */
/*   2019.09.01  CHOI H.W  Oracle 11g -> 19c, CHAR->VARCHAR2 정비             */
/*==========================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

EXEC SQL INCLUDE sqlca;
EXEC SQL INCLUDE oraca;
EXEC ORACLE OPTION (ORACA=YES);

EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR   h_user[32];
    VARCHAR   h_pass[32];
    VARCHAR   h_dbstr[32];
    VARCHAR   h_bizdate[9];       /* YYYYMMDD */
    VARCHAR   h_merch[16];
    VARCHAR   h_mname[64];
    VARCHAR   h_status[2];
    VARCHAR   h_zmsg[64];
    long      h_locked;
    long      h_pend;
    long      h_unmatch;
    long      h_prevcnt;
    long      h_cid;
    long      h_txncnt;
    long      h_gross;
    long      h_fee;
    long      h_net;
    long      h_totcnt;
    long      h_totamt;
    long      h_nmerch;
    long      h_zsev;
EXEC SQL END DECLARE SECTION;

/* 가맹점별 당일 확정매입 커서 (취소 C 제외) — 마감 대상 스윕 */
EXEC SQL DECLARE cur_merch CURSOR FOR
    SELECT m.merchant_id,
           NVL(m.name,'(무명)'),
           NVL(m.status,'N'),
           COUNT(p.purchase_id),
           NVL(SUM(p.amount), 0),
           NVL(SUM(p.fee),    0),
           NVL(SUM(p.net),    0)
      FROM merchant m, purchase p
     WHERE p.merchant_id (+) = m.merchant_id
       AND p.biz_date         = TO_DATE(:h_bizdate,'YYYYMMDD')
       AND NVL(p.status,'A') <> 'C'
     GROUP BY m.merchant_id, m.name, m.status
    HAVING COUNT(p.purchase_id) > 0
     ORDER BY m.merchant_id;

static void sql_error(const char *stage);

int main(int argc, char **argv)
{
    int done = 0;

    /* ---- 0) 접속 (배치는 자기 세션으로 CONNECT) ---------------------- */
    strcpy((char *)h_user.arr,  "clbatch");   h_user.len  = strlen((char *)h_user.arr);
    strcpy((char *)h_pass.arr,  "clbatch");   h_pass.len  = strlen((char *)h_pass.arr);
    strcpy((char *)h_dbstr.arr, "ACQPRD");    h_dbstr.len = strlen((char *)h_dbstr.arr);

    EXEC SQL WHENEVER SQLERROR GOTO connect_err;
    EXEC SQL CONNECT :h_user IDENTIFIED BY :h_pass USING :h_dbstr;

    /* 이후 오류는 표준 라벨분기 */
    EXEC SQL WHENEVER SQLERROR GOTO sqlfail;

    /* ---- 1) 영업일 결정 (인자 없으면 전영업일 SYSDATE-1) ------------- */
    if (argc >= 2) {
        strncpy((char *)h_bizdate.arr, argv[1], 8);
        h_bizdate.arr[8] = '\0';
        h_bizdate.len = strlen((char *)h_bizdate.arr);
    } else {
        EXEC SQL SELECT TO_CHAR(SYSDATE-1,'YYYYMMDD') INTO :h_bizdate FROM DUAL;
    }
    printf("[CL_DAILY] 일마감 개시 bizdate=%.8s\n", h_bizdate.arr);

    /* ---- 2) 기간 잠금 선점검 (잠기면 즉시 중단) --------------------- */
    EXEC SQL SELECT COUNT(*) INTO :h_locked
               FROM cl_period_lock
              WHERE period_key = :h_bizdate
                AND locked     = 'Y';
    if (h_locked > 0) {
        fprintf(stderr, "[CL_DAILY] 기간잠금 상태 - 일마감 불가 bizdate=%.8s\n",
                h_bizdate.arr);
        EXEC SQL ROLLBACK WORK RELEASE;
        return 4;
    }

    /* ---- 3) 사전상태 점검 (보류/미매칭/기마감) — 경고후 강행 -------- */
    EXEC SQL SELECT COUNT(*) INTO :h_pend
               FROM purchase
              WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD')
                AND status   = 'H';
    EXEC SQL SELECT NVL(COUNT(*),0) INTO :h_unmatch
               FROM ac_unmatch
              WHERE biz_date = TO_DATE(:h_bizdate,'YYYYMMDD');
    EXEC SQL SELECT COUNT(*) INTO :h_prevcnt
               FROM cl_close_log
              WHERE close_type = 'DAILY'
                AND period_key  = :h_bizdate
                AND status     <> 'X';
    if (h_pend > 0)
        printf("[CL_DAILY] 경고: 보류(H) %ld건 잔존 - 마감 진행\n", h_pend);
    if (h_unmatch > 0)
        printf("[CL_DAILY] 경고: 미매칭 %ld건 잔존 - 마감 진행\n", h_unmatch);
    if (h_prevcnt > 0)
        printf("[CL_DAILY] 기마감 %ld건 존재 - 신규 마감이력 추가생성\n", h_prevcnt);

    /* ---- 4) 마감헤더 채번 (시퀀스 .NEXTVAL) ------------------------ */
    EXEC SQL SELECT cl_close_seq.NEXTVAL INTO :h_cid FROM DUAL;
    EXEC SQL
        INSERT INTO cl_close_log
               (close_id, close_type, period_key, status,
                txn_count, total_amount, opened_at)
        VALUES (:h_cid, 'DAILY', :h_bizdate, 'O',
                0, 0, SYSDATE);

    /* ---- 5) 본처리: 가맹점별 집계 커서 루프 ------------------------ */
    h_totcnt = 0; h_totamt = 0; h_nmerch = 0;
    EXEC SQL OPEN cur_merch;
    for (;;) {
        EXEC SQL WHENEVER NOTFOUND DO done = 1;
        EXEC SQL FETCH cur_merch
                  INTO :h_merch, :h_mname, :h_status,
                       :h_txncnt, :h_gross, :h_fee, :h_net;
        if (done) break;

        /* 비활성 가맹점은 집계만 하고 정산연계는 보류(로그) */
        if (strncmp((char *)h_status.arr, "A", 1) != 0)
            printf("[CL_DAILY] 주의: 비활성(%.1s) 가맹점 %.*s 마감집계 포함\n",
                   h_status.arr, h_merch.len, h_merch.arr);

        EXEC SQL
            INSERT INTO cl_close_detail
                   (close_id, merchant_id, txn_count,
                    gross_amount, fee_amount, net_amount, created_at)
            VALUES (:h_cid, :h_merch, :h_txncnt,
                    :h_gross, :h_fee, :h_net, SYSDATE);

        /* 마감된 매입행을 'S'(마감완료)로 상태전이 */
        EXEC SQL
            UPDATE purchase
               SET status     = DECODE(status, 'A', 'S', status),
                   closed_at  = SYSDATE,
                   close_id   = :h_cid
             WHERE merchant_id = :h_merch
               AND biz_date    = TO_DATE(:h_bizdate,'YYYYMMDD')
               AND status      = 'A';

        h_totcnt += h_txncnt;
        h_totamt += h_gross;
        h_nmerch += 1;
        if (h_nmerch % 500 == 0) {
            printf("[CL_DAILY] 진행 %ld개 가맹점 누적 %ld건 %ld원 - 중간커밋\n",
                   h_nmerch, h_totcnt, h_totamt);
            EXEC SQL COMMIT WORK;
        }
    }
    EXEC SQL CLOSE cur_merch;

    /* ---- 6) 마감헤더 확정 (집계 반영, 상태 O->C) ------------------- */
    EXEC SQL
        UPDATE cl_close_log
           SET txn_count    = :h_totcnt,
               total_amount = :h_totamt,
               status       = 'C',
               closed_at    = SYSDATE
         WHERE close_id     = :h_cid;

    /* ---- 7) 마감 규모별 심각도 분류 (DECODE 대체 if-ladder) -------- */
    if (h_totcnt == 0)              { h_zsev = 0; strcpy((char *)h_zmsg.arr, "마감대상 없음"); }
    else if (h_totamt >= 500000000L){ h_zsev = 3; strcpy((char *)h_zmsg.arr, "고액 마감구간"); }
    else if (h_totamt >= 50000000L) { h_zsev = 2; strcpy((char *)h_zmsg.arr, "중액 마감구간"); }
    else                            { h_zsev = 1; strcpy((char *)h_zmsg.arr, "소액 마감구간"); }
    h_zmsg.len = strlen((char *)h_zmsg.arr);

    EXEC SQL
        INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
        VALUES (cm_log_seq.NEXTVAL, 'CL_DAILY',
                DECODE(SIGN(:h_zsev - 2), 1, 'W', 'I'),
                'CL_DAILY 일마감 sev='||:h_zsev||' '||:h_zmsg
                    ||' cnt='||:h_totcnt||' amt='||:h_totamt,
                TO_DATE(:h_bizdate,'YYYYMMDD'));

    /* ---- 8) 최종 커밋 --------------------------------------------- */
    EXEC SQL COMMIT WORK;

    printf("[CL_DAILY] 일마감 완료 cid=%ld 가맹점=%ld 건수=%ld 금액=%ld sev=%ld(%s)\n",
           h_cid, h_nmerch, h_totcnt, h_totamt, h_zsev, h_zmsg.arr);
    EXEC SQL COMMIT WORK RELEASE;
    return 0;

connect_err:
    fprintf(stderr, "[CL_DAILY] DB 접속 실패: %.70s\n", sqlca.sqlerrm.sqlerrmc);
    return 8;

sqlfail:
    sql_error("일마감 본처리");
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK RELEASE;
    return 12;
}

/*--------------------------------------------------------------------------*/
/*  sql_error  -  오류 로깅 (sqlca/oraca)                                     */
/*--------------------------------------------------------------------------*/
static void sql_error(const char *stage)
{
    fprintf(stderr, "[CL_DAILY] SQL 오류 (%s): %.70s\n",
            stage, sqlca.sqlerrm.sqlerrmc);
    fprintf(stderr, "           sqlcode=%ld rows=%ld stmt=%.60s\n",
            sqlca.sqlcode, sqlca.sqlerrd[2], oraca.orastxt.orastxtc);
}
