/*
 * MG_CHANLIST.pgc - mg 모듈 서비스 MG_CHANLIST (one service = one file).
 * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.
 */
#include "MG_CHANLIST.h"

/* MG_CHANLIST - 채널 상태 요약: 전체 채널 중 UP/DOWN 개수를 집계하고, UP 채널ID 를
 *   콤마로 이어 목록으로 회신. UP 이 하나도 없으면 rc=1(전면 다운) 경고. */
void MG_CHANLIST(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;

    /* ===================================================================
     * 심화 처리부 (전문게이트웨이 공통 온라인 로직) - MG_CHANLIST
     * 입력검증 / 마스터·상태조회 / 업무규칙 / 본처리 / 이력 / 재집계 /
     * 예외보상 / 응답보조 / 로깅
     * =================================================================== */
    {
        EXEC SQL BEGIN DECLARE SECTION;
        char gw_chan[16], gw_bizdate[16], gw_mti[8], gw_merch[16];
        long gw_stan, gw_last, gw_total, gw_err, gw_backlog;
        long gw_chanlast;
        long gw_limit, gw_mdr, gw_daysum;
        EXEC SQL END DECLARE SECTION;
        char gw_cstat[16], gw_reason[64];
        long gw_amount, gw_band, gw_grade, gw_reject, gw_score, gw_fee;
        long gw_msgid, gw_thr, gw_aff, gw_stanmsg;

        /* --- 1) 입력 검증부: 필수/형식/범위 ------------------------------ */
        gets_(b, T_CHANNEL, gw_chan, sizeof(gw_chan));
        if (gw_chan[0] == 0) strcpy(gw_chan, "CH01");
        gets_(b, T_BIZDATE, gw_bizdate, sizeof(gw_bizdate));
        if (gw_bizdate[0] == 0) strcpy(gw_bizdate, "2026-07-19");
        gets_(b, T_STR1, gw_mti, sizeof(gw_mti));
        if (gw_mti[0] == 0) strcpy(gw_mti, "0200");
        gets_(b, T_MERCHANT, gw_merch, sizeof(gw_merch));
        if (gw_merch[0] == 0) strcpy(gw_merch, "M0001");
        gw_amount = getl(b, T_AMOUNT);
        gw_reason[0] = 0;
        if (strlen(gw_chan) < 2) {
            setl(b, T_RC, 9301); sets_(b, T_MSG, "채널ID 형식오류(2자 미만)"); FAIL(b);
        }
        if (strlen(gw_bizdate) < 8) {
            setl(b, T_RC, 9302); sets_(b, T_MSG, "영업일 형식오류"); FAIL(b);
        }
        if (strlen(gw_mti) < 4) {
            setl(b, T_RC, 9303); sets_(b, T_MSG, "전문유형(MTI) 자릿수 오류"); FAIL(b);
        }
        if (gw_amount < 0) {
            setl(b, T_RC, 9304); sets_(b, T_MSG, "전문 금액이 음수"); FAIL(b);
        }

        /* --- 2) 마스터/정책/상태 조회부 --------------------------------- */
        if (mgdb_get_channel_status(gw_chan, gw_cstat) < 0) strcpy(gw_cstat, "DOWN");

        EXEC SQL SELECT coalesce(last_stan, 0) INTO :gw_last
            FROM mg_channel WHERE channel = :gw_chan;
        if (sqlca.sqlcode == 100) gw_last = 0;
        else if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 9310); sets_(b, T_MSG, "채널 마스터 조회실패"); FAIL(b);
        }

        EXEC SQL SELECT count(*),
                        coalesce(sum(CASE WHEN rc <> '00' THEN 1 ELSE 0 END), 0)
            INTO :gw_total, :gw_err
            FROM mg_msg_log WHERE channel = :gw_chan AND biz_date = :gw_bizdate;
        if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 9311); sets_(b, T_MSG, "전문원장 집계 실패"); FAIL(b);
        }

        EXEC SQL SELECT count(*) INTO :gw_backlog
            FROM mg_queue WHERE channel = :gw_chan AND status IN ('Q', 'F');
        if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 9312); sets_(b, T_MSG, "큐 적체 조회 실패"); FAIL(b);
        }

        EXEC SQL SELECT coalesce(daily_limit, 0), coalesce(mdr_bps, 0)
            INTO :gw_limit, :gw_mdr
            FROM merchant WHERE merchant_id = :gw_merch;
        if (sqlca.sqlcode == 100) { gw_limit = 100000000L; gw_mdr = 25; }
        else if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 9313); sets_(b, T_MSG, "가맹점 정책 조회실패"); FAIL(b);
        }

        userlog("MG_CHANLIST 심화진입 chan=%s mti=%s amt=%ld 상태=%s last=%ld",
                gw_chan, gw_mti, gw_amount, gw_cstat, gw_last);

        /* --- 3) 업무규칙 분기부: 금액구간/채널등급/적체 ------------------ */
        gw_band = (gw_amount >= 1000000L) ? 3 : (gw_amount >= 100000L) ? 2
                : (gw_amount > 0) ? 1 : 0;
        if (strcmp(gw_cstat, "UP") == 0)          gw_grade = 0;
        else if (strcmp(gw_cstat, "SIGNON") == 0) gw_grade = 1;
        else                                       gw_grade = 2;
        gw_thr = 81L;
        gw_reject = 0;
        if (gw_band == 3 && gw_amount > gw_limit) {
            gw_reject = 1; strcpy(gw_reason, "가맹점 일한도 초과");
        } else if (gw_grade == 2) {
            gw_reject = 2; strcpy(gw_reason, "채널 비가용(DOWN)");
        } else if (gw_backlog > gw_thr) {
            gw_reject = 3; strcpy(gw_reason, "채널 큐 적체 과다");
        } else {
            strcpy(gw_reason, "정상 수용");
        }

        /* --- 4) 본처리부: 시퀀스 채번/수수료 산출 + 채널 순번 대조(읽기) --- */
        gw_fee  = acq_fee(gw_amount, gw_mdr);
        gw_stan = mgdb_next_stan();
        if (gw_stan < 0) {
            setl(b, T_RC, 9320); sets_(b, T_MSG, "STAN 채번 실패"); FAIL(b);
        }
        /* 채널 마스터(mg_channel) 행을 UPDATE 하는 것은 채널 생애주기 소유자
         * (MG_CHANOPEN/CLOSE/SWITCH/FAILOVER/RESET/STAT, MG_SIGNON/SIGNOFF)만이다.
         * 비소유자인 MG_CHANLIST 은 하나의 글로벌 트랜잭션 안에서 형제 XA
         * 브랜치와 같은 행을 물어 교착하지 않도록 mg_channel 을 SELECT 만 하고,
         * 전문 순번(STAN)은 mg_stan_seq 채번값을 권위로 삼는다. */
        EXEC SQL SELECT coalesce(last_stan, 0) INTO :gw_chanlast
            FROM mg_channel WHERE channel = :gw_chan;
        if (sqlca.sqlcode == 100) gw_chanlast = 0;
        else if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 9321); sets_(b, T_MSG, "채널 순번조회 실패"); FAIL(b);
        }
        if (gw_stan > gw_chanlast) {
            gw_aff = 1;             /* 정상: 채번값이 채널 기록순번보다 앞선다 */
        } else {
            gw_aff = 0;             /* 순번 역전: 채널 재개통/시퀀스 리셋 흔적 */
            userlog("MG_CHANLIST 순번역전 감지 chan=%s seq=%ld chan_last=%ld",
                    gw_chan, gw_stan, gw_chanlast);
        }
        /* 채널행 갱신 대신 자기 이력(mg_msg_log)에 채번 사실을 적재한다.
         * 채널별 최종 순번은 조회 시점에 max(stan) 으로 난다. */
        gw_stanmsg = mgdb_next_msg_id();
        if (gw_stanmsg > 0) {
            if (mgdb_insert_msg(gw_stanmsg, gw_stan, gw_mti, gw_chan, "OUT",
                                "000000", "", gw_amount, "", "00",
                                "GW-STAN-MG_CHANLIST", "S", gw_bizdate) < 0) {
                userlog("MG_CHANLIST 순번이력 적재경고 stan=%ld", gw_stan);
            }
        }

        /* --- 5) 이력/감사 적재부: 전문원장에 감사행 기록 --------------- */
        gw_msgid = mgdb_next_msg_id();
        if (gw_msgid > 0) {
            if (mgdb_insert_msg(gw_msgid, gw_stan, gw_mti, gw_chan, "IN",
                                "000000", "", gw_amount, "", "00",
                                "GW-AUDIT-MG_CHANLIST", "C", gw_bizdate) < 0) {
                userlog("MG_CHANLIST 감사이력 적재경고 stan=%ld", gw_stan);
            }
        }

        /* --- 6) 집계/요약 재집계부: 당일 채널 금액 재합산 ------------- */
        EXEC SQL SELECT coalesce(sum(amount), 0) INTO :gw_daysum
            FROM mg_msg_log WHERE channel = :gw_chan AND biz_date = :gw_bizdate;
        if (sqlca.sqlcode < 0) {
            setl(b, T_RC, 9330); sets_(b, T_MSG, "당일 금액 재집계 실패"); FAIL(b);
        }

        /* --- 7) 예외/보상부: 건강점수 산출 + 거절 로깅 ---------------- */
        gw_score = 100;
        if (gw_grade != 0)  gw_score -= 40;
        if (gw_total > 0)   gw_score -= (gw_err * 30L) / gw_total;
        if (gw_backlog > 0) gw_score -= (gw_backlog > 20 ? 20 : gw_backlog);
        if (gw_score < 0)   gw_score = 0;
        if (gw_reject != 0) {
            userlog("MG_CHANLIST 규칙거절 사유=%s reject=%ld band=%ld score=%ld",
                    gw_reason, gw_reject, gw_band, gw_score);
        }

        /* --- 8) 응답 보조필드부: 심화전용 스크래치 필드에만 반영 ----- */
        /* (원 서비스가 입력으로 읽지 않는 필드만 사용해 I/O 계약 보존) */
        setl(b, T_ARG6, gw_stan);
        setl(b, T_ARG7, gw_backlog);
        setl(b, T_ARG8, gw_score);
        setl(b, T_AMT2, gw_fee);
        setl(b, T_AMT3, gw_daysum);
        sets_(b, T_STR8, gw_reason);
        (void)gw_msgid;

        /* --- 9) 완료 로깅 -------------------------------------------- */
        userlog("MG_CHANLIST 심화완료 chan=%s daysum=%ld fee=%ld score=%ld aff=%ld",
                gw_chan, gw_daysum, gw_fee, gw_score, gw_aff);
    }
    char list[256], tmp[24];
    long up = 0, down = 0;
    EXEC SQL BEGIN DECLARE SECTION;
    char h_chan[16], h_status[16];
    EXEC SQL END DECLARE SECTION;

    list[0] = 0;
    EXEC SQL DECLARE clst CURSOR FOR
        SELECT channel, status FROM mg_channel ORDER BY channel;
    EXEC SQL OPEN clst;
    if (sqlca.sqlcode < 0) FAIL(b);
    for (;;) {
        EXEC SQL FETCH clst INTO :h_chan, :h_status;
        if (sqlca.sqlcode == 100) break;
        if (sqlca.sqlcode < 0) { EXEC SQL CLOSE clst; FAIL(b); }
        if (strcmp(h_status, "UP") == 0) {
            up++;
            sprintf(tmp, "%s%s", (up > 1 ? "," : ""), h_chan);
            if (strlen(list) + strlen(tmp) < sizeof(list) - 1) strcat(list, tmp);
        } else {
            down++;
        }
    }
    EXEC SQL CLOSE clst;

    sets_(b, T_STR3, list);
    setl(b, T_ARG2, up);
    setl(b, T_ARG3, down);
    setl(b, T_RC, (up == 0) ? 1 : 0);
    OK(b);
}
