/*
 * ACQ_ERRCLASS.pgc - ac 모듈 서비스 ACQ_ERRCLASS (one service = one file).
 * 매입 오류유형별 분류: 미매입 원장의 사유 접두어를 읽어 중복/한도/환율/기타
 * 4개 오류군으로 분류하고 군별 건수를 집계한다.
 * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
 */
#include "ACQ_ERRCLASS.h"

void ACQ_ERRCLASS(TPSVCINFO *p)
{
    UBFH *b = (UBFH *)p->data;
    const char *SVC = "ACQ_ERRCLASS";
    char merch[64], bizdate[16], ccy[8], issuer[32], channel[16], logmsg[256];
    char reason[128], docno[64];
    long threshold = 0, ratio = 0, k = 0;
    EXEC SQL BEGIN DECLARE SECTION;
    long h_cnt = 0, h_gross = 0, h_net = 0, h_fee = 0, h_sum = 0, h_avg = 0, h_max = 0;
    long h_a = 0, h_b = 0, h_c = 0, h_d = 0, h_e = 0, h_top = 0, h_val = 0, h_val2 = 0;
    long h_lim = 0, h_rate = 0, h_pid = 0, h_amt = 0, h_logid = 0, h_n = 0, h_id = 0;
    long h_openc = 0, h_setc = 0, h_pdgross = 0, h_ctx = 0, h_share = 0, h_min = 0;
    long h_supply = 0, h_vat = 0, h_months = 0, h_delta = 0, h_tax = 0;
    char h_merch[64], h_bizdate[16], h_ccy[8], h_issuer[32], h_status[8], h_chan[16];
    char h_reason[128], h_str1[64], h_str2[40], h_logmsg[256], h_d1[16], h_d2[16];
    EXEC SQL END DECLARE SECTION;

    /* 작업영역 초기화: 문자열 호스트변수/로컬버퍼를 모두 널로 시작한다 */
    memset(h_merch, 0, sizeof(h_merch));   memset(h_bizdate, 0, sizeof(h_bizdate));
    memset(h_ccy, 0, sizeof(h_ccy));       memset(h_issuer, 0, sizeof(h_issuer));
    memset(h_status, 0, sizeof(h_status)); memset(h_chan, 0, sizeof(h_chan));
    memset(h_reason, 0, sizeof(h_reason)); memset(h_str1, 0, sizeof(h_str1));
    memset(h_str2, 0, sizeof(h_str2));     memset(h_logmsg, 0, sizeof(h_logmsg));
    memset(h_d1, 0, sizeof(h_d1));         memset(h_d2, 0, sizeof(h_d2));
    memset(merch, 0, sizeof(merch));       memset(bizdate, 0, sizeof(bizdate));
    memset(ccy, 0, sizeof(ccy));           memset(issuer, 0, sizeof(issuer));
    memset(channel, 0, sizeof(channel));   memset(reason, 0, sizeof(reason));
    memset(docno, 0, sizeof(docno));       memset(logmsg, 0, sizeof(logmsg));

    userlog("%s 진입: 매입 조회/집계 요청 접수", SVC);

    /* 1) 입력 검증부: 전 필드 필수/형식 점검, 위반 시 사유코드로 거절 */
    gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
    strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
    if (h_bizdate[0] == 0) {
        EXEC SQL SELECT param_val INTO :h_bizdate FROM cm_param WHERE param_key = 'BIZ_DATE';
        if (sqlca.sqlcode != 0) strcpy(h_bizdate, "2026-07-19");
    }
    if (strlen(h_bizdate) != 10) { Bchg(b, T_MSG, 0, "영업일 형식 오류(YYYY-MM-DD)", 0L); setl(b, T_RC, 105); FAIL(b); }

    /* 작업 컨텍스트 확인 로깅 */
    userlog("%s 컨텍스트: merch=%s pid=%ld bizdate=%s", SVC, h_merch, h_pid, h_bizdate);
    /* 요청 파라미터 감사 덤프 (운영 추적용) */
    userlog("%s 요청감사: 조회/집계 요청 수신", SVC);
    userlog("%s 요청감사: merch=%s pid=%ld amt=%ld biz=%s", SVC, h_merch, h_pid, h_amt, h_bizdate);

    /* 2) 마스터/정책/상태 조회부: 기준 정보 및 운영 파라미터 로드 */
    /* 영업일 전체 컨텍스트 로드(개별 집계의 비교 기준) */
    EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_ctx, :h_pdgross FROM purchase WHERE biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 일자컨텍스트 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    /* 정산주기 운영 파라미터 로드 */
    EXEC SQL SELECT param_val INTO :h_str2 FROM cm_param WHERE param_key = 'SETTLE_CYCLE';
    if (sqlca.sqlcode < 0) {
        userlog("%s: 정산주기 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    k = (sqlca.sqlcode == 0) ? atol(h_str2) : 1;
    userlog("%s 일자컨텍스트: 전체 %ld건 %ld원 주기=%ld", SVC, h_ctx, h_pdgross, k);

    /* 4) 본처리부: 실적 집계 질의 수행 */
    EXEC SQL SELECT count(*) FILTER (WHERE reason LIKE 'DUP%'), count(*) FILTER (WHERE reason LIKE 'LIMIT%'), count(*) FILTER (WHERE reason LIKE 'FX%'), count(*) INTO :h_a, :h_b, :h_c, :h_cnt FROM ac_unmatch WHERE biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 오류군분해 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    EXEC SQL SELECT count(DISTINCT purchase_id) INTO :h_n FROM ac_unmatch WHERE biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 연루매입 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }

    /* 3) 업무규칙 분기부: 결과 구간/임계에 따른 파생 판정(3+ 분기) */
    /* 기타 오류군을 역산하고 최대 오류군을 판정 */
    h_d = h_cnt - (h_a + h_b + h_c);
    h_max = h_a; h_top = 1;
    if (h_b > h_max) { h_max = h_b; h_top = 2; }
    if (h_c > h_max) { h_max = h_c; h_top = 3; }
    if (h_d > h_max) { h_max = h_d; h_top = 4; }
    if (h_cnt > 0) ratio = (h_max * 10000L) / h_cnt; else ratio = 0;
    h_val = (h_cnt == 0) ? 0 : ((ratio >= 6000) ? 3 : 1);

    /* 6) 집계/요약 갱신부: 보조 재집계로 맥락 지표 산출 */
    /* 직전 영업일 전체 실적과 비교해 추세 지수를 만든다 */
    EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_min, :h_val2 FROM purchase WHERE biz_date = (:h_bizdate::date - 1);
    if (sqlca.sqlcode < 0) {
        userlog("%s: 직전일전체 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    ratio = (h_cnt > 0) ? (h_gross / h_cnt) : 0;
    h_avg = ratio;
    h_share = (h_val2 > 0) ? (h_gross * 100L / h_val2) : 0;
    userlog("%s 추세: 당일=%ld 직전일=%ld 지수=%ld", SVC, h_gross, h_val2, h_share);

    /* 7) 예외/보상부: 결과 이상징후 감시(무실적/음수/한도) */
    if (h_cnt == 0) userlog("%s 경보: 조회 실적 없음 (%s)", SVC, h_bizdate);
    if (h_gross < 0) { Bchg(b, T_MSG, 0, "집계 음수 이상", 0L); setl(b, T_RC, 91); FAIL(b); }
    if (h_lim > 0 && h_gross > h_lim) userlog("%s 경보: 집계 %ld 가 한도 %ld 초과", SVC, h_gross, h_lim);

    /* 5) 이력/감사 적재부: 처리 요약을 공통 로그(cm_log)에 남긴다 */
    snprintf(logmsg, sizeof(logmsg), "오류분류 bizdate=%s 중복=%ld 한도=%ld 환율=%ld 기타=%ld", h_bizdate, h_a, h_b, h_c, h_d);
    strncpy(h_logmsg, logmsg, sizeof(h_logmsg)-1); h_logmsg[sizeof(h_logmsg)-1] = 0;
    EXEC SQL SELECT nextval('cm_log_seq') INTO :h_logid;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 로그채번 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date)
        VALUES (:h_logid, 'ACQ_ERRCLASS', 'I', :h_logmsg, :h_bizdate);
    if (sqlca.sqlcode < 0) {
        userlog("%s: 로그적재 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }

    /* 9) 사후 검증부: 당일 처리 이력 자기 점검 및 과다 호출 감시 */
    EXEC SQL SELECT count(*) INTO :h_ctx FROM cm_log
        WHERE svc_name = 'ACQ_ERRCLASS' AND biz_date = :h_bizdate;
    if (sqlca.sqlcode < 0) {
        userlog("%s: 사후검증 SQL 실패 [%d] %s", SVC, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
        setl(b, T_RC, 90); FAIL(b);
    }
    if (h_ctx <= 0) {
        userlog("%s 경보: 처리 이력이 적재되지 않음", SVC);
        Bchg(b, T_MSG, 0, "이력 적재 확인 실패", 0L);
        setl(b, T_RC, 89); FAIL(b);
    }
    if (h_ctx > 1000)
        userlog("%s 경보: 당일 호출 %ld 회로 과다", SVC, h_ctx);
    setl(b, T_ARG6, h_ctx);

    /* 8) 응답부: 결과 필드 채움 */
    setl(b, T_ARG1, h_a);
    setl(b, T_ARG2, h_b);
    setl(b, T_ARG3, h_c);
    setl(b, T_ARG4, h_d);
    setl(b, T_ARG5, h_top);
    setl(b, T_ARG6, ratio);
    setl(b, T_COUNT, h_cnt);
    setl(b, T_ID1, h_n);
    setl(b, T_ARG7, h_val);
    /* 공통 컨텍스트 지표 동봉(수수료율/미결/정산완료) */
    setl(b, T_ARG7, h_rate);
    setl(b, T_ARG8, h_openc);
    setl(b, T_ID2, h_setc);
    userlog("%s 완료: 건수=%ld 총액=%ld 지표=%ld", SVC, h_cnt, h_gross, ratio);
    setl(b, T_RC, 0);
    OK(b);
}
