<?php

require_once getenv("DOCUMENT_ROOT")."/assets/conf.inc.php";
require_once getenv("DOCUMENT_ROOT")."/assets/dbCon.php";

class DailyService extends CONF {

    public function saveDaily(array $data = [], array $opt = [])
    {
        $connect = $GLOBALS['conn'] ?? null;
        if (!$connect) {
            throw new Exception("DB connection not found");
        }

        $inTx = !empty($opt['in_tx']);

        if (empty($data['d_orderdate']) || empty($data['d_accountno'])) {
            throw new Exception("d_orderdate, d_accountno required");
        }

        // =========================
        // KEY NORMALIZE
        // =========================
        $d_orderdate = preg_replace('/[^0-9]/', '', $data['d_orderdate']);
        if (strlen($d_orderdate) !== 8) {
            throw new Exception("invalid d_orderdate");
        }

        $d_accountno = mysqli_real_escape_string($connect, trim($data['d_accountno']));
        if ($d_accountno === '') {
            throw new Exception("invalid d_accountno");
        }

        $d_jobtype = mysqli_real_escape_string($connect, trim($data['d_jobtype']));
        if ($d_jobtype === '') {
            $d_jobtype = 'UCO';
        }

        $now14 = date('YmdHis');

        if (!$inTx) {
            mysqli_query($connect, "START TRANSACTION");
        }

        try {

            // =========================
            // EXISTING LOAD (개선됨)
            // =========================

            $existing = null;

            // 1. d_uid 우선 (가장 안전)
            if (!empty($opt['match_uid'])) {
                $uid = (int)$opt['match_uid'];

                $res = mysqli_query($connect, "
                    SELECT *
                    FROM tbl_daily
                    WHERE d_uid = '{$uid}'
                    LIMIT 1
                ");
                if (!$res) throw new Exception(mysqli_error($connect));

                $existing = mysqli_fetch_assoc($res);
            }

            // 2. old_key fallback (key 변경 대응)
            if (!$existing && !empty($opt['old_key'])) {

                $oldDate = preg_replace('/[^0-9]/', '', $opt['old_key']['d_orderdate'] ?? '');
                $oldAcc  = mysqli_real_escape_string($connect, $opt['old_key']['d_accountno'] ?? '');
                $oldJob  = mysqli_real_escape_string($connect, $opt['old_key']['d_jobtype'] ?? '');

                if ($oldDate && $oldAcc) {
                    $res = mysqli_query($connect, "
                        SELECT *
                        FROM tbl_daily
                        WHERE d_orderdate = '{$oldDate}'
                            AND d_accountno = '{$oldAcc}'
                            AND d_jobtype = '{$oldJob}'
                        LIMIT 1
                    ");
                    if (!$res) throw new Exception(mysqli_error($connect));

                    $existing = mysqli_fetch_assoc($res);
                }
            }

            // 3. 기본 key 매칭
            if (!$existing) {
                $res = mysqli_query($connect, "
                    SELECT * 
                    FROM tbl_daily
                    WHERE d_orderdate = '{$d_orderdate}'
                        AND d_accountno = '{$d_accountno}'
                        AND d_jobtype = '{$d_jobtype}'
                    LIMIT 1
                ");
                if (!$res) throw new Exception(mysqli_error($connect));

                $existing = mysqli_fetch_assoc($res);
            }

            $isUpdate = (bool)$existing;
            $beforeData = $existing ?: null;

            // =========================
            // DUPLICATE POLICY (중요)
            // =========================
            if ($isUpdate && empty($opt['allow_update'])) {
                if (!$inTx) mysqli_query($connect, "ROLLBACK");
                return null; // skip
            }

            // =========================
            // normalize + snapshot
            // =========================
            $data = $this->normalizeDailyPayload($data, $existing ?: null);
            $data = $this->hydrateDailySnapshot($data, $existing ?: null);

            // estquantity 자동 계산 (입력때만 안그러면 d_estquantity 값이 0으로 덮어 씌워짐)
            if (!$existing && empty($data['d_estquantity'])) {
                $data['d_estquantity'] = $this->calculateEstQuantity($data, $existing);
            }

            // note 백업 (tbl_note 로 전달할 값)
            $noteForUpsert = $data['d_note'] ?? '';
            $pickupNoteForUpsert = $data['d_pickupnote'] ?? '';
            $level = $data['d_level'] ?? 9;

            // =========================
            // 컬럼 필터링
            // =========================
            $allowed = $this->getDailyColumns();
            $data = array_intersect_key($data, array_flip($allowed));

            // =========================
            // UPDATE
            // =========================
            if ($isUpdate) {

                $setParts = [];

                foreach ($data as $k => $v) {

                    if (in_array($k, ['d_orderdate','d_accountno','d_jobtype'], true)) continue;
                    if (!array_key_exists($k, $existing)) continue;

                    $setParts[] = "{$k}='" . mysqli_real_escape_string($connect, (string)$v) . "'";
                }

                $setParts[] = "d_modifydate='{$now14}'";

                if (!empty($setParts)) {

                    // update는 무조건 d_uid 기준으로
                    $uid = (int)$existing['d_uid'];

                    $sql = "
                        UPDATE tbl_daily
                        SET " . implode(",", $setParts) . "
                        WHERE d_uid='{$uid}'
                        LIMIT 1
                    ";

                    if (!mysqli_query($connect, $sql)) {
                        throw new Exception(mysqli_error($connect));
                    }
                }
            }

            // =========================
            // INSERT
            // =========================
            else {

                $cols = [];
                $vals = [];

                foreach ($data as $k => $v) {
                    $cols[] = $k;
                    $vals[] = "'" . mysqli_real_escape_string($connect, (string)$v) . "'";
                }

                if (!in_array('d_modifydate', $cols)) {
                    $cols[] = 'd_modifydate';
                    $vals[] = "'{$now14}'";
                }

                $sql = "
                    INSERT INTO tbl_daily (" . implode(",", $cols) . ")
                    VALUES (" . implode(",", $vals) . ")
                ";

                if (!mysqli_query($connect, $sql)) {
                    throw new Exception(mysqli_error($connect));
                }
            }

            // =========================
            // AFTER RELOAD (uid 기준)
            // =========================
            if ($isUpdate) {
                $uid = (int)$existing['d_uid'];
                $res = mysqli_query($connect, "
                    SELECT *
                    FROM tbl_daily
                    WHERE d_uid='{$uid}'
                    LIMIT 1
                ");
            } else {
                $res = mysqli_query($connect, "
                    SELECT * 
                    FROM tbl_daily
                    WHERE d_orderdate='{$d_orderdate}'
                        AND d_accountno='{$d_accountno}'
                        AND d_jobtype = '{$d_jobtype}'
                    ORDER BY d_uid DESC
                    LIMIT 1
                ");
            }

            $afterData = mysqli_fetch_assoc($res);
            // note
            if ($noteForUpsert !== '') {
                $afterData['d_note'] = $noteForUpsert;
                $afterData['d_level'] = $level;
            }

            if ($pickupNoteForUpsert !== '') {
                $afterData['d_pickupnote'] = $pickupNoteForUpsert;
                $afterData['d_level'] = $level;
            }

            // =========================
            // ERP OUTBOX
            // =========================
            $event_type = $isUpdate ? 'DAILY_UPDATED' : 'DAILY_CREATED';

            $payload = mysqli_real_escape_string(
                $connect,
                json_encode([
                    'key' => [
                        'd_orderdate'=>$afterData['d_orderdate'],
                        'd_accountno'=>$afterData['d_accountno'],
                        'd_jobtype'=>$afterData['d_jobtype']
                    ],
                    'before'=>$beforeData,
                    'after'=>$afterData
                ], JSON_UNESCAPED_UNICODE)
            );

            mysqli_query($connect, "
                INSERT INTO tbl_erp_outbox
                (eo_event_type, eo_key_date, eo_key_accountno, eo_key_jobtype, eo_payload, eo_status, eo_created_at)
                VALUES
                ('{$event_type}',
                '{$afterData['d_orderdate']}',
                '{$afterData['d_accountno']}',
                '{$afterData['d_jobtype']}',
                '{$payload}',
                'PENDING',
                NOW())
            ");

            // =========================
            // AFTER WORK
            // =========================
            $this->runAfterSave($beforeData, $afterData, $opt);

            // =========================
            // COMMIT
            // =========================
            if (!$inTx) {
                mysqli_query($connect, "COMMIT");
            }

            return $afterData;

        } catch (Exception $e) {

            if (!$inTx) {
                mysqli_query($connect, "ROLLBACK");
            }

            throw $e;
        }
    }

    private function isDuplicate($orderdate, $accountno, $jobtype): bool
    {
        $connect = $GLOBALS['conn'];

        $res = mysqli_query($connect, "
            SELECT 1 
            FROM tbl_daily
            WHERE d_orderdate='{$orderdate}'
                AND d_accountno='{$accountno}'
                AND d_jobtype = '{$jobtype}'
            LIMIT 1
        ");

        return (bool)mysqli_fetch_assoc($res);
    }

    private function normalizeDailyPayload(array $data, ?array $existing = null): array
    {
        // 날짜 정리
        if (isset($data['d_orderdate'])) {
            $data['d_orderdate'] = preg_replace('/[^0-9]/', '', $data['d_orderdate']);
        }

        if (isset($data['d_visitdate'])) {
            $data['d_visitdate'] = preg_replace('/[^0-9]/', '', $data['d_visitdate']);
        }

        // 숫자 필드 기본값
        $intFields = [
            'd_quantity',
            'd_sludge'
        ];

        foreach ($intFields as $f) {
            if (isset($data[$f])) {
                $data[$f] = ($data[$f] === '' || $data[$f] === null)
                    ? 0
                    : (int)$data[$f];
            }
        }

        // 문자열 trim
        $strFields = [
            'd_pickupnote',
            'd_note'
        ];

        foreach ($strFields as $f) {
            if (isset($data[$f])) {
                $data[$f] = trim((string)$data[$f]);
            }
        }

        return $data;
    }

    private function hydrateDailySnapshot(array $data, ?array $existing = null): array
    {
        $connect = $GLOBALS['conn'] ?? null;
        if (!$connect) {
            throw new Exception("DB connection not found");
        }

        // =========================
        // DRIVER SNAPSHOT
        // =========================
        $druid = isset($data['d_druid'])
            ? (int)$data['d_druid']
            : (int)($existing['d_druid'] ?? 0);

        // fallback: legacy member uid -> current driver uid
        if ($druid <= 0) {
            $driverUid = isset($data['d_driveruid'])
                ? (int)$data['d_driveruid']
                : (int)($existing['d_driveruid'] ?? 0);

            if ($driverUid > 0) {
                $sqMap = qry("
                    SELECT 
                        m.m_currentdriver AS dr_uid,
                        d.dr_initial
                    FROM tbl_member m
                    LEFT JOIN tbl_driver d
                        ON d.dr_uid = m.m_currentdriver
                    WHERE m.m_uid = '{$driverUid}'
                    LIMIT 1
                ");
                $map = fetch_array($sqMap);

                if ($map && (int)$map['dr_uid'] > 0) {
                    $druid = (int)$map['dr_uid'];
                    $data['d_druid'] = $druid;
                    $data['d_drinitial'] = $map['dr_initial'] ?? '';
                }
            }
        }

        // druid가 있으면 driver snapshot 확정
        if ($druid > 0) {
            $sqDriver = qry("
                SELECT dr_initial
                FROM tbl_driver
                WHERE dr_uid = '{$druid}'
                LIMIT 1
            ");
            $driver = fetch_array($sqDriver);

            if ($driver && !empty($driver['dr_initial'])) {
                $data['d_druid'] = $druid;
                $data['d_drinitial'] = $driver['dr_initial'];
            } elseif ($existing) {
                $data['d_druid'] = $existing['d_druid'] ?? 0;
                $data['d_drinitial'] = $existing['d_drinitial'] ?? '';
            }
        }

        // =========================
        // CUSTOMER SNAPSHOT (확장)
        // =========================
        $customerUid = isset($data['d_customeruid'])
            ? (int)$data['d_customeruid']
            : (int)($existing['d_customeruid'] ?? 0);

        if ($customerUid > 0) {
            $sqCustomer = qry("
                SELECT 
                    c_regionuid, 
                    c_region,
                    c_fullcycle, 
                    c_fullcycleforced, 
                    c_fullcycleflag,
                    c_lastpickupdate, 
                    c_lastpickupquantity, 
                    c_lastpaiddate,
                    c_name,
                    c_paymenttype,
                    c_paymentcycle,
                    c_rate,
                    c_form_eu,
                    c_form_corsia,
                    c_maincontainer,
                    c_container,
                    c_location,
                    c_phone,
                    c_address,
                    c_city,
                    c_postal,
                    c_area

                FROM tbl_customer
                WHERE c_uid = '{$customerUid}'
                LIMIT 1
            ");
            $customer = fetch_array($sqCustomer);

            if ($customer) {
                $data['d_name']               = $customer['c_name'] ?? '';
                $data['d_form_eu']            = $customer['c_form_eu'] ?? '';
                $data['d_form_corsia']        = $customer['c_form_corsia'] ?? '';
                $data['d_maincontainer']      = $customer['c_maincontainer'] ?? '';
                //$data['d_container']          = $customer['c_container'] ?? '';
                $data['d_container']          = $this->buildContainerString($customerUid, $connect);
                $data['d_location']           = $customer['c_location'] ?? '';
                $data['d_phone']              = $customer['c_phone'] ?? '';
                $data['d_address']            = $customer['c_address'] ?? '';
                $data['d_city']               = $customer['c_city'] ?? '';
                $data['d_postal']             = $customer['c_postal'] ?? '';
                $data['d_area']               = $customer['c_area'] ?? '';
                $data['d_regionuid']          = (int)($customer['c_regionuid'] ?? 0);
                $data['d_region']             = $customer['c_region'] ?? '';
                $data['d_fullcycle']          = $customer['c_fullcycle'] ?? 0;
                $data['d_fullcycleforced']    = $customer['c_fullcycleforced'] ?? 0;
                $data['d_fullcycleflag']      = $customer['c_fullcycleflag'] ?? 0;
                $data['d_lastpickupdate']     = $customer['c_lastpickupdate'] ?? '';
                $data['d_lastpickupquantity'] = $customer['c_lastpickupquantity'] ?? 0;
                $data['d_lastpaiddate']       = $customer['c_lastpaiddate'] ?? '';
                $data['d_paymenttype']        = $customer['c_paymenttype'] ?? '';
                $data['d_cycle']              = $customer['c_paymentcycle'] ?? '';
                $data['d_rate']               = $customer['c_rate'] ?? '';
                if (!isset($data['d_oil_2y'])) $data['d_oil_2y'] = 0;
                if (!isset($data['d_oil_1y'])) $data['d_oil_1y'] = 0;
                if (!isset($data['d_oil_0y'])) $data['d_oil_0y'] = 0;
            }
        }

        return $data;
    }

    function buildContainerString($c_uid, $connect) {
        $sql = "
            SELECT cc_type, COUNT(*) as cnt
            FROM tbl_customer_container
            WHERE cc_customer_uid = '{$c_uid}'
            AND cc_status = 'A'
            GROUP BY cc_type
            ORDER BY cc_type
        ";

        $res = mysqli_query($connect, $sql);

        $parts = [];

        while ($row = mysqli_fetch_assoc($res)) {
            $type = $row['cc_type'];
            $cnt = (int)$row['cnt'];

            if ($cnt > 1) {
                $parts[] = "{$type}x{$cnt}";
            } else {
                $parts[] = $type;
            }
        }

        return implode(', ', $parts);
    }

    private function getDailyColumns(): array
    {
        return [
            'd_uid',

            // key
            'd_orderdate',
            'd_accountno',
            'd_customeruid',
            'd_jobtype',

            // driver / region
            'd_driveruid',
            'd_druid',
            'd_drinitial',
            'd_regionuid',
            'd_region',

            // basic status
            'd_ordertype',
            'd_visitdate',
            'd_visit',
            'd_status',

            // qty / sludge / payment
            'd_quantity',
            'd_sludge',
            'd_paymenttype',
            'd_payamount',
            'd_payeename',
            'd_paystatus',

            // customer snapshot
            'd_name',
            'd_form_eu',
            'd_form_corsia',
            'd_maincontainer',
            'd_container',
            'd_location',
            'd_phone',
            'd_address',
            'd_city',
            'd_postal',
            'd_area',

            // oil / cycle history snapshot
            'd_oil_2y',
            'd_oil_1y',
            'd_oil_0y',
            'd_fullcycle',
            'd_fullcycleforced',
            'd_fullcycleflag',
            'd_lastpickupdate',
            'd_lastpickupquantity',
            'd_lastpaiddate',

            // pricing / estimate
            'd_cycle',
            'd_rate',
            'd_estquantity',

            // audit
            'd_createruid',
            'd_createddate',
            'd_inputdate',
            'd_modifydate',
            'd_ruid',

            // misc
            'd_payeesign',
            'd_is_transfer'
        ];
    }

    private function calculateEstQuantity(array $data, ?array $existing = null): int
    {
        $customerUid = $data['d_customeruid'] ?? ($existing['d_customeruid'] ?? 0);
        $orderdate = $data['d_orderdate'] ?? ($existing['d_orderdate'] ?? '');

        if (!$customerUid || !$orderdate) return 0;

        $sqCus = qry("
            SELECT c_fullquantity, c_fullquantitydaily
            FROM tbl_customer
            WHERE c_uid = '{$customerUid}'
            LIMIT 1
        ");
        $customer = fetch_array($sqCus);

        if (!$customer) return 0;

        $today = new DateTime(date("Y-m-d"));
        $searchDate = new DateTime(substr($orderdate,0,4)."-".substr($orderdate,4,2)."-".substr($orderdate,6,2));

        $interval = $today->diff($searchDate);
        $days = (int)$interval->days;

        return max(
            0,
            ($customer['c_fullquantity'] - $customer['c_fullquantitydaily']) +
            ($days * $customer['c_fullquantitydaily'])
        );
    }

    private function runAfterSave(?array $before, array $after, array $opt = []): void
    {
        $this->updateCustomerFromDaily($before, $after);
        $this->updateRequestFromDaily($after);
        $this->upsertDailyNote($after);
        $this->handlePickupCleanup($before, $after);
        $this->handlePickupReset($before, $after);
    }

    private function runAfterDelete(array $before, array $opt = []): void
    {
        $connect = $GLOBALS['conn'];

        if (empty($before['d_customeruid'])) {
            return;
        }

        $customerUid = mysqli_real_escape_string($connect, $before['d_customeruid']);
        $orderdate   = mysqli_real_escape_string($connect, $before['d_orderdate'] ?? '');

        // 1) REQUEST 삭제
        $this->deleteRequestByDaily($before);

        // 2) CUSTOMER 재계산 (lastpick, lastpaid 등 포함)
        $this->recalcCustomerFromDailyHistory($customerUid);

        // 3) GHOST 재판정
        $this->reEvaluateGhost($customerUid);
    }

    private function deleteRequestByDaily(array $before): void
    {
        $connect = $GLOBALS['conn'];

        $customerUid = (int)($before['d_customeruid'] ?? 0);
        $orderdate   = preg_replace('/[^0-9]/', '', $before['d_orderdate'] ?? '');

        if ($customerUid <= 0 || $orderdate === '') {
            return;
        }

        mysqli_query($connect, "
            DELETE FROM tbl_request
            WHERE r_customeruid = '{$customerUid}'
            AND r_requestdate = '{$orderdate}'
        ");
    }

    private function updateCustomerFromDaily($before, $after)
    {
        $connect = $GLOBALS['conn'];

        if (empty($after['d_customeruid'])) return;

        $customer_uid = mysqli_real_escape_string($connect, $after['d_customeruid']);

        // 고객 현재 상태 조회
        $qr = mysqli_query($connect, "
            SELECT 
                c_paymenttype,
                c_is_top,
                c_fpickup,
                c_schedule,
                c_scheduleday
            FROM tbl_customer
            WHERE c_uid = '{$customer_uid}'
            LIMIT 1
        ");

        $customer = mysqli_fetch_assoc($qr);
        if (!$customer) return;

        $updateFields = [];

        $ordertype   = $after['d_ordertype'] ?? 'N';
        $orderdate   = $after['d_orderdate'] ?? '';
        $visitdate   = $after['d_visitdate'] ?? '';
        $thisQuantity= (int)($after['d_quantity'] ?? 0);
        $lastQuantity= (int)($after['d_lastpickupquantity'] ?? 0);
        $paystatus   = $after['d_paystatus'] ?? '';
        $paymenttype = $after['d_paymenttype'] ?? '';

        $isTop = $customer['c_is_top'] ?? '';

        // --------------------------------------------------
        // 0) Ghost Check
        // --------------------------------------------------
        if (
            $thisQuantity > 0 &&
            $lastQuantity > 0 &&
            $thisQuantity < 10 &&
            $lastQuantity < 10 &&
            $isTop !== 'G'
        ) {
            $updateFields[] = "c_is_top = 'G'";
        }

        if ($isTop === 'G' && $thisQuantity >= 10) {
            $updateFields[] = "c_is_top = ''";
        }

        // --------------------------------------------------
        // 1) c_fpickup 처리
        // --------------------------------------------------
        if (
            empty($customer['c_fpickup']) &&
            !empty($orderdate) &&
            $thisQuantity >= 10
        ) {
            $updateFields[] = "c_fpickup = '{$orderdate}'";
        }

        // --------------------------------------------------
        // 2) orderflag 처리
        // --------------------------------------------------
        if (!empty($after['d_visit']) && $after['d_visit'] === 'Y') {
            $updateFields[] = "c_orderdate = ''";
            $updateFields[] = "c_orderflag = 0";
        } else {
            $updateFields[] = "c_orderdate = '{$orderdate}'";
            $updateFields[] = "c_orderflag = 1";
        }

        // --------------------------------------------------
        // 3) Scheduled 처리 (d_ordertype == 'S')
        // --------------------------------------------------
        if ($ordertype === 'S' && !empty($visitdate)) {

            $schedule    = $customer['c_schedule'] ?? '';
            $scheduleDay = $customer['c_scheduleday'] ?? '';

            if ($schedule > '1W' && $schedule <= '6W') {

                $weekCount = (int)substr($schedule, 0, 1);

                if ($weekCount > 0 && !empty($scheduleDay)) {

                    $dayParts  = explode('|', $scheduleDay);
                    $dayOfWeek = $dayParts[0] ?? '';

                    $calcStr  = "+{$weekCount} week {$dayOfWeek}";
                    $nextDate = date('Ymd', strtotime($calcStr, strtotime($visitdate)));

                    $updateFields[] = "c_schedulebasic = '{$nextDate}'";
                }
            }
        }

        // --------------------------------------------------
        // UPDATE 실행
        // --------------------------------------------------
        if (!empty($updateFields)) {

        mysqli_query($connect, "
                UPDATE tbl_customer
                SET " . implode(",", $updateFields) . "
                WHERE c_uid = '{$customer_uid}'
                LIMIT 1
            ");
        }
    }

    private function updateRequestFromDaily(array $after): void
    {
        $connect = $GLOBALS['conn'];

        if (empty($after['d_ordertype']) || $after['d_ordertype'] !== 'R') {
            return;
        }

        if (empty($after['d_ruid'])) {
            return;
        }

        // pickup 완료 조건 (방문 완료 + 상태 F)
        if (
            empty($after['d_visit']) ||
            $after['d_visit'] !== 'Y' ||
            empty($after['d_status']) ||
            $after['d_status'] !== 'F'
        ) {
            return;
        }

        $r_uid = mysqli_real_escape_string($connect, $after['d_ruid']);

        mysqli_query($connect, "
            UPDATE tbl_request
            SET r_status = 'F'
            WHERE r_uid = '{$r_uid}'
            LIMIT 1
        ");
    }

    private function upsertDailyNote(array $after): void
    {
        $connect = $GLOBALS['conn'];

        if (empty($after['d_uid'])) return;

        // note는 saveDaily payload에 포함되어 있어야 함
        // if (empty($after['d_pickupnote']) && empty($after['d_note'])) {
        //     return;
        // }

        $noteText = trim($after['d_pickupnote'] ?? $after['d_note'] ?? '');

        // if (strlen($noteText) <= 1) return;

        $dailyuid     = mysqli_real_escape_string($connect, $after['d_uid']);
        $customeruid  = mysqli_real_escape_string($connect, $after['d_customeruid'] ?? '');
        $memberuid    = mysqli_real_escape_string($connect, $after['d_driveruid'] ?? '');
        $noteEsc      = mysqli_real_escape_string($connect, $noteText);
        $now14        = date('YmdHis');
        $level        = mysqli_real_escape_string($connect, $after['d_level'] ?? 9);

        if ($noteText === '') {
            mysqli_query($connect, "
                DELETE FROM tbl_note
                WHERE n_dailyuid = '{$dailyuid}'
                LIMIT 1
            ");
            return;
        }

        // 기존 note 존재 여부 확인
        $qr = mysqli_query($connect, "
            SELECT n_uid 
            FROM tbl_note
            WHERE n_dailyuid = '{$dailyuid}'
            LIMIT 1
        ");

        $existing = mysqli_fetch_assoc($qr);

        if ($existing) {
            // UPDATE
            mysqli_query($connect, "
                UPDATE tbl_note
                SET 
                    n_memberuid = '{$memberuid}',
                    n_customeruid = '{$customeruid}',
                    n_type = 'D',
                    n_level = {$level},
                    n_view = 1,
                    n_note = '{$noteEsc}',
                    n_createddate = '{$now14}'
                WHERE n_dailyuid = '{$dailyuid}'
                LIMIT 1
            ");
        } else {
            // INSERT
            mysqli_query($connect, "
                INSERT INTO tbl_note (
                    n_memberuid,
                    n_customeruid,
                    n_dailyuid,
                    n_type,
                    n_level,
                    n_view,
                    n_note,
                    n_createddate
                ) VALUES (
                    '{$memberuid}',
                    '{$customeruid}',
                    '{$dailyuid}',
                    'D',
                    {$level},
                    1,
                    '{$noteEsc}',
                    '{$now14}'
                )
            ");
        }
    }

    private function handlePickupCleanup(?array $before, array $after): void
    {
        $connect = $GLOBALS['conn'];

        $beforeQty = (int)($before['d_quantity'] ?? 0);
        $afterQty  = (int)($after['d_quantity'] ?? 0);

        $status = $after['d_status'] ?? '';
        $visit  = $after['d_visit'] ?? '';

        // 최초 pickup
        $isFirstPickup = ($beforeQty < 10 && $afterQty >= 10);

        if (!$isFirstPickup) return;
        if ($status !== 'F' || $visit !== 'Y') return;

        $customerUid = mysqli_real_escape_string($connect, $after['d_customeruid']);
        $accountno   = mysqli_real_escape_string($connect, $after['d_accountno']);
        $today       = date('Ymd');
        $jobtype     = mysqli_real_escape_string($connect, $after['d_jobtype'] ?? 'UCO');

        // =========================
        // 삭제 대상 조회
        // =========================
        $res = mysqli_query($connect, "
            SELECT d_orderdate, d_accountno, d_jobtype
            FROM tbl_daily
            WHERE d_customeruid = '{$customerUid}'
                AND d_orderdate > '{$today}'
                AND d_status = 'A'
                AND d_jobtype = '{$jobtype}'
        ");

        if (!$res) {
            throw new Exception(mysqli_error($connect));
        }

        // =========================
        // 하나씩 deleteDaily 호출
        // =========================
        while ($row = mysqli_fetch_assoc($res)) {

            try {
                $this->deleteDaily(
                    $row['d_orderdate'],
                    $row['d_accountno'],
                    $row['d_jobtype'],
                    ['in_tx' => true]
                );

            } catch (Exception $e) {
                // 여기서 실패하면 전체 rollback 유도
                throw $e;
            }
        }

        error_log("[PickupCleanup] ERP-safe delete completed for customer={$accountno}");
    }

    private function handlePickupReset(?array $before, array $after): void
    {
        $connect = $GLOBALS['conn'];

        $beforeQty = (int)($before['d_quantity'] ?? 0);
        $afterQty  = (int)($after['d_quantity'] ?? 0);

        $status = $after['d_status'] ?? '';
        $visit  = $after['d_visit'] ?? '';

        $isFirstPickup = ($beforeQty < 10 && $afterQty >= 10);

        if (!$isFirstPickup) return;
        if ($status !== 'F' || $visit !== 'Y') return;

        $customerUid = mysqli_real_escape_string($connect, $after['d_customeruid']);
        $accountno   = mysqli_real_escape_string($connect, $after['d_accountno']);
        $orderdate   = mysqli_real_escape_string($connect, $after['d_orderdate']);

        $sql = "
            UPDATE tbl_customer
            SET 
                c_fullquantity = 0,
                c_lastpickupdate = '{$orderdate}',
                c_lastpickupquantity = '{$afterQty}'
            WHERE c_uid = '{$customerUid}'
        ";

        mysqli_query($connect, $sql);

        error_log("[PickupReset] customer={$accountno} reset fullquantity + lastpickupdate={$orderdate}");
    }

    public function deleteDaily(string $orderdate, string $accountno, string $jobtype, array $opt = [])
    {
        $connect = $GLOBALS['conn'] ?? null;
        if (!$connect) {
            throw new Exception("DB connection not found");
        }

        $inTx = !empty($opt['in_tx']);

        // =========================
        // KEY NORMALIZE
        // =========================
        $orderdate = preg_replace('/[^0-9]/', '', $orderdate);
        if (strlen($orderdate) !== 8) {
            throw new Exception("invalid orderdate");
        }

        $accountno = mysqli_real_escape_string($connect, trim($accountno));
        if ($accountno === '') {
            throw new Exception("invalid accountno");
        }

        $jobtype = mysqli_real_escape_string($connect, trim($jobtype));
        if ($jobtype === '') {
            $jobtype = 'UCO';
        }

        if (!$inTx) {
            mysqli_query($connect, "START TRANSACTION");
        }

        try {

            // =========================
            // 1. 기존 데이터 조회
            // =========================
            $res = mysqli_query($connect, "
                SELECT *
                FROM tbl_daily
                WHERE d_orderdate='{$orderdate}'
                    AND d_accountno='{$accountno}'
                    AND d_jobtype = '{$jobtype}'
                LIMIT 1
            ");
            if (!$res) throw new Exception(mysqli_error($connect));

            $existing = mysqli_fetch_assoc($res);

            if (!$existing) {
                throw new Exception("Daily record not found");
            }

            // =========================
            // 2. ERP OUTBOX 기록
            // =========================
            $payload = mysqli_real_escape_string(
                $connect,
                json_encode([
                    'key'=>[
                        'd_orderdate'=>$orderdate,
                        'd_accountno'=>$accountno,
                        'd_jobtype'  =>$jobtype
                    ],
                    'before'=>$existing
                ], JSON_UNESCAPED_UNICODE)
            );

            if (!mysqli_query($connect, "
                INSERT INTO tbl_erp_outbox
                (eo_event_type, eo_key_date, eo_key_accountno, eo_key_jobtype, eo_payload, eo_status, eo_created_at)
                VALUES
                ('DAILY_DELETED','{$orderdate}','{$accountno}','{$jobtype}','{$payload}','PENDING',NOW())
            ")) {
                throw new Exception(mysqli_error($connect));
            }

            // =========================
            // 3. 물리 삭제
            // =========================
            if (!mysqli_query($connect, "
                DELETE FROM tbl_daily
                WHERE d_orderdate='{$orderdate}'
                    AND d_accountno='{$accountno}'
                    AND d_jobtype = '{$jobtype}'
                LIMIT 1
            ")) {
                throw new Exception(mysqli_error($connect));
            }

            if (mysqli_affected_rows($connect) === 0) {
                throw new Exception("Delete failed");
            }

            // =========================
            // AFTER WORK 중앙화
            // =========================
            $this->runAfterDelete($existing, $opt);

            // =========================
            // COMMIT
            // =========================
            if (!$inTx) {
                mysqli_query($connect, "COMMIT");
            }

            return true;

        } catch (Exception $e) {

            if (!$inTx) {
                mysqli_query($connect, "ROLLBACK");
            }

            throw $e;
        }
    }

    private function reEvaluateGhost(int $customerUid): void
    {
        $connect = $GLOBALS['conn'];

        $res = mysqli_query($connect, "
            SELECT d_quantity, d_lastpickupquantity
            FROM tbl_daily
            WHERE d_customeruid = '{$customerUid}'
                AND d_status = 'F'
            ORDER BY d_orderdate DESC
            LIMIT 1
        ");

        $row = mysqli_fetch_assoc($res);

        if (!$row) {
            // 데이터 없으면 ghost 였던 것 해제
            mysqli_query($connect, "
                UPDATE tbl_customer
                SET c_is_top = ''
                WHERE c_uid = '{$customerUid}'
                    and c_is_top = 'G'
            ");
            return;
        }

        $thisQuantity = (int)$row['d_quantity'];
        $lastQuantity = (int)$row['d_lastpickupquantity'];

        if ($thisQuantity < 10 && $lastQuantity < 10) {
            mysqli_query($connect, "
                UPDATE tbl_customer
                SET c_is_top = 'G'
                WHERE c_uid = '{$customerUid}'
            ");
        } else {
            mysqli_query($connect, "
                UPDATE tbl_customer
                SET c_is_top = ''
                WHERE c_uid = '{$customerUid}'
                    and c_is_top = 'G'
            ");
        }
    }

    private function recalcCustomerFromDailyHistory(string $customerUid): void
    {
        $connect = $GLOBALS['conn'];
        $customerUidEsc = mysqli_real_escape_string($connect, $customerUid);

        // 0) "오늘 포함 이후" 오더장 존재 여부 체크 (남아있으면 리셋 금지)
        $today = date('Ymd');

        $resFuture = mysqli_query($connect, "
            SELECT 1
            FROM tbl_daily
            WHERE d_customeruid = '{$customerUidEsc}'
                AND d_orderdate >= '{$today}'
                AND d_status = 'A'
            LIMIT 1
        ");
        if (!$resFuture) {
            throw new Exception(mysqli_error($connect));
        }
        $hasFutureOrder = (bool)mysqli_fetch_assoc($resFuture);

        // 1) 마지막 완료 주문(F) 기준으로 last pickup 재계산
        $res = mysqli_query($connect, "
            SELECT 
                d_orderdate,
                d_quantity,
                d_sludge,
                d_paymenttype,
                d_paystatus
            FROM tbl_daily
            WHERE d_customeruid = '{$customerUidEsc}'
                AND d_status = 'F'
            ORDER BY d_orderdate DESC
            LIMIT 1
        ");
        if (!$res) {
            throw new Exception(mysqli_error($connect));
        }

        $last = mysqli_fetch_assoc($res);

        $lastPickupDate = '';
        $lastPickupQty  = 0;
        $lastSludge     = '';
        $lastPaidDate   = '';

        if ($last) {
            $lastPickupDate = $last['d_orderdate'];
            $lastPickupQty  = (int)$last['d_quantity'];
            $lastSludge     = (string)$last['d_sludge'];

            if ($last['d_paymenttype'] === 'CA' && $last['d_paystatus'] === 'P') {
                $lastPaidDate = $last['d_orderdate'];
            }
        }

        // 2) 마지막 주문이 cash paid가 아니면, cash paid 마지막을 다시 찾음
        if ($lastPaidDate === '') {

            $res2 = mysqli_query($connect, "
                SELECT d_orderdate
                FROM tbl_daily
                WHERE d_customeruid = '{$customerUidEsc}'
                    AND d_status = 'F'
                    AND d_paymenttype = 'CA'
                    AND d_paystatus = 'P'
                ORDER BY d_orderdate DESC
                LIMIT 1
            ");
            if (!$res2) {
                throw new Exception(mysqli_error($connect));
            }

            $paid = mysqli_fetch_assoc($res2);
            if ($paid) {
                $lastPaidDate = $paid['d_orderdate'];
            }
        }

        // 3) UPDATE 구성
        $setParts = [];
        $setParts[] = "c_lastpickupdate=" . ($lastPickupDate ? "'{$lastPickupDate}'" : "''");
        $setParts[] = "c_lastpickupquantity='{$lastPickupQty}'";
        $setParts[] = "c_lastpaiddate=" . ($lastPaidDate ? "'{$lastPaidDate}'" : "''");
        $setParts[] = "c_sludge='" . mysqli_real_escape_string($connect, $lastSludge) . "'";

        // 4) orderflag/orderdate 리셋 정책 반영
        // - 오늘 포함 이후 오더장이 남아있으면 유지 (아무것도 안함)
        // - 없으면 리셋
        if (!$hasFutureOrder) {
            $setParts[] = "c_orderdate=''";
            $setParts[] = "c_orderflag=0";
        }

        $sql = "
            UPDATE tbl_customer
            SET " . implode(",", $setParts) . "
            WHERE c_uid = '{$customerUidEsc}'
            LIMIT 1
        ";

        if (!mysqli_query($connect, $sql)) {
            throw new Exception(mysqli_error($connect));
        }
    }

    /* ===============================
     * 공통 JSON 응답
     * =============================== */

    private function jsonSuccess($data = [])
    {
        return json_encode([
            'result' => 'success',
            'data'   => $data
        ]);
    }

    private function jsonError($msg)
    {
        return json_encode([
            'result' => 'error',
            'message'=> $msg
        ]);
    }

}