<?php
/**
 * 네이티브 앱 마이페이지 API
 * Actions: get_mypage, update_deposit_name, cancel_charge_order
 */
include_once('./_common.php');

header('Content-Type: application/json; charset=UTF-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('X-Content-Type-Options: nosniff');

define('SNPM_EXPIRE_HOURS', 3);
define('SNPM_MAX_NAME_CHANGES', 1);

function snpm_req($key, $default = '')
{
    return isset($_POST[$key]) ? trim((string)$_POST[$key]) : $default;
}

function snpm_out($arr)
{
    echo json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function snpm_fail($code, $message)
{
    snpm_out(array('result' => 'fail', 'code' => $code, 'message' => $message));
}

function snpm_escape($value)
{
    global $db;
    if (function_exists('sql_real_escape_string')) return sql_real_escape_string($value);
    return mysqli_real_escape_string($db, $value);
}

function snpm_get_meta($cash_info)
{
    $data = @json_decode($cash_info, true);
    return is_array($data) ? $data : array();
}

function snpm_parse_currency_accounts($de_bank_account)
{
    $codes = array("KRW","CNY","USD","EUR","JPY","VND");
    $result = array();
    foreach ($codes as $c) $result[$c] = array();
    $fallback = array();
    if (empty($de_bank_account)) return $result;
    foreach (explode("\n", trim($de_bank_account)) as $line) {
        $line = trim($line);
        if ($line === '') continue;
        $matched = false;
        foreach ($codes as $c) {
            if (stripos($line, '[' . $c . ']') !== false) {
                $result[$c][] = trim(str_ireplace('[' . $c . ']', '', $line));
                $matched = true;
                break;
            }
        }
        if (!$matched) $fallback[] = $line;
    }
    if (empty($result['KRW']) && !empty($fallback)) $result['KRW'] = $fallback;
    return $result;
}

// ── 인증 ──────────────────────────────────────────────────────────────────

$mb_id_raw = snpm_req('mb_id');
$mb_pass   = snpm_req('mb_password');
$action    = snpm_req('action');

if ($mb_id_raw === '') $mb_id_raw = snpm_req('username');
if ($mb_pass === '')   $mb_pass   = snpm_req('password');

if ($mb_id_raw === '' || $mb_pass === '') snpm_fail('auth_required', '로그인 정보가 필요합니다.');
if (substr($mb_id_raw, 0, 8) === 'VpNgueSt') snpm_fail('auth_failed', '마이페이지는 회원 전용입니다.');

if (filter_var($mb_id_raw, FILTER_VALIDATE_EMAIL)) {
    $safe_s = snpm_escape($mb_id_raw);
    $mb = sql_fetch("SELECT * FROM {$g5['member_table']} WHERE mb_email='{$safe_s}' LIMIT 1", false);
    if (!$mb || !$mb['mb_id']) $mb = get_member($mb_id_raw);
} else {
    $mb = get_member($mb_id_raw);
}

if (!$mb || !$mb['mb_id']) snpm_fail('auth_failed', '인증에 실패했습니다.');
if (!function_exists('login_password_check') || !login_password_check($mb, $mb_pass, $mb['mb_password']))
    snpm_fail('auth_failed', '인증에 실패했습니다.');

$safe_mb = snpm_escape($mb['mb_id']);

// ── get_mypage ─────────────────────────────────────────────────────────────

if ($action === 'get_mypage') {

    // 만료된 주문 자동 취소 (3시간 경과, 상태=주문)
    $exp_result = sql_query(
        "SELECT od_id, od_fx_type FROM g5_shoppoint_order
         WHERE mb_id='{$safe_mb}' AND od_status='주문' AND od_settle_case='무통장'
         AND od_time < DATE_SUB(NOW(), INTERVAL " . SNPM_EXPIRE_HOURS . " HOUR)",
        false
    );
    while ($exp_row = sql_fetch_array($exp_result)) {
        $safe_exp = snpm_escape($exp_row['od_id']);
        sql_query(
            "UPDATE g5_shoppoint_order SET od_status='취소',
             od_shop_memo=CONCAT(IFNULL(od_shop_memo,''), ' [기한만료자동취소 ', NOW(), ']')
             WHERE od_id='{$safe_exp}' AND od_status='주문'",
            false
        );
    }

    $mb_point = (int)$mb['mb_point'];
    $expire_secs = SNPM_EXPIRE_HOURS * 3600;

    // 환율 테이블 로딩 (최신 데이터 우선, 타입별 중복 제거)
    $fx_rates_map = array();
    $fx_res = sql_query("SELECT fx_type, fx_rate, fx_from_amount, datetime FROM fx_data ORDER BY id DESC", false);
    while ($fx_row = sql_fetch_array($fx_res)) {
        if (!isset($fx_rates_map[$fx_row['fx_type']])) {
            $fx_rates_map[$fx_row['fx_type']] = array(
                'rate'       => (float)$fx_row['fx_rate'],
                'from_amt'   => (float)$fx_row['fx_from_amount'],
                'updated_at' => (string)$fx_row['datetime'],
            );
        }
    }

    // 충전 신청 내역 (최근 10건)
    $charge_rows = array();
    $cr = sql_query(
        "SELECT od_id, od_status, od_cart_price, od_fx_type, od_fx_amount,
                od_deposit_name, od_time, od_cash_info, od_shop_memo
         FROM g5_shoppoint_order
         WHERE mb_id='{$safe_mb}' AND od_settle_case='무통장'
         ORDER BY od_time DESC LIMIT 10",
        false
    );
    while ($row = sql_fetch_array($cr)) {
        $amount    = (int)$row['od_cart_price'];
        $raw_fx = trim($row['od_fx_type']);
        // 구형 'KRWCNY' 6자 형식 정규화 (앞3자=기준통화, 뒤3자=실제통화)
        if (strlen($raw_fx) === 6 && strtoupper(substr($raw_fx, 0, 3)) === 'KRW') {
            $currency = strtoupper(substr($raw_fx, 3));
        } else {
            $currency = ($raw_fx !== '') ? $raw_fx : 'KRW';
        }
        $fx_amount = (float)$row['od_fx_amount'];
        $fx_formula = '';
        $fx_rate_val = 0.0;
        $fx_rate_updated_at_val = '';
        if ($currency !== 'KRW') {
            $fx_key = 'KRW' . $currency;
            if (isset($fx_rates_map[$fx_key])) {
                $rate     = $fx_rates_map[$fx_key]['rate'];
                $from_amt = $fx_rates_map[$fx_key]['from_amt'];
                $fx_rate_val = $rate;
                $fx_rate_updated_at_val = $fx_rates_map[$fx_key]['updated_at'] ?? '';
                if ($fx_amount <= 0 || (int)$fx_amount === $amount) {
                    $fx_amount = round($amount * $rate, 2);
                }
                $rate_str = number_format($rate, 6);
                $fx_formula = number_format($amount) . ' P × ' . $rate_str . ' = '
                    . number_format($fx_amount, 2) . ' ' . $currency;
                $fx_formula .= "\n(적용 환율 1 Point ≈ " . $rate_str . ' ' . $currency . ')';
            } else {
                if ($fx_amount <= 0) $fx_amount = $amount;
            }
        } else {
            if ($fx_amount <= 0) $fx_amount = $amount;
            $fx_formula = number_format($amount) . ' P = ' . number_format($amount) . ' KRW (1:1)';
        }
        $od_time    = $row['od_time'];
        $expire_at  = date('Y-m-d H:i:s', strtotime($od_time) + $expire_secs);
        $meta       = snpm_get_meta($row['od_cash_info']);
        $dep_changes = (int)($meta['deposit_name_changes'] ?? 0);
        $cancel_reason = '';
        if ($row['od_status'] === '취소') {
            $memo = (string)$row['od_shop_memo'];
            if (strpos($memo, '기한만료자동취소') !== false) {
                $cancel_reason = '입금 기한 초과로 자동 취소';
            } elseif (strpos($memo, '사용자직접취소') !== false) {
                $cancel_reason = '직접 취소';
            } else {
                $cancel_reason = '취소됨';
            }
        }

        $safe_od_id = snpm_escape((string)$row['od_id']);
        $notify_data = sql_fetch(
            "SELECT COUNT(*) AS cnt,
                    (SELECT dn_admin_memo FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}' AND dn_admin_memo!=''
                     ORDER BY dn_id DESC LIMIT 1) AS latest_fb,
                    (SELECT dn_status FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}' AND dn_admin_memo!=''
                     ORDER BY dn_id DESC LIMIT 1) AS fb_status,
                    (SELECT transfer_datetime FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_datetime,
                    (SELECT used_platform_account FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_platform,
                    (SELECT alt_bank_name FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_alt_bank,
                    (SELECT alt_bank_account FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_alt_acct,
                    (SELECT alt_bank_holder FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_alt_holder,
                    (SELECT alt_depositor_name FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_depositor,
                    (SELECT CAST(alt_deposit_amount AS CHAR) FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_amount,
                    (SELECT alt_notes FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_notes,
                    (SELECT dn_timezone FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'
                     ORDER BY dn_id DESC LIMIT 1) AS last_timezone,
                    (SELECT dn_shortage_amount FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}' AND dn_status='보류'
                     ORDER BY dn_id DESC LIMIT 1) AS shortage_amount,
                    (SELECT dn_shortage_currency FROM g5_deposit_notify
                     WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}' AND dn_status='보류'
                     ORDER BY dn_id DESC LIMIT 1) AS shortage_currency
             FROM g5_deposit_notify
             WHERE od_id='{$safe_od_id}' AND mb_id='{$safe_mb}'",
            false
        );

        $charge_rows[] = array(
            'od_id'                  => (string)$row['od_id'],
            'status'                 => $row['od_status'],
            'amount'                 => $amount,
            'currency'               => $currency,
            'fx_amount'              => round($fx_amount, 2),
            'fx_formula'             => $fx_formula,
            'fx_rate'                => $fx_rate_val,
            'fx_rate_updated_at'     => $fx_rate_updated_at_val,
            'deposit_name'           => $row['od_deposit_name'],
            'time'                   => $od_time,
            'expire_time'            => $expire_at,
            'deposit_name_changes'   => $dep_changes,
            'changes_left'           => max(0, SNPM_MAX_NAME_CHANGES - $dep_changes),
            'cancel_reason'          => $cancel_reason,
            'notify_count'           => (int)($notify_data['cnt'] ?? 0),
            'notify_feedback'        => (string)($notify_data['latest_fb'] ?? ''),
            'feedback_status'        => (string)($notify_data['fb_status'] ?? ''),
            'last_notify_datetime'   => (string)($notify_data['last_datetime'] ?? ''),
            'last_notify_platform'   => isset($notify_data['last_platform']) ? (int)$notify_data['last_platform'] : 1,
            'last_notify_alt_bank'   => (string)($notify_data['last_alt_bank'] ?? ''),
            'last_notify_alt_acct'   => (string)($notify_data['last_alt_acct'] ?? ''),
            'last_notify_timezone'   => (string)($notify_data['last_timezone'] ?? 'Asia/Seoul'),
            'last_notify_alt_holder' => (string)($notify_data['last_alt_holder'] ?? ''),
            'last_notify_depositor'  => (string)($notify_data['last_depositor'] ?? ''),
            'last_notify_amount'     => (string)($notify_data['last_amount'] ?? ''),
            'last_notify_notes'      => (string)($notify_data['last_notes'] ?? ''),
            'pending_shortage'          => (float)($notify_data['shortage_amount'] ?? 0),
            'pending_shortage_currency' => (string)($notify_data['shortage_currency'] ?? ''),
        );
    }

    // 출금 신청 내역 (최근 10건)
    $withdraw_rows = array();
    $wr = sql_query(
        "SELECT od_id, od_status, od_cart_price, od_withdraw_fee,
                od_withdraw_type, od_withdraw_bank_name, od_withdraw_bank_account,
                od_withdraw_bank_account_name, od_withdraw_pay, od_withdraw_pay_id,
                od_withdraw_coin, od_withdraw_coin_id, od_time,
                od_fx_type, od_fx_amount, od_withdraw_bank_country
         FROM g5_shoppoint_order4withdraw
         WHERE mb_id='{$safe_mb}'
         ORDER BY od_time DESC LIMIT 10",
        false
    );
    while ($row = sql_fetch_array($wr)) {
        $wod_id = (string)$row['od_id'];
        $safe_wod = snpm_escape($wod_id);
        // notify 데이터 조회
        $wn_data = sql_fetch(
            "SELECT COUNT(*) AS cnt,
                    MAX(CASE WHEN wn_status IN ('처리완료','보류','취소') THEN wn_admin_memo ELSE '' END) AS latest_fb,
                    MAX(CASE WHEN wn_status IN ('처리완료','보류','취소') THEN wn_status ELSE '' END) AS latest_status
             FROM g5_withdraw_notify
             WHERE od_id='{$safe_wod}' AND mb_id='{$safe_mb}'",
            false
        );
        $withdraw_rows[] = array(
            'od_id'        => $wod_id,
            'status'       => $row['od_status'],
            'amount'       => (int)$row['od_cart_price'],
            'fee'          => (int)$row['od_withdraw_fee'],
            'type'         => $row['od_withdraw_type'],
            'bank_name'    => $row['od_withdraw_bank_name'],
            'bank_account' => $row['od_withdraw_bank_account'],
            'bank_holder'  => $row['od_withdraw_bank_account_name'],
            'pay_name'     => $row['od_withdraw_pay'],
            'pay_id'       => $row['od_withdraw_pay_id'],
            'coin_name'    => $row['od_withdraw_coin'],
            'coin_id'      => $row['od_withdraw_coin_id'],
            'time'         => $row['od_time'],
            'currency'     => (($t=trim($row['od_fx_type']??'')) && strlen($t)>=6 ? substr($t,3,3) : ''),
            'fx_amount'    => (float)$row['od_fx_amount'],
            'bank_country' => (string)($row['od_withdraw_bank_country'] ?? ''),
            'notify_count'           => (int)($wn_data['cnt'] ?? 0),
            'notify_feedback'        => (string)($wn_data['latest_fb'] ?? ''),
            'notify_feedback_status' => (string)($wn_data['latest_status'] ?? ''),
        );
    }


    // VPN 주문 내역 (만료된 PREPARED 주문 자동 정리)
    // 통신판매중개 고지: 각 주문의 판매자(seller_id/seller_nick)를 함께 반환한다.
    sql_query("DELETE FROM vpn_native_buy_orders WHERE status='PREPARED' AND expires_at < NOW()", false);
    $vpn_orders = array();
    $item_table   = isset($g5['g5_shop_item_table']) ? $g5['g5_shop_item_table'] : 'g5_shop_item';
    $cart_table   = isset($g5['g5_shop_cart_table']) ? $g5['g5_shop_cart_table'] : 'g5_shop_cart';
    $member_table = isset($g5['member_table']) ? $g5['member_table'] : 'g5_member';
    $vnr = sql_query(
        "SELECT v.order_id, v.it_name, v.total_amount, v.status, v.created_at, v.approved_at, v.qty, v.ticket_days,
                i.pt_id AS seller_id, sm.mb_nick AS seller_nick
           FROM vpn_native_buy_orders v
           LEFT JOIN {$item_table} i ON i.it_id = v.it_id
           LEFT JOIN {$member_table} sm ON sm.mb_id = i.pt_id
          WHERE v.mb_id='{$safe_mb}'
            AND v.status != 'PREPARED'
          ORDER BY v.created_at DESC LIMIT 20",
        false
    );
    while ($row = sql_fetch_array($vnr)) {
        $vpn_orders[] = array(
            "order_id"    => (string)$row["order_id"],
            "it_name"     => (string)$row["it_name"],
            "amount"      => (int)$row["total_amount"],
            "status"      => (string)$row["status"],
            "time"        => (string)$row["created_at"],
            "paid_at"     => (string)($row["approved_at"] ?? ""),
            "source"      => "native",
            "qty"         => (int)$row["qty"],
            "ticket_days" => (int)$row["ticket_days"],
            "unit_type"   => "day",
            "seller_id"   => trim((string)($row["seller_id"] ?? "")),
            "seller_nick" => trim((string)($row["seller_nick"] ?? "")),
        );
    }
    // 연장 주문: vpn_no → vpnid_list.od_id → 장바구니(it_id) → 상품(pt_id) → 판매자 닉네임
    $extend_rows = array();
    $extend_od_ids = array();
    $ver = sql_query(
        "SELECT e.order_no, e.it_name, e.amount, e.status, e.created_at, e.paid_at, e.qty, e.unit_type,
                vl.od_id AS vpn_od_id
           FROM vpn_extend_orders e
           LEFT JOIN vpnid_list vl ON vl.no = e.vpn_no
          WHERE e.mb_id='{$safe_mb}'
          ORDER BY e.created_at DESC LIMIT 20",
        false
    );
    while ($row = sql_fetch_array($ver)) {
        $od_id = trim((string)($row["vpn_od_id"] ?? ""));
        if ($od_id !== '') {
            $extend_od_ids[] = $od_id;
        }
        $extend_rows[] = $row;
    }
    $extend_seller_map = array(); // od_id => array(seller_id, seller_nick)
    if (!empty($extend_od_ids)) {
        $in_clause = implode(',', array_map(function($v) {
            return "'" . sql_real_escape_string($v) . "'";
        }, array_unique($extend_od_ids)));
        $sr = sql_query(
            "SELECT c.od_id, c.ct_id, i.pt_id AS seller_id, sm.mb_nick AS seller_nick
               FROM {$cart_table} c
               INNER JOIN {$item_table} i ON i.it_id = c.it_id
               LEFT JOIN {$member_table} sm ON sm.mb_id = i.pt_id
              WHERE c.od_id IN ({$in_clause})
              ORDER BY c.ct_id ASC",
            false
        );
        while ($srow = sql_fetch_array($sr)) {
            $od_key = (string)$srow["od_id"];
            if (isset($extend_seller_map[$od_key])) {
                continue; // 주문당 첫 상품 기준
            }
            $extend_seller_map[$od_key] = array(
                "seller_id"   => trim((string)($srow["seller_id"] ?? "")),
                "seller_nick" => trim((string)($srow["seller_nick"] ?? "")),
            );
        }
    }
    foreach ($extend_rows as $row) {
        $od_id = trim((string)($row["vpn_od_id"] ?? ""));
        $seller = isset($extend_seller_map[$od_id])
            ? $extend_seller_map[$od_id]
            : array("seller_id" => "", "seller_nick" => "");
        $vpn_orders[] = array(
            "order_id"    => (string)$row["order_no"],
            "it_name"     => (string)$row["it_name"],
            "amount"      => (int)$row["amount"],
            "status"      => (string)$row["status"],
            "time"        => (string)$row["created_at"],
            "paid_at"     => (string)($row["paid_at"] ?? ""),
            "source"      => "extend",
            "qty"         => (int)$row["qty"],
            "ticket_days" => 0,
            "unit_type"   => (string)$row["unit_type"],
            "seller_id"   => $seller["seller_id"],
            "seller_nick" => $seller["seller_nick"],
        );
    }
    usort($vpn_orders, function($a, $b) { return strcmp($b["time"], $a["time"]); });

    $all_curr = snpm_parse_currency_accounts(
        isset($default['de_bank_account']) ? $default['de_bank_account'] : ''
    );

    snpm_out(array(
        'result'            => 'success',
        'mb_point'          => $mb_point,
        'mb_nick'           => (!empty($mb['mb_nick']) ? $mb['mb_nick'] : $mb['mb_name']),
        'charge_orders'     => $charge_rows,
        'withdraw_orders'   => $withdraw_rows,
        'vpn_orders'        => $vpn_orders,
        'currency_accounts' => $all_curr,
    ));
}

// ── update_deposit_name ────────────────────────────────────────────────────

if ($action === 'update_deposit_name') {
    $od_id    = snpm_req('od_id');
    $new_name = snpm_req('new_deposit_name');

    if ($od_id === '') snpm_fail('invalid_param', '주문번호가 필요합니다.');

    $new_name = preg_replace('/[\x00-\x1F\x7F]/', '', $new_name); // 제어문자 제거
    $name_len = mb_strlen($new_name, 'UTF-8');
    if ($name_len < 2 || $name_len > 20)
        snpm_fail('invalid_name', '입금자명은 2자 이상 20자 이하로 입력해 주세요.');

    $safe_od  = snpm_escape($od_id);
    $order = sql_fetch(
        "SELECT od_id, od_status, od_time, od_deposit_name, od_cash_info
         FROM g5_shoppoint_order
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}' AND od_settle_case='무통장'
         LIMIT 1",
        false
    );
    if (!$order) snpm_fail('not_found', '주문을 찾을 수 없습니다.');
    // 통화 결정
    $raw_fx2 = trim($order['od_fx_type'] ?? '');
    if (strlen($raw_fx2) === 6 && strtoupper(substr($raw_fx2, 0, 3)) === 'KRW') {
        $od_currency2 = strtoupper(substr($raw_fx2, 3));
    } else {
        $od_currency2 = ($raw_fx2 !== '') ? strtoupper($raw_fx2) : 'KRW';
    }
    // 플랫폼 안내 계좌 (해당 통화)
    $def_row = sql_fetch("SELECT de_bank_account FROM g5_shop_default LIMIT 1", false);
    $all_curr2 = snpm_parse_currency_accounts($def_row['de_bank_account'] ?? '');
    $od_platform_accounts = isset($all_curr2[$od_currency2]) ? $all_curr2[$od_currency2] : $all_curr2['KRW'];
    if ($order['od_status'] !== '주문') snpm_fail('invalid_status', '대기중인 주문만 수정할 수 있습니다.');

    // 만료 확인
    $expire_ts = strtotime($order['od_time']) + SNPM_EXPIRE_HOURS * 3600;
    if (time() > $expire_ts) {
        $safe_exp2 = snpm_escape($od_id);
        sql_query("UPDATE g5_shoppoint_order SET od_status='취소', od_shop_memo=CONCAT(IFNULL(od_shop_memo,''), ' [기한만료자동취소 ', NOW(), ']') WHERE od_id='{$safe_exp2}' AND od_status='주문'", false);
        snpm_fail('expired', '입금 기한이 만료되어 주문이 취소되었습니다.');
    }

    // 변경 횟수 확인
    $meta    = snpm_get_meta($order['od_cash_info']);
    $changes = (int)($meta['deposit_name_changes'] ?? 0);
    if ($changes >= SNPM_MAX_NAME_CHANGES)
        snpm_fail('limit_exceeded', '입금자명 변경은 최대 ' . SNPM_MAX_NAME_CHANGES . '회까지 가능합니다.');

    // 최초 원본 보관
    if (!isset($meta['original_deposit_name']))
        $meta['original_deposit_name'] = $order['od_deposit_name'];
    $meta['deposit_name_changes'] = $changes + 1;
    $meta['last_changed'] = date('Y-m-d H:i:s');

    $safe_new  = snpm_escape($new_name);
    $safe_meta = snpm_escape(json_encode($meta, JSON_UNESCAPED_UNICODE));
    $mod_log   = snpm_escape(date('Y-m-d H:i:s') . ' 입금자명변경:' . $new_name);

    sql_query(
        "UPDATE g5_shoppoint_order
         SET od_deposit_name='{$safe_new}',
             od_cash_info='{$safe_meta}',
             od_mod_history=CONCAT(IFNULL(od_mod_history,''), '\n', '{$mod_log}')
         WHERE od_id='{$safe_od}' AND od_status='주문'",
        false
    );

    snpm_out(array(
        'result'       => 'success',
        'deposit_name' => $new_name,
        'changes_left' => max(0, SNPM_MAX_NAME_CHANGES - ($changes + 1)),
    ));
}

// ── cancel_charge_order ────────────────────────────────────────────────────

if ($action === 'cancel_charge_order') {
    $od_id = snpm_req('od_id');
    if ($od_id === '') snpm_fail('invalid_param', '주문번호가 필요합니다.');

    $safe_od = snpm_escape($od_id);
    $order = sql_fetch(
        "SELECT od_id, od_status FROM g5_shoppoint_order
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}' AND od_settle_case='무통장'
         LIMIT 1",
        false
    );
    if (!$order) snpm_fail('not_found', '주문을 찾을 수 없습니다.');
    if ($order['od_status'] !== '주문') snpm_fail('invalid_status', '대기중인 주문만 취소할 수 있습니다.');

    $cancel_note = snpm_escape(date('Y-m-d H:i:s') . ' 사용자직접취소');
    sql_query(
        "UPDATE g5_shoppoint_order
         SET od_status='취소',
             od_shop_memo=CONCAT(IFNULL(od_shop_memo,''), ' [', '{$cancel_note}', ']')
         WHERE od_id='{$safe_od}' AND od_status='주문'",
        false
    );

    snpm_out(array('result' => 'success'));
}


// ── notify_deposit ─────────────────────────────────────────────────────────

if ($action === 'notify_deposit') {
    $od_id                 = snpm_req('od_id');
    $transfer_datetime     = snpm_req('transfer_datetime');
    $transfer_timezone_raw = snpm_req('transfer_timezone');
    $transfer_timezone     = (@timezone_open($transfer_timezone_raw) !== false && $transfer_timezone_raw !== '')
                             ? $transfer_timezone_raw : 'Asia/Seoul';
    $transfer_amount       = (int)snpm_req('transfer_amount');
    $memo                  = snpm_req('memo');
    $used_platform_account = (snpm_req('used_platform_account') === '0') ? 0 : 1;
    $alt_bank_name         = snpm_req('alt_bank_name');
    $alt_bank_account      = snpm_req('alt_bank_account');
    $alt_bank_holder       = snpm_req('alt_bank_holder');
    $alt_depositor_name    = snpm_req('alt_depositor_name');
    $alt_deposit_amount_raw = snpm_req('alt_deposit_amount');
    $alt_deposit_amount     = (preg_match('/^\d+(\.\d+)?$/', $alt_deposit_amount_raw) && (float)$alt_deposit_amount_raw > 0)
                              ? $alt_deposit_amount_raw : '0';
    $alt_notes             = snpm_req('alt_notes');

    if ($od_id === '') snpm_fail('invalid_param', '주문번호가 필요합니다.');
    if ($transfer_datetime === '') snpm_fail('invalid_param', '이체일시를 입력해 주세요.');
    if (!$used_platform_account && $alt_bank_name === '')
        snpm_fail('invalid_param', '입금하신 은행명을 입력해 주세요.');

    $safe_od              = snpm_escape($od_id);

    // 동일 주문 최대 10회 제한
    $exist_cnt = (int)(sql_fetch(
        "SELECT COUNT(*) AS cnt FROM g5_deposit_notify
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}'",
        false
    )['cnt'] ?? 0);
    if ($exist_cnt >= 10)
        snpm_fail('limit_exceeded', '동일 주문에 대한 입금신고 (포인트충전)은 최대 10회까지만 허용됩니다.');
    $safe_datetime        = snpm_escape($transfer_datetime);
    $safe_memo            = snpm_escape($memo);
    $safe_alt_bank        = snpm_escape($alt_bank_name);
    $safe_alt_acct        = snpm_escape($alt_bank_account);
    $safe_alt_holder      = snpm_escape($alt_bank_holder);
    $safe_alt_depositor   = snpm_escape($alt_depositor_name);
    $safe_alt_notes       = snpm_escape($alt_notes);

    $order = sql_fetch(
        "SELECT od_id, od_status, od_time FROM g5_shoppoint_order
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}' AND od_settle_case='무통장' LIMIT 1",
        false
    );
    if (!$order) snpm_fail('not_found', '주문을 찾을 수 없습니다.');
    $order_allow_from = date('Y-m-d H:i:s', strtotime($order['od_time']) - 86400);
    if ($transfer_datetime < $order_allow_from)
        snpm_fail('invalid_param', '이체일시는 주문 일시 기준 1일 이전보다 빠를 수 없습니다. (시간대 차이 허용)');

    $now = date('Y-m-d H:i:s');
    $acct_type = $used_platform_account ? '플랫폼안내계좌' : '별도계좌';
    $acct_info = '';
    if (!$used_platform_account) {
        $acct_info = " [{$alt_bank_name} {$alt_bank_account} {$alt_bank_holder}]";
        if ($alt_depositor_name !== '') $acct_info .= " 입금자:{$alt_depositor_name}";
        if ($alt_deposit_amount > 0)   $acct_info .= " 입금액:{$alt_deposit_amount}원";
        if ($alt_notes !== '')         $acct_info .= " 비고:{$alt_notes}";
    }

    // g5_deposit_notify 테이블에 기록
    $safe_tz = snpm_escape($transfer_timezone);
    sql_query(
        "INSERT INTO g5_deposit_notify
          (od_id, mb_id, dn_created_at, transfer_datetime, dn_timezone, transfer_amount,
           used_platform_account, alt_bank_name, alt_bank_account, alt_bank_holder,
           alt_depositor_name, alt_deposit_amount, alt_notes, memo, dn_status)
         VALUES
          ('{$safe_od}', '{$safe_mb}', '{$now}', '{$safe_datetime}', '{$safe_tz}', {$transfer_amount},
           {$used_platform_account}, '{$safe_alt_bank}', '{$safe_alt_acct}', '{$safe_alt_holder}',
           '{$safe_alt_depositor}', {$alt_deposit_amount}, '{$safe_alt_notes}',
           '{$safe_memo}', '대기')",
        false
    );

    // 기존 od_shop_memo에도 병기 (관리자 주문 뷰에서 확인 가능하도록)
    $notify_text = snpm_escape(sprintf(
        '[입금확인요청 %s][%s] 이체일시:%s, 이체금액:%d원%s, 메모:%s',
        $now, $acct_type, $transfer_datetime, $transfer_amount, $acct_info, $memo
    ));
    sql_query(
        "UPDATE g5_shoppoint_order
         SET od_shop_memo=CONCAT(IFNULL(od_shop_memo,''), '\n', '{$notify_text}'),
             od_mod_history=CONCAT(IFNULL(od_mod_history,''), '\n', '{$notify_text}')
         WHERE od_id='{$safe_od}'",
        false
    );

    snpm_out(array('result' => 'success'));
}

// ── get_notify_list ────────────────────────────────────────────────────────
if ($action === 'get_notify_list') {
    $od_id = snpm_req('od_id');
    if ($od_id === '') snpm_fail('invalid_param', '주문번호가 필요합니다.');
    $safe_od = snpm_escape($od_id);
    $order = sql_fetch(
        "SELECT od_id FROM g5_shoppoint_order
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}' LIMIT 1",
        false
    );
    if (!$order) snpm_fail('not_found', '주문을 찾을 수 없습니다.');
    $res = sql_query(
        "SELECT dn_created_at, transfer_datetime, dn_timezone, used_platform_account,
                alt_bank_name, alt_bank_account, alt_bank_holder, alt_depositor_name,
                alt_deposit_amount, memo, dn_status
         FROM g5_deposit_notify
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}'
         ORDER BY dn_id DESC",
        false
    );
    $list = [];
    while ($r = sql_fetch_array($res)) {
        $amt_raw = rtrim(rtrim((string)$r['alt_deposit_amount'], '0'), '.');
        if ($amt_raw === '' || $amt_raw === '.') $amt_raw = '0';
        $list[] = [
            'created_at'       => $r['dn_created_at'],
            'transfer_datetime'=> $r['transfer_datetime'],
            'timezone'         => $r['dn_timezone'],
            'platform_account' => (bool)(int)$r['used_platform_account'],
            'alt_bank'         => $r['alt_bank_name'],
            'alt_account'      => $r['alt_bank_account'],
            'alt_holder'       => $r['alt_bank_holder'],
            'depositor'        => $r['alt_depositor_name'],
            'amount'           => $amt_raw,
            'memo'             => (string)($r['memo'] ?? ''),
            'status'           => $r['dn_status'],
        ];
    }
    snpm_out(['result' => 'success', 'list' => $list, 'platform_accounts' => $od_platform_accounts]);
}


// ── notify_withdraw ────────────────────────────────────────────────────────
if ($action === 'notify_withdraw') {
    $od_id   = snpm_req('od_id');
    $message = isset($_POST['message']) ? trim((string)$_POST['message']) : '';
    if ($od_id === '') snpm_fail('invalid_param', '주문번호가 필요합니다.');
    $safe_od = snpm_escape($od_id);
    $safe_msg = snpm_escape(mb_substr($message, 0, 500));

    $order = sql_fetch(
        "SELECT od_id FROM g5_shoppoint_order4withdraw
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}' LIMIT 1",
        false
    );
    if (!$order) snpm_fail('not_found', '출금 주문을 찾을 수 없습니다.');

    // ── 메시지 필수 입력 ────────────────────────────────────────────
    if ($message === '') snpm_fail('message_required', '전달할 내용을 입력해 주세요.');

    // ── 마지막 요청 조회 (중복·쿨다운 체크) ─────────────────────────
    $last_wn = sql_fetch(
        "SELECT wn_created_at, wn_message FROM g5_withdraw_notify
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}'
         ORDER BY wn_id DESC LIMIT 1",
        false
    );
    // 동일 내용 중복 요청 → 조용히 성공 처리 (insert 없이 반환)
    if ($last_wn !== false && trim((string)($last_wn['wn_message'] ?? '')) === $message) {
        snpm_out(['result'=>'success','message'=>'출금 확인 요청이 전송되었습니다.\n담당자가 신속하게 확인 후 처리해 드리겠습니다.']);
    }
    // 쿨다운 30분: 조용히 대기 안내 반환
    if ($last_wn !== false && strtotime($last_wn['wn_created_at']) > time() - 1800) {
        snpm_out(['result'=>'success','message'=>'요청이 접수되어 처리 대기 중입니다.\n잠시 후 담당자가 순서대로 확인하겠습니다.']);
    }
    // 총 횟수 제한(10회): 조용히 성공 처리
    $exist_cnt = (int)(sql_fetch(
        "SELECT COUNT(*) AS cnt FROM g5_withdraw_notify WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}'",
        false
    )['cnt'] ?? 0);
    if ($exist_cnt >= 10) {
        snpm_out(['result'=>'success','message'=>'요청이 접수되어 처리 대기 중입니다.\n담당자가 순서대로 확인하고 있습니다.']);
    }

    $now = date('Y-m-d H:i:s');
    sql_query(
        "INSERT INTO g5_withdraw_notify (od_id, mb_id, wn_created_at, wn_message, wn_status)
         VALUES ('{$safe_od}', '{$safe_mb}', '{$now}', '{$safe_msg}', '대기')",
        false
    );
    $notify_text = snpm_escape(sprintf('[출금확인요청 %s] %s', $now, $message !== '' ? $message : '(메시지 없음)'));
    sql_query(
        "UPDATE g5_shoppoint_order4withdraw
         SET od_shop_memo=CONCAT(IFNULL(od_shop_memo,''), '\n', '{$notify_text}')
         WHERE od_id='{$safe_od}'",
        false
    );
    snpm_out(['result' => 'success', 'message' => '출금 확인 요청이 전송되었습니다.\n담당자가 신속하게 확인 후 처리해 드리겠습니다.']);
}

// ── get_withdraw_notify_list ───────────────────────────────────────────────
if ($action === 'get_withdraw_notify_list') {
    $od_id = snpm_req('od_id');
    if ($od_id === '') snpm_fail('invalid_param', '주문번호가 필요합니다.');
    $safe_od = snpm_escape($od_id);
    $order = sql_fetch(
        "SELECT od_id FROM g5_shoppoint_order4withdraw
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}' LIMIT 1",
        false
    );
    if (!$order) snpm_fail('not_found', '주문을 찾을 수 없습니다.');
    $res = sql_query(
        "SELECT wn_created_at, wn_message, wn_status, wn_admin_memo
         FROM g5_withdraw_notify
         WHERE od_id='{$safe_od}' AND mb_id='{$safe_mb}'
         ORDER BY wn_id DESC",
        false
    );
    $list = [];
    while ($r = sql_fetch_array($res)) {
        $list[] = [
            'created_at' => $r['wn_created_at'],
            'message'    => (string)($r['wn_message'] ?? ''),
            'status'     => $r['wn_status'],
            'admin_memo' => (string)($r['wn_admin_memo'] ?? ''),
        ];
    }
    snpm_out(['result' => 'success', 'list' => $list]);
}

snpm_fail('unknown_action', '알 수 없는 요청입니다.');
