<?php

trait InstallAPI {

    protected $dailyService;
    public function __construct() {
        $this->dailyService = new DailyService();
    }

    public function search_customer() {
        $keyword = trim($_POST['keyword'] ?? '');
        if ($keyword === '') {
            echo $this->json([]);
            return;
        }

        $keyword = addslashes($keyword);
        $sql = "
            SELECT
                c.c_uid,
                c.c_accountno,
                c.c_name,
                c.c_address,
                c.c_city,
                c.c_postal,
                c.c_phone
            FROM tbl_customer c
            WHERE
                (
                    c.c_accountno LIKE '%{$keyword}%'
                    OR c.c_name LIKE '%{$keyword}%'
                )
                AND NOT EXISTS (
                    SELECT 1
                    FROM tbl_install_waitlist iw
                    WHERE iw.iw_customer_uid = c.c_uid
                    AND iw.iw_status not in ('DELETED', 'COMPLETED')
                )
            ORDER BY c.c_name
            LIMIT 20
        ";
        $res = qry($sql);

        $rows = [];
        while ($row = mysqli_fetch_assoc($res)) {
            $rows[] = $row;
        }
        echo $this->json($rows);
    }

    public function install_wait_create_draft() {
        $c_uid        = $_POST['c_uid'] ?? 0;
        $job_type     = $_POST['job_type'] ?? '';
        $driver_uid   = $_POST['driver_uid'] ?? '';
        $member_uid   = $_POST['member_uid'] ?? '';
        $request_note = $_POST['request_note'] ?? '';

        if (!$c_uid || !$job_type) {
            echo $this->json(['ok' => 0, 'msg' => 'invalid params']);
            return;
        }

        $sql = "
            INSERT INTO tbl_install_waitlist (
                iw_customer_uid,
                iw_work_type,
                iw_accountno,
                iw_customer_name,
                iw_address,
                iw_city,
                iw_postal,
                iw_phone,
                iw_lat,
                iw_lng,
                iw_status,
                iw_request_source,
                iw_request_by,
                iw_request_note,
                iw_request_at,
                iw_created_by,
                iw_created_at
            )
            SELECT
                '{$c_uid}'                                AS iw_customer_uid,
                '".addslashes($job_type)."'               AS iw_work_type,
                c.c_accountno                             AS iw_accountno,
                c.c_name                                  AS iw_customer_name,
                c.c_address                               AS iw_address,
                c.c_city                                  AS iw_city,
                c.c_postal                                AS iw_postal,
                c.c_phone                                 AS iw_phone,
                c.c_geolat                                AS iw_lat,
                c.c_geolon                                AS iw_lng,
                'DRAFT'                                   AS iw_status,
                'MAP'                                     AS iw_request_source,
                '{$driver_uid}'                           AS iw_request_by,
                '".addslashes($request_note)."'           AS iw_request_note,
                NOW()                                     AS iw_request_at,
                '{$member_uid}'                           AS iw_created_by,
                NOW()                                     AS iw_created_at
            FROM tbl_customer c
            WHERE c.c_uid = '{$c_uid}'
                AND NOT EXISTS (
                    SELECT 1
                    FROM tbl_install_waitlist iw
                    WHERE iw.iw_customer_uid = c.c_uid
                    AND iw.iw_status NOT IN ('DELETED','COMPLETED')
                )
        ";

        qry($sql);

        if (mysqli_affected_rows($GLOBALS['conn']) == 0) {
            echo $this->json([
                'ok' => 0,
                'msg' => '이미 waitlist에 존재합니다.'
            ]);
            return;
        }

        $iw_uid = mysqli_insert_id($GLOBALS['conn']); // or global db handle

        echo $this->json([
            'ok'     => 1,
            'iw_uid' => $iw_uid
        ]);
    }

    public function install_wait_update_draft() {

        $iw_uid = intval($_POST['iw_uid'] ?? 0);
        $job_type = addslashes($_POST['job_type'] ?? '');
        $request_note = addslashes($_POST['request_note'] ?? '');

        if (!$iw_uid) {
            echo $this->json(['ok'=>0,'msg'=>'invalid iw_uid']);
            return;
        }

        // DRAFT 상태만 수정 가능
        $sqlCheck = "
            SELECT iw_status
            FROM tbl_install_waitlist
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ";
        $r = qry($sqlCheck);
        $row = mysqli_fetch_assoc($r);

        if (!$row || $row['iw_status'] !== 'DRAFT') {
            echo $this->json(['ok'=>0,'msg'=>'cannot modify this request']);
            return;
        }

        $sql = "
            UPDATE tbl_install_waitlist
            SET
                iw_work_type = '{$job_type}',
                iw_request_note = '{$request_note}',
                iw_updated_at = NOW()
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ";

        qry($sql);

        echo $this->json(['ok'=>1]);
    }

    public function install_wait_delete_draft() {

        $iw_uid = intval($_POST['iw_uid'] ?? 0);

        if (!$iw_uid) {
            echo $this->json(['ok'=>0]);
            return;
        }

        // 상태 확인
        $sqlCheck = "
            SELECT iw_status
            FROM tbl_install_waitlist
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ";

        $r = qry($sqlCheck);
        $row = mysqli_fetch_assoc($r);

        if (!$row || $row['iw_status'] !== 'DRAFT') {
            echo $this->json(['ok'=>0, 'msg'=>'cannot delete']);
            return;
        }

        // Soft delete
        $sql = "
            UPDATE tbl_install_waitlist
            SET iw_status = 'DELETED',
                iw_deleted_at = NOW()
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ";

        qry($sql);

        echo $this->json(['ok'=>1]);
    }



    public function install_wait_get_active() {

        $c_uid = intval($_POST['c_uid'] ?? 0);
        if (!$c_uid) {
            echo $this->json(['ok'=>0,'msg'=>'invalid params']);
            return;
        }

        // DRAFT/WAITING/ASSIGNED 중 최신 1개 (원하는 정책이면 ORDER를 상태 우선으로 바꿔도 됨)
        $sql = "
            SELECT
            iw.iw_uid,
            iw.iw_status,
            iw.iw_work_type,
            iw.iw_request_note,
            CASE 
                WHEN iw.iw_request_by = 'Customer' THEN 'Customer'
                ELSE CONCAT(IFNULL(m.m_firstname,''), ' ', IFNULL(m.m_lastname,''))
            END AS iw_request_by_name,
            iw.iw_request_by,
            iw.iw_request_at,
            iw.iw_created_at,

            di.di_install_date,
            di.di_install_time

            FROM tbl_install_waitlist iw
            LEFT JOIN tbl_daily_install di
                ON di.di_wait_uid = iw.iw_uid
            LEFT JOIN tbl_member m
                ON iw.iw_created_by = m.m_uid
            WHERE iw.iw_customer_uid = '{$c_uid}'
                AND iw.iw_status IN ('DRAFT','WAITING','ASSIGNED')
            ORDER BY iw.iw_uid DESC, di.di_uid DESC
            LIMIT 1
        ";

        $r = qry($sql);
        if ($row = mysqli_fetch_assoc($r)) {
            // (A) iw_uid에 연결된 사진 조회해서 붙이기
            $iw_uid = (int)$row['iw_uid'];
            $photos = [];

            $sql_img = "
                SELECT i_uid, i_filename, i_filepath
                FROM tbl_customer_image
                WHERE i_status = 'A'
                AND i_type = 'install_order'
                AND i_sourceuid = '{$iw_uid}'
                ORDER BY i_uid ASC
            ";

            $ri = qry($sql_img);

            while ($img = mysqli_fetch_assoc($ri)) {

                $imgPath = $img['i_filepath'] . $img['i_filename'];

                $photos[] = [
                    'i_uid'   => (int)$img['i_uid'],
                    'imgPath' => $imgPath
                ];
            }

            $row['photos'] = $photos;

            echo $this->json([
                'ok'     => 1,
                'exists' => 1,
                'data'   => $row
            ]);
            return;
        }

        echo $this->json(['ok'=>1,'exists'=>0]);
    }


    public function install_wait_upsert_containers() {

        $iw_uid = intval($_POST['iw_uid'] ?? 0);
        $items  = $_POST['items'] ?? [];
        $requestAction = $_POST['action'] ?? null;

        if (!$iw_uid) {
            echo $this->json(['ok' => 0, 'msg' => 'no iw_uid']);
            return;
        }

        qry("START TRANSACTION");

        try {

            // 기존 컨테이너 전부 삭제
            qry("
                DELETE FROM tbl_install_waitlist_container
                WHERE iwc_wait_uid = '{$iw_uid}' AND iwc_action = '{$requestAction}'
            ");

            // 재 insert
            if (is_array($items)) {
                foreach ($items as $it) {

                    // item action 따로 사용
                    $itemAction = addslashes($it['action'] ?? '');
                    if (!$itemAction) {
                        throw new Exception('missing item action');
                    }

                    $type       = addslashes($it['type'] ?? '');
                    $capacity   = isset($it['capacity']) && $it['capacity'] !== ''
                                    ? intval($it['capacity'])
                                    : 'NULL';
                    $is_used    = addslashes($it['is_used'] ?? 'N');
                    $owner_type = addslashes($it['owner_type'] ?? 'C');
                    $has_lock   = addslashes($it['has_lock'] ?? 'N');
                    $has_wheel  = addslashes($it['has_wheel'] ?? 'N');
                    $has_frame  = addslashes($it['has_frame'] ?? 'N');

                    $sql = "
                        INSERT INTO tbl_install_waitlist_container
                        SET
                            iwc_wait_uid       = '{$iw_uid}',
                            iwc_action         = '{$itemAction}',
                            iwc_type           = '{$type}',
                            iwc_capacity       = {$capacity},
                            iwc_is_used        = '{$is_used}',
                            iwc_owner_type     = '{$owner_type}',
                            iwc_has_lock       = '{$has_lock}',
                            iwc_has_wheel      = '{$has_wheel}',
                            iwc_has_woodframe  = '{$has_frame}'
                    ";

                    if (!qry($sql)) {
                        throw new Exception('insert failed');
                    }
                }
            }

            qry("COMMIT");

        } catch (Exception $e) {

            qry("ROLLBACK");

            echo $this->json([
                'ok' => 0,
                'msg' => $e->getMessage()
            ]);
            return;
        }

        echo $this->json(['ok' => 1]);
    }


    public function get_customer_containers_active() {

        $c_uid = intval($_POST['c_uid'] ?? 0);
        if (!$c_uid) {
            echo $this->json([]);
            return;
        }

        $sql = "
            SELECT
                cc_uid,
                cc_type,
                cc_capacity,
                cc_owner_type,
                cc_is_used,
                cc_has_lock,
                cc_has_wheel,
                cc_has_woodframe
            FROM tbl_customer_container
            WHERE cc_customer_uid = '{$c_uid}'
                AND cc_status = 'A'
            ORDER BY cc_type, cc_capacity
        ";

        $res = qry($sql);

        $rows = [];
        while ($row = mysqli_fetch_assoc($res)) {
            $rows[] = $row;
        }

        echo $this->json($rows);
    }

    public function install_wait_delete() {

        $iw_uid = intval($_POST['iw_uid'] ?? 0);

        if (!$iw_uid) {
            echo $this->json(['ok' => 0, 'msg' => 'ID is missing']);
            return;
        }

        // qry() + fetch
        $rs = qry("SELECT iw_status FROM tbl_install_waitlist WHERE iw_uid = '{$iw_uid}'");
        $row = mysqli_fetch_assoc($rs);

        if (!$row) {
            echo $this->json(['ok' => 0, 'msg' => 'Item not found']);
            return;
        }

        qry("START TRANSACTION");
        // 지우지는 말자. 
        // $ok1 = qry("DELETE FROM tbl_install_waitlist_container WHERE iwc_wait_uid = '{$iw_uid}'");
        if (!$this->update_waitlist_status($iw_uid, 'DELETED')) {
            qry("ROLLBACK");
            $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]);
        }

        qry("COMMIT");
        echo $this->json(['ok' => 1]);
    }


    public function install_wait_complete() {

        $iw_uid = intval($_POST['iw_uid'] ?? 0);
        if (!$iw_uid) {
            echo $this->json(['ok' => 0, 'msg' => 'no iw_uid']);
            return;
        }

        // set parameters
        $request_at   = $_POST['request_at']   ?? null;
        $due_date     = $_POST['due_date']     ?? null;
        $request_by   = $_POST['request_by']   ?? null;
        $request_note = $_POST['request_note'] ?? null;
        $cs_note      = $_POST['cs_note']      ?? null;
        $work_note    = $_POST['work_note']      ?? null;

        $person_cnt   = $_POST['person_cnt']   ?? null; // null or 2
        $person_reason= $_POST['person_reason'] ?? null;

        $scheduled    = intval($_POST['scheduled'] ?? 0);
        $cycle        = intval($_POST['scheduled_cycle'] ?? 0);
        $lock_date    = intval($_POST['lock_date'] ?? 0);

        $need_oil     = $_POST['need_oil']   ?? 0; // 0 or 1

        // Two-person OFF = NULL
        if (!$person_cnt) {
            $person_cnt    = null;
            $person_reason = null;
        }

        // Recurring OFF = 0
        if (!$scheduled) {
            $cycle = 0;
        }

        qry("
            UPDATE tbl_install_waitlist
            SET
                iw_request_at          = ".($request_at ? "'{$request_at}'" : "NOW()").",
                iw_requested_due_date  = ".($due_date ? "'{$due_date}'" : "NULL").",
                iw_lock_date           = '{$lock_date}',
                iw_request_by          = ".($request_by ? "'{$request_by}'" : "NULL").",
                iw_request_note        = ".($request_note ? "'".addslashes($request_note)."'" : "NULL").",
                iw_cs_note             = ".($cs_note ? "'".addslashes($cs_note)."'" : "NULL").",
                iw_work_note           = ".($work_note ? "'".addslashes($work_note)."'" : "NULL").",

                iw_required_person_cnt = ".($person_cnt !== null ? intval($person_cnt) : "NULL").",
                iw_multi_person_reason = ".($person_reason ? "'".addslashes($person_reason)."'" : "NULL").",
                iw_scheduled            = '{$scheduled}',
                iw_scheduled_cycle      = '{$cycle}',
                iw_need_oil_pickup      = '{$need_oil}',

                iw_status               = 'WAITING'
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ");

        echo $this->json(['ok' => 1]);
    }

    // 수정을 위한
    public function get_install_wait_detail() {
        $iw_uid = intval($_POST['iw_uid'] ?? 0);

        $rs = qry("
            SELECT *
            FROM tbl_install_waitlist
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ");
        $row = mysqli_fetch_assoc($rs);

        echo json_encode($row ?: null);
    }

    public function get_install_wait_containers() {
        $iw_uid = intval($_POST['iw_uid'] ?? 0);

        $rows = [];
        $rs = qry("
            SELECT *
            FROM tbl_install_waitlist_container
            WHERE iwc_wait_uid = '{$iw_uid}'
            ORDER BY iwc_action
        ");
        while ($r = mysqli_fetch_assoc($rs)) {
            $rows[] = $r;
        }

        echo json_encode($rows);
    }


    // =========================================
    // install wait list - right
    // =========================================
    public function inq_daily_install_points() {

        $di_install_date = trim($_POST['di_install_date'] ?? '');
        $di_driver_uid   = intval($_POST['di_driver_uid'] ?? 0);

        if (!$di_install_date || !$di_driver_uid) {
            echo $this->json([]);
            return;
        }

        // install.php 스타일: addslashes / qry
        $date = addslashes($di_install_date);

        $sql = "
            SELECT
                di.di_uid,
                di.di_order_seq,
                di.di_driver_uid,
                ifnull(mem.m_initial,'') as di_customer_driver_initial,
                di.di_install_date,
                di.di_install_time,
                di.di_lock_date,
                di.di_wait_uid,
                di.di_customer_uid,
                di.di_accountno,
                di.di_phone,
                di.di_address,
                di.di_city,
                di.di_postal,
                di.di_lat,
                di.di_lng,
                di.di_work_type,
                di.di_status,
                di.di_request_note,
                di.di_cs_note,
                di.di_work_note,
                c.c_name AS di_customer_name,
                SUM(
                CASE
                    WHEN dic.dic_action = 'PICKUP'
                    AND dic.dic_type = 'D'
                    THEN 1 ELSE 0
                END
                ) AS pickup_d_cnt,
                SUM(
                CASE
                    WHEN dic.dic_action = 'PICKUP'
                    AND dic.dic_type <> 'D'
                    THEN 1 ELSE 0
                END
                ) AS pickup_b_cnt,
                SUM(
                CASE
                    WHEN dic.dic_action = 'INSTALL'
                    AND dic.dic_type = 'D'
                    THEN 1 ELSE 0
                END
                ) AS install_d_cnt,
                SUM(
                CASE
                    WHEN dic.dic_action = 'INSTALL'
                    AND dic.dic_type <> 'D'
                    THEN 1 ELSE 0
                END
                ) AS install_b_cnt

            FROM tbl_daily_install di
            LEFT JOIN tbl_member mem
                ON di.di_customer_driver_uid = mem.m_uid
            LEFT JOIN tbl_customer c
                ON c.c_uid = di.di_customer_uid
            LEFT JOIN tbl_daily_install_container dic
                ON dic.dic_di_uid = di.di_uid
            WHERE di.di_status = 'A'
                AND di.di_install_date = '{$date}'
                AND di.di_driver_uid = '{$di_driver_uid}'
            GROUP BY di.di_uid
            ORDER BY di.di_order_seq ASC

        ";

        $res = qry($sql);

        $rows = [];
        while ($row = mysqli_fetch_assoc($res)) {
            $rows[] = $row;
        }

        echo $this->json($rows);
    }

    private function insert_daily_install_from_waitlist($iw_uid, $di_driver_uid, $di_install_date, $di_order_seq) {

        $created_by = mysqli_real_escape_string($GLOBALS['conn'], $_SESSION['ss_UID'] ?? '');
        $sql = "
            INSERT INTO tbl_daily_install (
                di_order_seq,
                di_driver_uid,
                di_install_date,
                di_lock_date,
                di_wait_uid,
                di_customer_uid,
                di_customer_name,
                di_customer_driver_uid,
                di_accountno,
                di_phone,
                di_address,
                di_city,
                di_postal,
                di_lat,
                di_lng,
                di_work_type,
                di_oil_pickup,
                di_request_note,
                di_work_note,
                di_cs_note,
                di_created_by
            )
            SELECT
                '{$di_order_seq}',
                '{$di_driver_uid}',
                '{$di_install_date}',
                iw.iw_lock_date,
                iw.iw_uid,
                iw.iw_customer_uid,
                iw.iw_customer_name,
                c.c_driveruid,
                iw.iw_accountno,
                iw.iw_phone,
                iw.iw_address,
                iw.iw_city,
                iw.iw_postal,
                iw.iw_lat,
                iw.iw_lng,
                iw.iw_work_type,
                iw.iw_need_oil_pickup,
                iw.iw_request_note,
                iw.iw_work_note,
                iw.iw_cs_note,
                '{$created_by}'
            FROM tbl_install_waitlist iw
            LEFT JOIN tbl_customer c
                ON c.c_uid = iw.iw_customer_uid
            WHERE iw_uid = '{$iw_uid}'
        ";

        if (!qry($sql)) {
            return false;
        }

        return mysqli_insert_id($GLOBALS['conn']);
    }

    private function insert_daily_install_container_from_waitlist($di_uid, $iw_uid) {

        $sql = "
            INSERT INTO tbl_daily_install_container (
                dic_di_uid,
                dic_action,
                dic_type,
                dic_capacity,
                dic_is_used,
                dic_has_lock,
                dic_has_wheel,
                dic_has_woodframe,
                dic_owner_type
            )
            SELECT
                '{$di_uid}',
                iwc_action,
                iwc_type,
                iwc_capacity,
                iwc_is_used,
                iwc_has_lock,
                iwc_has_wheel,
                iwc_has_woodframe,
                iwc_owner_type
            FROM tbl_install_waitlist_container
            WHERE iwc_wait_uid = '{$iw_uid}'
        ";

        // 0 row여도 정상
        return qry($sql) !== false;
    }

    private function update_waitlist_status($iw_uid, $status) {

        $status = mysqli_real_escape_string($GLOBALS['conn'], $status);

        $sql = "
            UPDATE tbl_install_waitlist
            SET iw_status = '{$status}'
            WHERE iw_uid = '{$iw_uid}'
        ";

        return qry($sql);
    }

    public function confirm_daily_install() {

        $trial_rows      = $_POST['trial_rows'] ?? [];
        $existing_rows   = $_POST['existing_rows'] ?? [];
        $di_driver_uid   = intval($_POST['di_driver_uid'] ?? 0);
        $di_install_date = $_POST['di_install_date'] ?? null;

        $fail = function($stage, $msg, $extra = []) {
            echo $this->json(array_merge([
                'ok'    => 0,
                'stage' => $stage,
                'msg'   => $msg,
                'db'    => mysqli_error($GLOBALS['conn'])
            ], $extra));
            exit;
        };

        if (!$di_driver_uid || !$di_install_date) {
            $fail('validate', 'invalid params');
        }

        if ((!is_array($trial_rows) || !count($trial_rows)) && (!is_array($existing_rows) || !count($existing_rows))) {
            $fail('validate', 'nothing to confirm');
        }

        /* =======================================================
        0. 기존 DB max seq 가져오기
        ======================================================= */
        $qr_max = qry("
            SELECT MAX(di_order_seq) AS max_seq
            FROM tbl_daily_install
            WHERE di_driver_uid = '{$di_driver_uid}'
                AND di_install_date = '{$di_install_date}'
        ");

        $row_max = fetch_array($qr_max);
        $base_seq = intval($row_max['max_seq'] ?? 0);

        /* =======================================================
        1. 전체 rows 합치고 seq 재정렬
        ======================================================= */
        $all = array_merge(
            is_array($existing_rows) ? $existing_rows : [],
            is_array($trial_rows) ? $trial_rows : []
        );

        usort($all, function($a, $b){
            return intval($a['di_order_seq'] ?? 0) <=> intval($b['di_order_seq'] ?? 0);
        });

        $has_existing = is_array($existing_rows) && count($existing_rows) > 0;
        $has_trial    = is_array($trial_rows) && count($trial_rows) > 0;

        $base_seq = 0;

        if ($has_existing) {
            $seq_values = array_map(function($r){
                return intval($r['di_order_seq'] ?? 0);
            }, $existing_rows);

            $min_seq = min(array_filter($seq_values));
            $base_seq = $min_seq - 1;
        } else if ($has_trial) {
            $qr_max = qry("
                SELECT MAX(di_order_seq) AS max_seq
                FROM tbl_daily_install
                WHERE di_driver_uid = '{$di_driver_uid}'
                    AND di_install_date = '{$di_install_date}'
            ");

            $row_max = fetch_array($qr_max);
            $base_seq = intval($row_max['max_seq'] ?? 0);
        }

        $seq = 1;
        foreach($all as &$r){
            $r['di_order_seq'] = $base_seq + $seq++;
        }
        unset($r);

        /* =======================================================
        2. 다시 existing / trial 분리
        ======================================================= */
        $existing_map = [];
        foreach($existing_rows as $row){
            $existing_map[intval($row['di_uid'])] = true;
        }

        $new_existing_rows = [];
        $new_trial_rows = [];

        foreach($all as $r){
            if(isset($existing_map[intval($r['di_uid'] ?? 0)])){
                $new_existing_rows[] = $r;
            }else{
                $new_trial_rows[] = $r;
            }
        }

        qry("START TRANSACTION");

        /* =======================================================
        A) 기존 daily_install 순서 업데이트
        ======================================================= */
        foreach ($new_existing_rows as $row) {

            $di_uid       = intval($row['di_uid'] ?? 0);
            $di_order_seq = intval($row['di_order_seq'] ?? 0);

            if (!$di_uid || !$di_order_seq) {
                qry("ROLLBACK");
                $fail('validate_existing_row', 'invalid existing row', ['row'=>$row]);
            }

            $sql = "
                UPDATE tbl_daily_install
                SET di_order_seq = '{$di_order_seq}'
                WHERE di_uid = '{$di_uid}'
                    AND di_driver_uid = '{$di_driver_uid}'
                    AND di_install_date = '{$di_install_date}'
                LIMIT 1
            ";

            if (!qry($sql)) {
                qry("ROLLBACK");
                $fail('update_existing_order', 'failed', ['di_uid'=>$di_uid]);
            }
        }

        /* =======================================================
        B) trial(waitlist) → INSERT
        ======================================================= */
        foreach ($new_trial_rows as $row) {

            $iw_uid       = intval($row['iw_uid'] ?? 0);
            $di_order_seq = intval($row['di_order_seq'] ?? 0);

            if (!$iw_uid || !$di_order_seq) {
                qry("ROLLBACK");
                $fail('validate_trial_row', 'invalid trial row', ['row'=>$row]);
            }

            $di_uid = $this->insert_daily_install_from_waitlist(
                $iw_uid,
                $di_driver_uid,
                $di_install_date,
                $di_order_seq
            );

            if (!$di_uid) {
                qry("ROLLBACK");
                $fail('insert_daily_install', 'failed', ['iw_uid'=>$iw_uid]);
            }

            if (!$this->insert_daily_install_container_from_waitlist($di_uid, $iw_uid)) {
                qry("ROLLBACK");
                $fail('insert_container', 'failed', ['iw_uid'=>$iw_uid]);
            }

            if (!$this->update_waitlist_status($iw_uid, 'ASSIGNED')) {
                qry("ROLLBACK");
                $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]);
            }
        }

        qry("COMMIT");
        echo $this->json(['ok'=>1]);
        exit;
    }


    public function unassign_daily_install() {

        $di_uids = $_POST['di_uids'] ?? [];
        if (!is_array($di_uids) || !count($di_uids)) {
            echo $this->json(['ok'=>0,'msg'=>'invalid di_uids']);
            return;
        }

        // 숫자만 필터
        $di_uids = array_map('intval', $di_uids);
        $di_uids = array_filter($di_uids);

        if (!count($di_uids)) {
            echo $this->json(['ok'=>0,'msg'=>'empty di_uids']);
            return;
        }

        $in = implode(',', $di_uids);

        $fail = function($stage, $msg, $extra = []) {
            echo $this->json(array_merge([
                'ok'    => 0,
                'stage' => $stage,
                'msg'   => $msg,
                'db'    => mysqli_error($GLOBALS['conn'])
            ], $extra));
            exit;
        };

        // 1. 대상 조회
        $qr = qry("
            SELECT di_uid, di_wait_uid, di_driver_uid, di_install_date
            FROM tbl_daily_install
            WHERE di_uid IN ($in)
        ");

        $rows = [];
        while ($r = fetch_array($qr)) {
            $rows[] = $r;
        }

        if (!count($rows)) {
            $fail('select', 'no rows found');
        }

        qry("START TRANSACTION");

        // 2. 삭제
        if (!qry("DELETE FROM tbl_daily_install WHERE di_uid IN ($in)")) {
            qry("ROLLBACK");
            $fail('delete_daily_install', 'failed');
        }

        // 3. waitlist 복귀 (중복 제거)
        $iw_uids = [];
        foreach ($rows as $r) {
            if (!empty($r['di_wait_uid'])) {
                $iw_uids[] = intval($r['di_wait_uid']);
            }
        }
        $iw_uids = array_unique($iw_uids);

        foreach ($iw_uids as $iw_uid) {
            if (!$this->update_waitlist_status($iw_uid, 'WAITING')) {
                qry("ROLLBACK");
                $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]);
            }
        }

        // 4. resequence (driver + date별 그룹)
        $group = [];

        foreach ($rows as $r) {
            $key = $r['di_driver_uid'].'|'.$r['di_install_date'];
            $group[$key] = [
                'driver_uid' => $r['di_driver_uid'],
                'date' => $r['di_install_date']
            ];
        }

        foreach ($group as $g) {
            if (!$this->resequence_daily_install($g['driver_uid'], $g['date'])) {
                qry("ROLLBACK");
                $fail('resequence', 'failed', $g);
            }
        }

        qry("COMMIT");

        echo $this->json([
            'ok' => 1,
            'cnt' => count($rows)
        ]);
    }

    public function resequence_daily_install($di_driver_uid, $di_install_date) {

        $di_driver_uid = intval($di_driver_uid);
        $di_install_date = mysqli_real_escape_string($GLOBALS['conn'], $di_install_date);

        $qr = qry("
            SELECT di_uid
            FROM tbl_daily_install
            WHERE di_driver_uid = '{$di_driver_uid}'
                AND di_install_date = '{$di_install_date}'
                AND di_status = 'A'
            ORDER BY di_order_seq ASC, di_uid ASC
        ");

        if (!$qr) return false;

        $seq = 1;
        while ($row = fetch_array($qr)) {
            $di_uid = intval($row['di_uid']);
            if (!qry("UPDATE tbl_daily_install SET di_order_seq = '{$seq}' WHERE di_uid = '{$di_uid}'")) {
                return false;
            }
            $seq++;
        }

        return true;
    }

    public function update_waitlist_latlng() {

        $iw_uid = intval($_POST['iw_uid'] ?? 0);
        $lat    = $_POST['lat'] ?? null;
        $lng    = $_POST['lng'] ?? null;

        if (!$iw_uid || !$lat || !$lng) {
            echo $this->json(['ok'=>0,'msg'=>'invalid params']);
            return;
        }

        $lat = floatval($lat);
        $lng = floatval($lng);

        $sql = "
            UPDATE tbl_install_waitlist
            SET
                iw_lat = '{$lat}',
                iw_lng = '{$lng}'
            WHERE iw_uid = '{$iw_uid}'
            LIMIT 1
        ";

        if (!qry($sql)) {
            echo $this->json([
                'ok' => 0,
                'msg'=> 'update failed',
                'db' => mysqli_error($GLOBALS['conn'])
            ]);
            return;
        }

        echo $this->json(['ok'=>1]);
    }

    public function get_customer_container_one(){
        $cc_uid = intval($_POST['cc_uid']);

        $r = mysqli_query($GLOBALS['conn'],"SELECT * FROM tbl_customer_container WHERE cc_uid='{$cc_uid}' LIMIT 1");
        $row = mysqli_fetch_assoc($r);

        if(!$row){
            echo $this->json(['ok'=>0]);
            return;
        }

        if(!$row){
            echo $this->json(['ok'=>0]);
            return;
        }
        echo $this->json(['ok'=>1,'row'=>$row]);
    }

    public function customer_container_save(){

        $mode = $_POST['mode'] ?? 'ADD';
        $cc_uid = intval($_POST['cc_uid'] ?? 0);
        $c_uid = intval($_POST['c_uid'] ?? 0);

        $type = addslashes($_POST['type'] ?? '');
        if( $mode != 'PICKUP' ) $capacity = $_POST['capacity'] !== '' ? intval($_POST['capacity']) : "NULL";
        $is_used = $_POST['is_used'] ?? 'N';
        $has_lock = $_POST['has_lock'] ?? 'N';
        $has_wheel = $_POST['has_wheel'] ?? 'N';
        $has_frame = $_POST['has_frame'] ?? 'N';
        $owner_type = $_POST['owner_type'] ?? 'C';
        $install_date = $_POST['install_date'] ?? null;
        $pickup_date = $_POST['pickup_date'] ?? null;
        $install_note = addslashes($_POST['install_note'] ?? '');
        $pickup_note = addslashes($_POST['pickup_note'] ?? '');

        if($mode==='ADD'){
            $sql = "
                INSERT INTO tbl_customer_container
                (cc_customer_uid,cc_type,cc_capacity,cc_is_used,
                cc_has_lock,cc_has_wheel,cc_has_woodframe,cc_owner_type,
                cc_status, cc_install_date, cc_install_at,cc_install_note,
                cc_createddate,cc_updateddate)
                VALUES(
                '{$c_uid}','{$type}',{$capacity},
                '{$is_used}','{$has_lock}','{$has_wheel}','{$has_frame}','{$owner_type}',
                'A','{$install_date}','{$install_date}','{$install_note}',
                DATE_FORMAT(NOW(),'%Y%m%d%H%i%s'),
                DATE_FORMAT(NOW(),'%Y%m%d%H%i%s')
                )
            ";
            qry($sql);
        }

        if ($mode === 'EDIT') {

            $result = qry("SELECT cc_status FROM tbl_customer_container WHERE cc_uid='{$cc_uid}'");
            $row = mysqli_fetch_assoc($result);

            if (!$row) {
                echo $this->json(['ok'=>0,'msg'=>'invalid container']);
                return;
            }

            $status = $row['cc_status'];
            // 날짜 처리 (빈 값이면 NULL)
            $install_at_sql = $install_date  ? "'{$install_date} 00:00:00'" : "NULL";
            $pickup_at_sql = $pickup_date ? "'{$pickup_date} 00:00:00'" : "NULL";

            // ===== ACTIVE 수정 =====
            if ($status === 'A') {
                $sql = "
                    UPDATE tbl_customer_container 
                    SET
                        cc_capacity={$capacity},
                        cc_is_used='{$is_used}',
                        cc_has_lock='{$has_lock}',
                        cc_has_wheel='{$has_wheel}',
                        cc_has_woodframe='{$has_frame}',
                        cc_owner_type='{$owner_type}',
                        cc_install_date='{$install_date}',
                        cc_install_at={$install_at_sql},
                        cc_install_note='{$install_note}',
                        cc_modified_at=NOW(),
                        cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s')
                    WHERE cc_uid='{$cc_uid}'
                ";
                qry($sql);
            }
            // ===== INACTIVE 수정 =====
            else {
                $sql = "
                    UPDATE tbl_customer_container 
                    SET
                        cc_pickup_at={$pickup_at_sql},
                        cc_pickup_note='{$pickup_note}',
                        cc_modified_at=NOW(),
                        cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s')
                    WHERE cc_uid='{$cc_uid}'
                ";
                qry($sql);
            }
        }

        if($mode==='PICKUP'){
            $sql="
                UPDATE tbl_customer_container 
                SET
                    cc_status='I',
                    cc_pickup_date='{$pickup_date}',
                    cc_pickup_note='{$pickup_note}',
                    cc_pickup_at=NOW(),
                    cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s')
                WHERE cc_uid='{$cc_uid}'
            ";
            qry($sql);
        }

        echo $this->json(['ok'=>1]);
    }

    // DAILY INSTALL 
    public function daily_install_update_status()
    {
        $di_uid          = intval($_POST['di_uid'] ?? 0);
        $member_uid      = intval($_POST['member_uid'] ?? 0);
        $newStatus       = strtoupper(trim($_POST['status'] ?? ''));
        $install_note    = trim($_POST['install_note'] ?? '');
        $additional_note = trim($_POST['additional_note'] ?? '');
        $oil_qty         = intval($_POST['oil_qty'] ?? 0);
        $sludge          = intval($_POST['sludge'] ?? 0);

        $allowed = ['P','M','X'];

        $fail = function($stage, $msg) {
            echo $this->json([
                'ok'    => 0,
                'stage' => $stage,
                'msg'   => $msg,
                'db'    => mysqli_error($GLOBALS['conn'])
            ]);
            exit;
        };

        if(!$di_uid || !$member_uid || !in_array($newStatus, $allowed)){
            $fail('validate', 'invalid params');
        }

        // 1) install 조회
        $qr = qry("
            SELECT *
            FROM tbl_daily_install
            WHERE di_uid = '{$di_uid}'
            LIMIT 1
        ");
        $di = mysqli_fetch_assoc($qr);
        if(!$di){
            $fail('select_di', 'not found');
        }

        qry("START TRANSACTION");

        try {

            // 2) 상태 업데이트
            $this->updateDailyInstallStatus($di_uid, $newStatus, $install_note, $oil_qty, $sludge);

            // 2-1) additional note 저장
            $this->upsertAdditionalNote($di, $additional_note);

            // 3) 컨테이너 반영
            if ($di['di_status'] !== 'P' && $newStatus === 'P') {

                if ($di['di_container_applied'] == 0) {
                    $this->apply_container_changes_for_di($di_uid, $install_note, intval($di['di_customer_uid']));

                    // 3-1) 컨테이너 반영 후 customer 의 main 볼륨이랑 main 컨테이너 변경
                    $this->updateCustomerMainContainer(intval($di['di_customer_uid']), $di['di_install_date']);

                    // 실행 완료 기록
                    $this->markContainerApplied($di_uid);
                }
            }

            // 4) oil pickup → daily
            $this->saveDailyFromInstallIfNeeded($di_uid, $oil_qty, $sludge);

            // 5) waitlist 처리
            $this->handleWaitlistAfterInstall($di, $newStatus, $member_uid, $install_note);

            // 6) 순서 재정렬
            $this->reorder_daily_install_sequence(
                $di['di_driver_uid'],
                $di['di_install_date']
            );

            qry("COMMIT");
            echo $this->json(['ok'=>1]);
            exit;

        } catch (Exception $e) {
            qry("ROLLBACK");
            $fail('exception', $e->getMessage());
        }
    }

    private function updateDailyInstallStatus(
        int $di_uid,
        string $newStatus,
        string $install_note,
        int $oil_qty,
        int $sludge
    )
    {
        $conn = $GLOBALS['conn'];

        $newStatus = mysqli_real_escape_string($conn, $newStatus);
        $noteEsc   = mysqli_real_escape_string($conn, $install_note);
        $oil_qty   = intval($oil_qty);
        $sludge    = intval($sludge);

        $sql = "
            UPDATE tbl_daily_install
            SET
                di_status = '{$newStatus}',
                di_install_note = '{$noteEsc}',
                di_oil_pickup = '{$oil_qty}',
                di_sludge = '{$sludge}',
                di_install_time = IF('{$newStatus}' <> 'A', CURTIME(), di_install_time)
            WHERE di_uid = '{$di_uid}'
            LIMIT 1
        ";

        if (!qry($sql)) {
            throw new Exception('daily install status update failed');
        }
    }

    /**
     * install 추가 메모를 tbl_note에 upsert
     *
     * install 하면서 컨테이너 위치관련 note를 n_dailyuid 를 'i_{di_uid}' 형태로 저장
     * 예: di_uid = 123 이면 n_dailyuid = 'i_123'
     */
    private function upsertAdditionalNote(array $di, string $additional_note): void
    {
        $connect = $GLOBALS['conn'];
        // 디버그
        // file_put_contents(__DIR__.'/debug.log', "NOTE: ".$additional_note."\n", FILE_APPEND); 
        $noteText = trim($additional_note);
        if (strlen($noteText) <= 1) {
            // 기존 note 삭제
            $di_uid = intval($di['di_uid'] ?? 0);
            if ($di_uid > 0) {

                $noteDailyUid = 'i_' . $di_uid;
                $n_dailyuid = mysqli_real_escape_string($connect, $noteDailyUid);

                mysqli_query($connect, "
                    DELETE FROM tbl_note
                    WHERE n_dailyuid = '{$n_dailyuid}'
                ");
            }

            return;
        }

        $di_uid = intval($di['di_uid'] ?? 0);
        if ($di_uid <= 0) {
            return;
        }

        // install 전용 note key
        $noteDailyUid = 'i_' . $di_uid;

        $n_dailyuid   = mysqli_real_escape_string($connect, $noteDailyUid);
        $customeruid  = mysqli_real_escape_string($connect, $di['di_customer_uid'] ?? '');
        $memberuid    = mysqli_real_escape_string($connect, $_SESSION['ss_UID'] ?? '');
        $noteEsc      = mysqli_real_escape_string($connect, $noteText);
        $now14        = date('YmdHis');
        $level        = intval($_SESSION['ss_LEVEL'] ?? 9);

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

        if (!$qr) {
            throw new Exception('install note select failed');
        }

        $existing = mysqli_fetch_assoc($qr);

        if ($existing) {
            // UPDATE
            $ok = 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 = '{$n_dailyuid}'
                LIMIT 1
            ");

            if (!$ok) {
                throw new Exception('install note update failed');
            }

        } else {
            // INSERT
            $ok = 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}',
                    '{$n_dailyuid}',
                    'D',
                    {$level},
                    1,
                    '{$noteEsc}',
                    '{$now14}'
                )
            ");

            if (!$ok) {
                throw new Exception('install note insert failed');
            }
        }
    }

    private function markContainerApplied(
        int $di_uid
    )
    {
        $conn = $GLOBALS['conn'];

        $sql = "
            UPDATE tbl_daily_install
            SET
                di_container_applied = 1
            WHERE di_uid = '{$di_uid}'
            LIMIT 1
        ";

        if (!qry($sql)) {
            throw new Exception('daily install status update failed');
        }
    }

    private function saveDailyFromInstallIfNeeded(int $di_uid, int $oil_qty, int $sludge): void
    {
        if ($oil_qty <= 0) return;

        $qr = qry("SELECT * FROM tbl_daily_install WHERE di_uid='{$di_uid}' LIMIT 1");
        $di = mysqli_fetch_assoc($qr);
        if (!$di) throw new Exception('install not found');

        $visitdate = date('Ymd', strtotime($di['di_install_date']));
        $now14 = date('YmdHis');

        // customer account
        $qrCus = qry("
            SELECT 
                c_accountno,
                c_regionuid,
                c_region
            FROM tbl_customer
            WHERE c_uid='{$di['di_customer_uid']}'
            LIMIT 1
        ");
        $customer = mysqli_fetch_assoc($qrCus);

        if (!$customer) {
            throw new Exception('customer not found');
        }

        // =========================
        // payload (saveInput 스타일)
        // =========================
        $payload = [
            'd_customeruid' => $di['di_customer_uid'],
            'd_accountno'   => $customer['c_accountno'],
            'd_orderdate'   => $visitdate,
            'd_driveruid'   => $_SESSION['ss_UID'] ?? 0,   // member uid
            'd_druid'       => 11,       // driver uid 고정 hard coding
            'd_drinitial'   => 'I.N',    // driver initial 고정 hard coding
            'd_regionuid'   => $customer['c_regionuid'] ?? 0,
            'd_region'      => $customer['c_region'] ?? '',
            'd_visit'       => 'Y',
            'd_visitdate'   => $visitdate . '000000',
            'd_status'      => 'F',
            'd_jobtype'     => 'INSTALL',
            'd_quantity'    => $oil_qty,
            'd_sludge'      => $sludge,
            'd_inputdate'   => $now14,
            'd_is_transfer' => 'N',
            'd_level'       => $_SESSION['ss_LEVEL'] ?? '',
            'd_createruid'  => $_SESSION['ss_UID'] ?? '',
            'd_createddate' => $now14,
        ];

        // =========================
        // 기존 daily 찾기 (중복 방지)
        // =========================
        $opt = [
            'allow_update' => true,
            'old_key' => [
                'd_orderdate' => $visitdate,
                'd_accountno' => $customer['c_accountno'],
                'd_jobtype'   => 'INSTALL'
            ]
        ];

        $this->dailyService->saveDaily($payload, $opt);
    }

    private function handleWaitlistAfterInstall(array $di, string $newStatus, int $member_uid, string $note)
    {
        $wait_uid = intval($di['di_wait_uid']);
        if ($wait_uid <= 0) return;

        if (in_array($newStatus, ['P','M'])) {

            $this->updateWaitlistStatus($wait_uid, 'COMPLETED');

            $this->createNextScheduleIfNeeded(
                $wait_uid,
                $di['di_install_date'],
                $member_uid
            );

        } elseif ($newStatus === 'X') {

            $this->updateWaitlistStatus($wait_uid, 'WAITING', $member_uid, $note);
        }
    }

    private function updateWaitlistStatus(
        int $wait_uid,
        string $status,
        ?int $member_uid = null,
        ?string $note = null
    )
    {
        $status = strtoupper($status);

        $allowed = ['COMPLETED', 'WAITING'];
        if (!in_array($status, $allowed, true)) {
            throw new Exception('invalid waitlist status');
        }

        $setParts = [];
        $setParts[] = "iw_status = '{$status}'";

        if ($status === 'COMPLETED') {
            // 완료 시 cancel 정보 초기화
            $setParts[] = "iw_cancel_note = NULL";
            $setParts[] = "iw_cancel_at = NULL";
            $setParts[] = "iw_cancel_by = NULL";
        }

        if ($status === 'WAITING') {
            // 취소로 되돌릴 때
            $noteEsc = mysqli_real_escape_string($GLOBALS['conn'], $note ?? '');
            $member  = intval($member_uid ?? 0);

            $setParts[] = "iw_cancel_note = '{$noteEsc}'";
            $setParts[] = "iw_cancel_at = NOW()";
            $setParts[] = "iw_cancel_by = '{$member}'";
        }

        $sql = "
            UPDATE tbl_install_waitlist
            SET " . implode(',', $setParts) . "
            WHERE iw_uid = '{$wait_uid}'
            LIMIT 1
        ";

        if (!qry($sql)) {
            throw new Exception('waitlist status update failed');
        }
    }

    private function createNextScheduleIfNeeded(int $wait_uid, string $installDate, int $member_uid)
    {
        $qr = qry("SELECT * FROM tbl_install_waitlist WHERE iw_uid = '{$wait_uid}' LIMIT 1");
        $wait = mysqli_fetch_assoc($qr);
        if (!$wait) {
            throw new Exception('waitlist not found');
        }

        if (intval($wait['iw_scheduled']) !== 1) return;

        $cycleDays = intval($wait['iw_scheduled_cycle']);
        if ($cycleDays <= 0) return;

        if (empty($installDate)) {
            throw new Exception('install date missing');
        }

        $nextDate = date('Y-m-d', strtotime($installDate . " +{$cycleDays} days"));

        // 
        $sql = "
            INSERT INTO tbl_install_waitlist (
                iw_customer_uid,
                iw_accountno,
                iw_customer_name,
                iw_phone,
                iw_address,
                iw_city,
                iw_postal,
                iw_lat,
                iw_lng,
                iw_requested_due_date,
                iw_work_type,
                iw_status,
                iw_priority,
                iw_created_by,
                iw_scheduled,
                iw_scheduled_cycle,
                iw_request_by,
                iw_request_at,
                iw_request_note,
                iw_cs_note,
                iw_work_note,
                iw_required_person_cnt,
                iw_multi_person_reason,
                iw_request_source
            ) VALUES (
                '{$wait['iw_customer_uid']}',
                '{$wait['iw_accountno']}',
                '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_customer_name'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_phone'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_address'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_city'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_postal'])."',
                '{$wait['iw_lat']}',
                '{$wait['iw_lng']}',
                '{$nextDate}',
                '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_work_type'])."',
                'WAITING',
                '{$wait['iw_priority']}',
                '{$wait['iw_created_by']}',
                1,
                '{$cycleDays}',
                '{$wait['iw_request_by']}',
                '{$installDate}',
                '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_request_note'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_cs_note'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_work_note'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_required_person_cnt'])."',
                '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_multi_person_reason'])."',
                'AUTO'
            )
        ";

        if (!qry($sql)) {
            throw new Exception('auto reschedule insert failed');
        }

        // 새 wait uid
        $new_wait_uid = mysqli_insert_id($GLOBALS['conn']);
        if (!$new_wait_uid) {
            throw new Exception('auto reschedule insert id missing');
        }

        // 컨테이너 복사
        $this->copyWaitContainers($wait_uid, (int)$new_wait_uid);
    }

    private function copyWaitContainers(int $from_wait_uid, int $to_wait_uid): void
    {
        $sql = "
            INSERT INTO tbl_install_waitlist_container (
                iwc_wait_uid,
                iwc_action,
                iwc_type,
                iwc_capacity,
                iwc_is_used,
                iwc_has_lock,
                iwc_has_wheel,
                iwc_has_woodframe,
                iwc_target_cc_uid,
                iwc_owner_type
            )
            SELECT
                '{$to_wait_uid}' AS iwc_wait_uid,
                iwc_action,
                iwc_type,
                iwc_capacity,
                iwc_is_used,
                iwc_has_lock,
                iwc_has_wheel,
                iwc_has_woodframe,
                iwc_target_cc_uid,
                iwc_owner_type
            FROM tbl_install_waitlist_container
            WHERE iwc_wait_uid = '{$from_wait_uid}'
        ";

        if (!qry($sql)) {
            throw new Exception('container copy insert failed');
        }
    }

    public function upload_after_photo() {

        $di_uid     = intval($_POST['di_uid'] ?? 0);
        $member_uid = intval($_POST['member_uid'] ?? 0);
        $note       = trim($_POST['note'] ?? '');
        $i_type     = $_POST['i_type'] ?? 'install';

        $fail = function($stage, $msg, $extra = []) {
            echo $this->json(array_merge([
                'ok'    => 0,
                'stage' => $stage,
                'msg'   => $msg,
                'db'    => mysqli_error($GLOBALS['conn'])
            ], $extra));
            exit;
        };

        if(!$di_uid || !$member_uid){
            $fail('validate', 'invalid params');
        }

        // daily_install 확인 (customer_uid 가져오기)
        $qr = qry("
            SELECT di_customer_uid
            FROM tbl_daily_install
            WHERE di_uid = '{$di_uid}'
            LIMIT 1
        ");
        $di = mysqli_fetch_assoc($qr);
        if(!$di){
            $fail('select_di', 'daily_install not found');
        }

        if(!isset($_FILES['photo']) || $_FILES['photo']['error'] !== 0){
            $fail('file', 'no file');
        }

        $file = $_FILES['photo'];
        $customer_uid = intval($di['di_customer_uid']);

        $ok = $this->save_customer_image_single(
            $customer_uid,
            $member_uid,
            $i_type,
            $di_uid,   // i_sourceuid = di_uid
            $note,
            $file
        );

        if(!$ok){
            $fail('save_image', 'upload failed');
        }

        echo $this->json(['ok'=>1]);
        exit;
    }

    private function save_customer_image_single($i_customeruid, $i_memberuid, $i_type, $i_sourceuid, $i_note, $file){

        $conn = $GLOBALS['conn'];

        $i_customeruid = intval($i_customeruid);
        $i_memberuid   = intval($i_memberuid);
        $i_sourceuid   = intval($i_sourceuid);

        $i_type    = mysqli_real_escape_string($conn, $i_type);
        $i_noteEsc = mysqli_real_escape_string($conn, trim($i_note));
        $i_createddate = date("YmdHis");

        // 확장자 체크(최소 보안)
        $allowed = ['jpg','jpeg','png','webp'];
        $ext = strtolower(pathinfo($file['name'] ?? '', PATHINFO_EXTENSION));
        if(!$ext || !in_array($ext, $allowed)){
            return false;
        }

        // 작업 단위 1장 유지: (customeruid + type, sourceuid 는 제외)
        $qry_update = "
            UPDATE tbl_customer_image
            SET i_status = 'I'
            WHERE i_customeruid = '{$i_customeruid}'
                AND i_type = '{$i_type}'
                AND i_status = 'A'
        ";
        if(qry($qry_update) === false) return false;

        // 저장 폴더
        $upload_folder = getenv("DOCUMENT_ROOT")."/upload/customer_image/".$i_customeruid;
        if(!is_dir($upload_folder)){
            mkdir($upload_folder, 0777, true);
        }

        $newFileName = "IMG_".$i_customeruid."_".time().rand(1000,9999)."_0.jpg";
        $savePath = $upload_folder . "/" . $newFileName;

        if (!$this->compressAndResizeImage($file['tmp_name'], $savePath, $ext, 1200, 75)) {
            return false;
        }

        $dbFilePath = "/upload/customer_image/".$i_customeruid."/";

        $sqlIns = "
            INSERT INTO tbl_customer_image (
                i_customeruid,
                i_memberuid,
                i_createdby,
                i_createddate,
                i_type,
                i_filename,
                i_filepath,
                i_status,
                i_note,
                i_sourceuid
            ) VALUES (
                '{$i_customeruid}',
                '{$i_memberuid}',
                '{$i_memberuid}',
                '{$i_createddate}',
                '{$i_type}',
                '{$newFileName}',
                '{$dbFilePath}',
                'A',
                '{$i_noteEsc}',
                '{$i_sourceuid}'
            )
        ";

        return qry($sqlIns) !== false;
    }

    private function apply_container_changes_for_di($di_uid, $note, $customer_uid){

        $containers = [];
        $rc = qry("SELECT * FROM tbl_daily_install_container WHERE dic_di_uid = '{$di_uid}'");

        while($row = mysqli_fetch_assoc($rc)){
            $containers[] = $row;
        }

        foreach($containers as $c){

            $action = strtoupper($c['dic_action'] ?? '');
            $type = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_type'] ?? '');

            if(is_null($c['dic_capacity'])){
                $capacityCond = "IS NULL";
                $capacityVal  = "NULL";
            }else{
                $cap = intval($c['dic_capacity']);
                $capacityCond = "= '{$cap}'";
                $capacityVal  = "'{$cap}'";
            }

            $owner_type = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_owner_type'] ?? 'C');
            $is_used    = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_is_used'] ?? 'N');
            $has_lock   = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_has_lock'] ?? 'N');
            $has_wheel  = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_has_wheel'] ?? 'N');
            $has_wood   = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_has_woodframe'] ?? 'N');

            // PICKUP
            if($action === 'PICKUP'){
                qry("
                    UPDATE tbl_customer_container
                    SET cc_status='I',
                        cc_pickup_note='{$note}',
                        cc_pickup_at=NOW(),
                        cc_pickup_date=NOW(),
                        cc_modified_at=NOW()
                    WHERE cc_uid = (
                        SELECT cc_uid FROM (
                            SELECT cc_uid
                            FROM tbl_customer_container
                            WHERE cc_customer_uid='{$customer_uid}'
                                AND cc_status='A'
                                AND cc_type='{$type}'
                                AND (cc_capacity {$capacityCond})
                            ORDER BY IFNULL(cc_install_at,'1970-01-01'), cc_uid
                            LIMIT 1
                        ) x
                    )
                ");
            }

            // INSTALL
            else if($action === 'INSTALL'){

                $nowYmdHis = date('YmdHis');

                qry("
                    INSERT INTO tbl_customer_container (
                        cc_customer_uid,
                        cc_type, cc_capacity,
                        cc_owner_type,
                        cc_is_used, cc_has_lock, cc_has_wheel, cc_has_woodframe,
                        cc_status,
                        cc_install_note,
                        cc_install_at, cc_install_date,
                        cc_createddate, cc_updateddate
                    ) VALUES (
                        '{$customer_uid}',
                        '{$type}', {$capacityVal},
                        '{$owner_type}',
                        '{$is_used}', '{$has_lock}', '{$has_wheel}', '{$has_wood}',
                        'A',
                        '{$note}',
                        NOW(), NOW(),
                        NOW(), NOW()
                    )
                ");
            }
        }
    }

    private function updateCustomerMainContainer($customer_uid, $install_date)
    {
        $customer_uid = intval($customer_uid);
        $install_yyyymmdd = null;
        if (!empty($install_date)) {
            $install_yyyymmdd = str_replace('-', '', $install_date);
        }

        $qr = qry("
            SELECT 
                c_manualvolume,
                c_fullcycleflag,
                c_fullcycleforced,
                c_mainvolume,
                c_installdate, 
                c_exchangedate, 
                c_removaldate
            FROM tbl_customer
            WHERE c_uid = '{$customer_uid}'
            LIMIT 1
        ");
        $cus = mysqli_fetch_assoc($qr);

        if (!$cus) {
            throw new Exception('customer not found');
        }

        if ($install_yyyymmdd) {
            // first install
            $current_installdate = $cus['c_installdate'];
            if (!$current_installdate || $current_installdate === 'N/A' || $install_yyyymmdd < $current_installdate) {

                qry("
                    UPDATE tbl_customer
                    SET c_installdate='{$install_yyyymmdd}'
                    WHERE c_uid='{$customer_uid}'
                ");
            }

            // exchange date (최근 install 작업)
            $current_exchangedate = $cus['c_exchangedate'];
            if ($current_exchangedate < $install_yyyymmdd) {
                qry("
                    UPDATE tbl_customer
                    SET c_exchangedate='{$install_yyyymmdd}'
                    WHERE c_uid='{$customer_uid}'
                ");
            }

            // removal date
            $q_activeCount = qry("
                SELECT COUNT(*) AS active_cnt
                FROM tbl_customer_container
                WHERE cc_customer_uid = '{$customer_uid}'
                    AND cc_status = 'A'
            ");

            $row_activeCount = mysqli_fetch_assoc($q_activeCount);
            $activeCnt = (int)($row_activeCount['active_cnt'] ?? 0);
            if ($activeCnt === 0) {
                qry("
                    UPDATE tbl_customer
                    SET c_removaldate = '{$install_yyyymmdd}'
                    WHERE c_uid = '{$customer_uid}'
                ");
            } else {
                // 치웠다가 다시 설치
                qry("
                    UPDATE tbl_customer
                    SET c_removaldate = ''
                    WHERE c_uid = '{$customer_uid}'
                    AND c_removaldate <> ''
                ");
            }
        }

        // =========================================
        // 1) 수동 관리
        // =========================================
        if ($cus['c_manualvolume'] === 'Y') {
            // 아무것도 하지 않음
            return;
        }
        // 기존 mainvolume 저장
        $oldVolume = intval($cus['c_mainvolume'] ?? 0);

        // =========================================
        // 2) active container 조회
        // =========================================
        $rc = qry("
            SELECT
                cc_type,
                cc_capacity,
                cc_owner_type
            FROM tbl_customer_container
            WHERE cc_customer_uid = '{$customer_uid}'
            AND cc_status = 'A'
        ");

        $containers = [];
        while ($row = mysqli_fetch_assoc($rc)) {
            $containers[] = $row;
        }

        // =========================================
        // 3) 컨테이너 없으면 초기화
        // =========================================
        if (!count($containers)) {

            qry("
                UPDATE tbl_customer
                SET
                    c_mainvolume = 0,
                    c_maincontainer = ''
                WHERE c_uid = '{$customer_uid}'
                LIMIT 1
            ");

            return;
        }

        // =========================================
        // 4) 타입 분류 (D / B)
        // =========================================
        $d_list = []; // D 계열
        $b_list = []; // B 계열

        foreach ($containers as $c) {

            $type = strtoupper(trim($c['cc_type'] ?? ''));
            $capacity = intval($c['cc_capacity'] ?? 0); // null / '' → 0 처리

            // B 타입 판별 400B, 600B, CB 등
            if (preg_match('/^\d*B$/', $type) || $type === 'CB') {

                $b_list[] = [
                    'type' => $type,
                    'capacity' => $capacity
                ];
            }

            // D 타입 판별 
            else if ($type === 'D') {

                $d_list[] = [
                    'type' => $type,
                    'capacity' => 200 // D는 고정 200
                ];
            }

            // BUCKET / TANK 등은 무시
        }

        // =========================================
        // 5) B가 하나라도 있으면 → B 기준 처리
        // =========================================
        if (count($b_list) > 0) {

            $maxCapacity = 0;
            $maxType = '';

            foreach ($b_list as $b) {

                if ($b['capacity'] > $maxCapacity) {
                    $maxCapacity = $b['capacity'];
                    $maxType = $b['type'];
                }
            }

            // capacity가 전부 0일 수도 있음 (예외 처리)
            if ($maxCapacity <= 0) {
                $maxType = $b_list[0]['type']; // fallback
            }

            // forced cycle
            $newVolume = $maxCapacity;
            $this->adjustFullCycle($customer_uid, $cus, $oldVolume, $newVolume);

            // main volume
            qry("
                UPDATE tbl_customer
                SET
                    c_mainvolume = '{$maxCapacity}',
                    c_maincontainer = '{$maxType}'
                WHERE c_uid = '{$customer_uid}'
                LIMIT 1
            ");

            return;
        }

        // =========================================
        // 6) B 없고 D만 있는 경우
        // =========================================
        if (count($d_list) > 0) {

            $count = count($d_list);

            if ($count === 1) {
                $volume = 200;
            } else {
                $volume = $count * 200;
            }

            // forced cycle
            $newVolume = $volume;
            $this->adjustFullCycle($customer_uid, $cus, $oldVolume, $newVolume);

            // main volume
            qry("
                UPDATE tbl_customer
                SET
                    c_mainvolume = '{$volume}',
                    c_maincontainer = 'D'
                WHERE c_uid = '{$customer_uid}'
                LIMIT 1
            ");

            return;
        }

        // =========================================
        // 7) fallback (이상 케이스)
        // =========================================
        qry("
            UPDATE tbl_customer
            SET
                c_mainvolume = 0,
                c_maincontainer = ''
            WHERE c_uid = '{$customer_uid}'
            LIMIT 1
        ");
    }

    private function adjustFullCycle($customer_uid, $cus, $oldVolume, $newVolume)
    {
        // 조건 체크
        if ($cus['c_manualvolume'] !== 'N') return;
        if (intval($cus['c_fullcycleflag']) !== 1) return;

        $forced = floatval($cus['c_fullcycleforced'] ?? 0);

        // 기존 값이 없으면 의미 없음
        if ($forced <= 0) return;

        // oldVolume 0이면 비율 계산 불가 → skip
        if ($oldVolume <= 0) return;

        // newVolume 0이면 forced도 0으로
        if ($newVolume <= 0) {
            $newForced = 0;
        } else {
            $ratio = $newVolume / $oldVolume;
            $newForced = $forced * $ratio;
        }

        qry("
            UPDATE tbl_customer
            SET c_fullcycleforced = '{$newForced}'
            WHERE c_uid = '{$customer_uid}'
            LIMIT 1
        ");
    }

    private function reorder_daily_install_sequence($driver_uid, $install_date) {

        $driver_uid = intval($driver_uid);
        $install_date = mysqli_real_escape_string($GLOBALS['conn'], $install_date);

        $seq = 1;

        // ================================
        // 완료된 것들 먼저 (P/M/X)
        // ================================
        $rsDone = qry("
            SELECT di_uid
            FROM tbl_daily_install
            WHERE di_driver_uid = '{$driver_uid}'
                AND di_install_date = '{$install_date}'
                AND di_status <> 'A'
            ORDER BY di_order_seq ASC
        ");

        while($row = mysqli_fetch_assoc($rsDone)){
            $uid = intval($row['di_uid']);
            qry("
                UPDATE tbl_daily_install
                SET di_order_seq = '{$seq}'
                WHERE di_uid = '{$uid}'
            ");
            $seq++;
        }

        // ================================
        // 그 다음 대기 상태 업데이트
        // ================================
        $rsActive = qry("
            SELECT di_uid
            FROM tbl_daily_install
            WHERE di_driver_uid = '{$driver_uid}'
                AND di_install_date = '{$install_date}'
                AND di_status = 'A'
            ORDER BY di_order_seq ASC
        ");

        while($row = mysqli_fetch_assoc($rsActive)){
            $uid = intval($row['di_uid']);
            qry("
                UPDATE tbl_daily_install
                SET di_order_seq = '{$seq}'
                WHERE di_uid = '{$uid}'
            ");
            $seq++;
        }
    }

    public function daily_install_reorder() {

        $driver_uid = intval($_POST['driver_uid'] ?? 0);
        $install_date = trim($_POST['install_date'] ?? '');
        $orderJson  = $_POST['order_list'] ?? '';

        $fail = function($stage, $msg, $extra = []) {
            echo $this->json(array_merge([
                'ok'    => 0,
                'stage' => $stage,
                'msg'   => $msg,
                'db'    => mysqli_error($GLOBALS['conn'])
            ], $extra));
            exit;
        };

        if(!$driver_uid || !$orderJson){
            $fail('validate', 'invalid params');
        }

        $orderList = json_decode($orderJson, true);
        if(!is_array($orderList) || !count($orderList)){
            $fail('decode', 'invalid order list');
        }

        qry("START TRANSACTION");

        foreach($orderList as $item){

            $di_uid = intval($item['di_uid'] ?? 0);
            $seq    = intval($item['di_order_seq'] ?? 0);

            if(!$di_uid || !$seq){
                qry("ROLLBACK");
                $fail('validate_row', 'invalid row');
            }

            $sql = "
                UPDATE tbl_daily_install
                SET di_order_seq = '{$seq}'
                WHERE di_uid = '{$di_uid}'
                    AND di_driver_uid = '{$driver_uid}'
                    AND di_status = 'A'
                LIMIT 1
            ";

            if(!qry($sql)){
                qry("ROLLBACK");
                $fail('update', 'update failed', ['di_uid'=>$di_uid]);
            }
        }

        // 마지막에 안전하게 정렬 보정
        $this->reorder_daily_install_sequence($driver_uid, $install_date);

        qry("COMMIT");

        echo $this->json(['ok'=>1]);
        exit;
    }

    public function bulk_update_daily_install() {

        $di_uids = $_POST['di_uids'] ?? [];
        $new_driver_uid = intval($_POST['di_driver_uid'] ?? 0);
        $new_install_date = trim($_POST['di_install_date'] ?? '');

        if (!is_array($di_uids) || !count($di_uids)) {
            echo $this->json(['ok'=>0, 'msg'=>'invalid di_uids']);
            return;
        }

        $di_uids = array_map('intval', $di_uids);
        $di_uids = array_filter($di_uids);

        if (!count($di_uids)) {
            echo $this->json(['ok'=>0, 'msg'=>'empty di_uids']);
            return;
        }

        if (!$new_driver_uid || !$new_install_date) {
            echo $this->json(['ok'=>0, 'msg'=>'invalid target params']);
            return;
        }

        $in = implode(',', $di_uids);
        $conn = $GLOBALS['conn'];

        $fail = function($stage, $msg, $extra = []) use ($conn) {
            echo $this->json(array_merge([
                'ok'    => 0,
                'stage' => $stage,
                'msg'   => $msg,
                'db'    => mysqli_error($conn)
            ], $extra));
            exit;
        };

        // 1) 대상 조회
        $qr = qry("
            SELECT di_uid, di_driver_uid, di_install_date
            FROM tbl_daily_install
            WHERE di_uid IN ($in)
        ");

        $rows = [];
        while ($r = fetch_array($qr)) {
            $rows[] = $r;
        }

        if (!count($rows)) {
            $fail('select', 'no rows found');
        }

        // 2) 기존 그룹 수집
        $oldGroups = [];
        foreach ($rows as $r) {
            $key = intval($r['di_driver_uid']) . '|' . $r['di_install_date'];
            $oldGroups[$key] = [
                'driver_uid' => intval($r['di_driver_uid']),
                'date'       => $r['di_install_date']
            ];
        }

        qry("START TRANSACTION");

        // 3) 대상 업데이트
        $new_driver_uid_esc = intval($new_driver_uid);
        $new_install_date_esc = mysqli_real_escape_string($conn, $new_install_date);

        $sql = "
            UPDATE tbl_daily_install
            SET
                di_driver_uid = '{$new_driver_uid_esc}',
                di_install_date = '{$new_install_date_esc}'
            WHERE di_uid IN ($in)
        ";

        if (!qry($sql)) {
            qry("ROLLBACK");
            $fail('update', 'bulk update failed');
        }

        // 4) 새 그룹 resequence
        if (!$this->resequence_daily_install($new_driver_uid_esc, $new_install_date_esc)) {
            qry("ROLLBACK");
            $fail('resequence_new', 'failed', [
                'di_driver_uid'   => $new_driver_uid_esc,
                'di_install_date' => $new_install_date_esc
            ]);
        }

        // 5) 이전 그룹 resequence
        foreach ($oldGroups as $g) {
            $sameGroup =
                intval($g['driver_uid']) === $new_driver_uid_esc &&
                $g['date'] === $new_install_date_esc;

            if ($sameGroup) continue;

            if (!$this->resequence_daily_install($g['driver_uid'], $g['date'])) {
                qry("ROLLBACK");
                $fail('resequence_old', 'failed', $g);
            }
        }

        qry("COMMIT");

        echo $this->json([
            'ok' => 1,
            'cnt' => count($rows)
        ]);
    }

    function compressAndResizeImage($srcPath, $destPath, $ext, $maxWidth = 1200, $quality = 75) {

        list($width, $height, $imageType) = getimagesize($srcPath);

        if (!$width || !$height) return false;

        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $srcImg = imagecreatefromjpeg($srcPath);

                if (function_exists('exif_read_data')) {
                    $exif = @exif_read_data($srcPath);

                    if (!empty($exif['Orientation'])) {
                        switch ($exif['Orientation']) {
                            case 3:
                                $srcImg = imagerotate($srcImg, 180, 0);
                                break;
                            case 6:
                                $srcImg = imagerotate($srcImg, -90, 0);
                                break;
                            case 8:
                                $srcImg = imagerotate($srcImg, 90, 0);
                                break;
                        }
                    }

                    $width = imagesx($srcImg);
                    $height = imagesy($srcImg);
                }

                break;
            case IMAGETYPE_PNG:
                $srcImg = imagecreatefrompng($srcPath);
                break;
            case IMAGETYPE_WEBP:
                $srcImg = imagecreatefromwebp($srcPath);
                break;
            default:
                return false;
        }

        if (!$srcImg) return false;

        if ($width > $maxWidth) {
            $newWidth = $maxWidth;
            $newHeight = intval(($height / $width) * $newWidth);
        } else {
            $newWidth = $width;
            $newHeight = $height;
        }

        $dstImg = imagecreatetruecolor($newWidth, $newHeight);

        imagecopyresampled(
            $dstImg, $srcImg,
            0, 0, 0, 0,
            $newWidth, $newHeight,
            $width, $height
        );

        $result = imagejpeg($dstImg, $destPath, $quality);

        imagedestroy($srcImg);
        imagedestroy($dstImg);

        return $result;
    }
}
