<?php
/**
 * Shipping_Controller — حمل و نقل کالا (مثل دیجی‌کالا)
 * Inbound (خرید) | Outbound (فروش) | Transfer
 *
 * Endpoints:
 *   GET    /acc/v1/ship/methods                              لیست روش‌های حمل
 *   GET    /acc/v1/ship/carriers                             شرکت‌های حمل
 *   POST   /acc/v1/ship/carriers
 *
 *   GET    /acc/v1/ship/shipments                            لیست محموله‌ها
 *   POST   /acc/v1/ship/shipments                            ایجاد محموله
 *   GET    /acc/v1/ship/shipments/{id}                       جزئیات + timeline
 *   PUT    /acc/v1/ship/shipments/{id}                       ویرایش
 *   POST   /acc/v1/ship/shipments/{id}/advance               تغییر وضعیت + ثبت stage
 *   POST   /acc/v1/ship/shipments/{id}/inspect               ثبت بازرسی
 *
 *   POST   /acc/v1/ship/from-purchase/{invoice_id}           ساخت از فاکتور خرید
 *   POST   /acc/v1/ship/from-sale/{invoice_id}               ساخت از فاکتور فروش
 *
 *   GET    /acc/v1/ship/dashboard                            آمار dashboard
 */

if ( ! defined( 'ABSPATH' ) ) exit;

class Shipping_Controller {

    /* ── جریان وضعیت‌ها برای inbound (خرید) ── */
    private static $INBOUND_FLOW = [
        'pending'    => 'در انتظار سفارش',
        'ordered'    => 'سفارش داده شده',
        'preparing'  => 'آماده‌سازی نزد تأمین‌کننده',
        'picked_up'  => 'تحویل به حمل‌کننده',
        'in_transit' => 'در حال حمل',
        'arrived'    => 'رسید به مقصد',
        'inspection' => 'در حال بازرسی',
        'received'   => 'پذیرفته شد در انبار',
        'rejected'   => 'مرجوع شد',
        'cancelled'  => 'لغو شد',
    ];

    /* ── جریان وضعیت‌ها برای outbound (فروش) ── */
    private static $OUTBOUND_FLOW = [
        'pending'          => 'در انتظار',
        'preparing'        => 'در حال آماده‌سازی در انبار',
        'packed'           => 'بسته‌بندی شد',
        'picked_up'        => 'تحویل به پیک',
        'in_transit'       => 'در حال حمل',
        'out_for_delivery' => 'در حال ارسال نهایی',
        'delivered'        => 'تحویل مشتری',
        'failed'           => 'تحویل ناموفق',
        'returned'         => 'مرجوع شد',
        'cancelled'        => 'لغو شد',
    ];

    public static function register_routes(): void {
        $ns = 'acc/v1';
        $p  = 'acc_jwt_check_permission';

        register_rest_route($ns, '/ship/methods',             ['methods'=>'GET','callback'=>[self::class,'list_methods'],'permission_callback'=>$p]);
        register_rest_route($ns, '/ship/carriers',           [['methods'=>'GET','callback'=>[self::class,'list_carriers'],'permission_callback'=>$p],
                                                               ['methods'=>'POST','callback'=>[self::class,'create_carrier'],'permission_callback'=>$p]]);
        register_rest_route($ns, '/ship/carriers/(?P<id>\d+)', [['methods'=>'PUT','callback'=>[self::class,'update_carrier'],'permission_callback'=>$p],
                                                                ['methods'=>'DELETE','callback'=>[self::class,'delete_carrier'],'permission_callback'=>$p]]);

        register_rest_route($ns, '/ship/shipments',           [['methods'=>'GET','callback'=>[self::class,'list_shipments'],'permission_callback'=>$p],
                                                                ['methods'=>'POST','callback'=>[self::class,'create_shipment'],'permission_callback'=>$p]]);
        register_rest_route($ns, '/ship/shipments/(?P<id>\d+)', [['methods'=>'GET','callback'=>[self::class,'get_shipment'],'permission_callback'=>$p],
                                                                  ['methods'=>'PUT','callback'=>[self::class,'update_shipment'],'permission_callback'=>$p]]);
        register_rest_route($ns, '/ship/shipments/(?P<id>\d+)/advance', ['methods'=>'POST','callback'=>[self::class,'advance_shipment'],'permission_callback'=>$p]);
        register_rest_route($ns, '/ship/shipments/(?P<id>\d+)/inspect', ['methods'=>'POST','callback'=>[self::class,'inspect_shipment'],'permission_callback'=>$p]);

        register_rest_route($ns, '/ship/from-purchase/(?P<invoice_id>\d+)', ['methods'=>'POST','callback'=>[self::class,'from_purchase'],'permission_callback'=>$p]);
        register_rest_route($ns, '/ship/from-sale/(?P<invoice_id>\d+)',     ['methods'=>'POST','callback'=>[self::class,'from_sale'],'permission_callback'=>$p]);
        register_rest_route($ns, '/ship/from-arag/(?P<order_id>\d+)',     ['methods'=>'POST','callback'=>[self::class,'from_arag_order'],'permission_callback'=>$p]);

        register_rest_route($ns, '/ship/dashboard', ['methods'=>'GET','callback'=>[self::class,'dashboard'],'permission_callback'=>$p]);
    }

    /* ════════════ روش‌ها و شرکت‌ها ════════════ */
    public static function list_methods( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $rows = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}ship_methods WHERE is_active=1 ORDER BY id", ARRAY_A );
        return new WP_REST_Response(['data'=>$rows ?? []]);
    }

    public static function list_carriers( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $rows = $wpdb->get_results(
            "SELECT c.*,
                    (SELECT COUNT(*) FROM {$wpdb->prefix}ship_shipments WHERE carrier_id=c.id) AS shipment_count
             FROM {$wpdb->prefix}ship_carriers c
             WHERE c.is_active=1 ORDER BY c.name",
            ARRAY_A
        );
        return new WP_REST_Response(['data'=>$rows ?? []]);
    }

    public static function create_carrier( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $wpdb->insert($wpdb->prefix.'ship_carriers', [
            'code'         => sanitize_text_field($req->get_param('code') ?? ''),
            'name'         => sanitize_text_field($req->get_param('name') ?? ''),
            'contact_name' => sanitize_text_field($req->get_param('contact_name') ?? ''),
            'phone'        => sanitize_text_field($req->get_param('phone') ?? ''),
            'email'        => sanitize_email($req->get_param('email') ?? ''),
            'address'      => sanitize_textarea_field($req->get_param('address') ?? ''),
        ]);
        return new WP_REST_Response(['id'=>$wpdb->insert_id,'success'=>true],201);
    }

    public static function update_carrier( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id = (int)$req->get_param('id');
        $data = [];
        foreach (['code','name','contact_name','phone','email','address'] as $k) {
            if ($req->get_param($k) !== null) $data[$k] = sanitize_textarea_field((string)$req->get_param($k));
        }
        if ($data) $wpdb->update($wpdb->prefix.'ship_carriers', $data, ['id'=>$id]);
        return new WP_REST_Response(['success'=>true]);
    }

    public static function delete_carrier( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $wpdb->update($wpdb->prefix.'ship_carriers', ['is_active'=>0], ['id'=>(int)$req->get_param('id')]);
        return new WP_REST_Response(['success'=>true]);
    }

    /* ════════════ Shipments ════════════ */
    public static function list_shipments( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;
        $wheres = []; $args = [];

        if ($v = $req->get_param('direction')) { $wheres[] = 's.direction = %s'; $args[] = sanitize_text_field($v); }
        if ($v = $req->get_param('status'))    { $wheres[] = 's.status = %s';    $args[] = sanitize_text_field($v); }
        if ($v = $req->get_param('ref_type'))  { $wheres[] = 's.ref_type = %s';  $args[] = sanitize_text_field($v); }
        if ($v = $req->get_param('search')) {
            $wheres[] = '(s.shipment_no LIKE %s OR s.tracking_code LIKE %s OR s.recipient_name LIKE %s)';
            $like = "%{$v}%"; $args[]=$like; $args[]=$like; $args[]=$like;
        }
        $where = $wheres ? 'WHERE ' . implode(' AND ', $wheres) : '';
        $sql = "SELECT s.*, m.name AS method_name, m.icon AS method_icon,
                       c.name AS carrier_full_name,
                       w.name AS warehouse_name
                FROM {$pre}ship_shipments s
                LEFT JOIN {$pre}ship_methods   m ON m.id = s.method_id
                LEFT JOIN {$pre}ship_carriers  c ON c.id = s.carrier_id
                LEFT JOIN {$pre}inv_warehouses w ON w.id = s.warehouse_id
                {$where}
                ORDER BY s.created_at DESC LIMIT 200";
        $rows = $args ? $wpdb->get_results($wpdb->prepare($sql, ...$args), ARRAY_A)
                      : $wpdb->get_results($sql, ARRAY_A);
        return new WP_REST_Response(['data'=>$rows ?? []]);
    }

    public static function get_shipment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;
        $id  = (int)$req->get_param('id');

        $shipment = $wpdb->get_row($wpdb->prepare(
            "SELECT s.*, m.name AS method_name, m.icon AS method_icon, m.avg_days,
                    c.name AS carrier_full_name, c.phone AS carrier_phone,
                    w.name AS warehouse_name,
                    fw.name AS from_warehouse_name
             FROM {$pre}ship_shipments s
             LEFT JOIN {$pre}ship_methods   m  ON m.id = s.method_id
             LEFT JOIN {$pre}ship_carriers  c  ON c.id = s.carrier_id
             LEFT JOIN {$pre}inv_warehouses w  ON w.id = s.warehouse_id
             LEFT JOIN {$pre}inv_warehouses fw ON fw.id = s.from_warehouse_id
             WHERE s.id = %d", $id
        ), ARRAY_A);
        if (!$shipment) return new WP_REST_Response(['message'=>'یافت نشد'], 404);

        $shipment['items'] = $wpdb->get_results($wpdb->prepare(
            "SELECT i.*, p.code AS product_code, p.unit AS product_unit
             FROM {$pre}ship_items i
             LEFT JOIN {$pre}inv_products p ON p.id = i.product_id
             WHERE i.shipment_id = %d", $id
        ), ARRAY_A) ?? [];

        $shipment['stages'] = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$pre}ship_stages WHERE shipment_id=%d ORDER BY created_at ASC", $id
        ), ARRAY_A) ?? [];

        $shipment['inspections'] = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$pre}ship_inspections WHERE shipment_id=%d ORDER BY inspection_date DESC", $id
        ), ARRAY_A) ?? [];

        // flow label
        $flow = $shipment['direction'] === 'outbound' ? self::$OUTBOUND_FLOW : self::$INBOUND_FLOW;
        $shipment['status_label'] = $flow[$shipment['status']] ?? $shipment['status'];
        $shipment['flow'] = $flow;

        return new WP_REST_Response(['data'=>$shipment]);
    }

    public static function create_shipment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;

        $data = self::_shipment_data($req);
        $items = $req->get_param('items') ?? [];

        // شماره خودکار
        if (!$data['shipment_no']) {
            $prefix = $data['direction'] === 'outbound' ? 'OUT' : ($data['direction'] === 'transfer' ? 'TRF' : 'IN');
            $year = date('Y');
            $last = (int)$wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$pre}ship_shipments WHERE shipment_no LIKE %s",
                "{$prefix}-{$year}-%"
            ));
            $data['shipment_no'] = sprintf('%s-%s-%04d', $prefix, $year, $last + 1);
        }

        $wpdb->insert("{$pre}ship_shipments", $data);
        $sid = (int)$wpdb->insert_id;

        // درج items
        if (is_array($items)) {
            foreach ($items as $it) {
                $wpdb->insert("{$pre}ship_items", [
                    'shipment_id'    => $sid,
                    'ref_item_id'    => !empty($it['ref_item_id']) ? (int)$it['ref_item_id'] : null,
                    'product_id'     => !empty($it['product_id']) ? (int)$it['product_id'] : null,
                    'product_name'   => sanitize_text_field($it['product_name'] ?? ''),
                    'qty_planned'    => (float)($it['qty_planned'] ?? 0),
                    'unit'           => sanitize_text_field($it['unit'] ?? ''),
                    'unit_weight_kg' => (float)($it['unit_weight_kg'] ?? 0),
                    'package_count'  => (int)($it['package_count'] ?? 0),
                ]);
            }
        }

        self::_log_stage($sid, $data['status'], 'محموله ایجاد شد');

        return new WP_REST_Response(['id'=>$sid,'shipment_no'=>$data['shipment_no'],'success'=>true], 201);
    }

    public static function update_shipment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id   = (int)$req->get_param('id');
        $data = self::_shipment_data($req, true);
        if ($data) $wpdb->update($wpdb->prefix.'ship_shipments', $data, ['id'=>$id]);
        return new WP_REST_Response(['success'=>true]);
    }

    /** انتقال به وضعیت بعد + ثبت stage */
    public static function advance_shipment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;
        $id  = (int)$req->get_param('id');

        $shipment = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$pre}ship_shipments WHERE id=%d", $id), ARRAY_A);
        if (!$shipment) return new WP_REST_Response(['message'=>'یافت نشد'], 404);

        $new_status = sanitize_text_field($req->get_param('status') ?? '');
        $flow = $shipment['direction'] === 'outbound' ? self::$OUTBOUND_FLOW : self::$INBOUND_FLOW;

        if (!isset($flow[$new_status])) {
            return new WP_REST_Response(['message'=>'وضعیت نامعتبر است'], 400);
        }

        // به‌روزرسانی timestamp‌های خاص
        $update = ['status' => $new_status];
        if ($new_status === 'picked_up' && !$shipment['actual_pickup_date']) {
            $update['actual_pickup_date'] = current_time('mysql');
        }
        if (in_array($new_status, ['received','delivered']) && !$shipment['actual_delivery_date']) {
            $update['actual_delivery_date'] = current_time('mysql');
        }

        $wpdb->update("{$pre}ship_shipments", $update, ['id'=>$id]);

        // ثبت stage
        $notes = sanitize_textarea_field($req->get_param('notes') ?? '');
        $lat = $req->get_param('lat') ? (float)$req->get_param('lat') : null;
        $lng = $req->get_param('lng') ? (float)$req->get_param('lng') : null;
        self::_log_stage($id, $new_status, $flow[$new_status], $notes, $lat, $lng);

        // اگر "received" یا "delivered" شد → ادغام با Inventory
        if ($new_status === 'received' && $shipment['direction'] === 'inbound') {
            self::_create_purchase_receipt($id);
        }
        if ($new_status === 'delivered' && $shipment['direction'] === 'outbound') {
            self::_create_sale_issue($id);
        }

        return new WP_REST_Response(['success'=>true, 'status'=>$new_status]);
    }

    /** ثبت بازرسی */
    public static function inspect_shipment( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $id = (int)$req->get_param('id');

        $uid = (int)get_current_user_id();
        $uname = '';
        if ($uid) { $u = get_user_by('id', $uid); $uname = $u ? $u->display_name : ''; }

        $wpdb->insert($wpdb->prefix.'ship_inspections', [
            'shipment_id'   => $id,
            'inspector_id'  => $uid ?: null,
            'inspector_name'=> $uname,
            'quality_score' => (int)($req->get_param('quality_score') ?? 80),
            'packaging_ok'  => $req->get_param('packaging_ok') ? 1 : 0,
            'quantity_ok'   => $req->get_param('quantity_ok') ? 1 : 0,
            'specs_ok'      => $req->get_param('specs_ok') ? 1 : 0,
            'damage_report' => sanitize_textarea_field($req->get_param('damage_report') ?? ''),
            'result'        => sanitize_text_field($req->get_param('result') ?? 'accepted'),
            'notes'         => sanitize_textarea_field($req->get_param('notes') ?? ''),
        ]);

        self::_log_stage($id, 'inspection', 'بازرسی ثبت شد',
            "امتیاز: " . (int)$req->get_param('quality_score'));

        return new WP_REST_Response(['id'=>$wpdb->insert_id, 'success'=>true], 201);
    }

    /* ════════════ ساخت از فاکتور خرید ════════════ */
    public static function from_purchase( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;
        $iid = (int)$req->get_param('invoice_id');

        $invoice = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$pre}ai_purchase_invoices WHERE id=%d", $iid
        ), ARRAY_A);
        if (!$invoice) return new WP_REST_Response(['message'=>'فاکتور یافت نشد'], 404);

        $items_input = $req->get_param('items');
        if (!is_array($items_input) || empty($items_input)) {
            return new WP_REST_Response(['message'=>'لیست آیتم‌ها الزامی است'], 400);
        }

        $method_id = $req->get_param('method_id') ? (int)$req->get_param('method_id') : null;
        $year = date('Y');
        $last = (int)$wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$pre}ship_shipments WHERE shipment_no LIKE %s", "IN-{$year}-%"
        ));
        $shipment_no = sprintf('IN-%s-%04d', $year, $last + 1);

        $wpdb->insert("{$pre}ship_shipments", [
            'shipment_no'  => $shipment_no,
            'direction'    => 'inbound',
            'status'       => 'pending',
            'method_id'    => $method_id,
            'carrier_id'   => $req->get_param('carrier_id') ? (int)$req->get_param('carrier_id') : null,
            'ref_type'     => 'purchase_invoice',
            'ref_id'       => $iid,
            'ref_no'       => $invoice['invoice_no'],
            'warehouse_id' => $invoice['warehouse_id'] ? (int)$invoice['warehouse_id'] : ($req->get_param('warehouse_id') ? (int)$req->get_param('warehouse_id') : null),
            'planned_delivery_date' => sanitize_text_field($req->get_param('planned_delivery_date') ?? ''),
            'notes'        => sanitize_textarea_field($req->get_param('notes') ?? ''),
        ]);
        $sid = (int)$wpdb->insert_id;

        // افزودن items
        foreach ($items_input as $it) {
            $inv_item = $wpdb->get_row($wpdb->prepare(
                "SELECT ii.*, sp.name AS product_name, sp.unit, sp.inv_product_id
                 FROM {$pre}ai_purchase_invoice_items ii
                 LEFT JOIN {$pre}ai_sale_products sp ON sp.id = ii.product_id
                 WHERE ii.id=%d", (int)($it['ref_item_id'] ?? 0)
            ), ARRAY_A);
            if (!$inv_item) continue;

            $wpdb->insert("{$pre}ship_items", [
                'shipment_id'    => $sid,
                'ref_item_id'    => (int)$inv_item['id'],
                'product_id'     => $inv_item['inv_product_id'] ? (int)$inv_item['inv_product_id'] : null,
                'product_name'   => $inv_item['product_name'] ?? '',
                'qty_planned'    => (float)($it['qty_planned'] ?? $inv_item['quantity']),
                'unit'           => $inv_item['unit'] ?? '',
                'unit_weight_kg' => (float)($it['unit_weight_kg'] ?? 0),
                'package_count'  => (int)($it['package_count'] ?? 0),
            ]);
        }

        self::_log_stage($sid, 'pending', "محموله از فاکتور خرید {$invoice['invoice_no']} ایجاد شد");
        return new WP_REST_Response(['id'=>$sid, 'shipment_no'=>$shipment_no, 'success'=>true], 201);
    }

    /* ════════════ ساخت از فاکتور فروش ════════════ */
    public static function from_sale( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;
        $iid = (int)$req->get_param('invoice_id');

        $invoice = $wpdb->get_row($wpdb->prepare(
            "SELECT i.*, c.name AS customer_name, c.phone AS customer_phone
             FROM {$pre}ai_sale_invoices i
             LEFT JOIN {$pre}ai_sale_customers c ON c.id = i.customer_id
             WHERE i.id=%d", $iid
        ), ARRAY_A);
        if (!$invoice) return new WP_REST_Response(['message'=>'فاکتور یافت نشد'], 404);

        $items_input = $req->get_param('items');
        if (!is_array($items_input) || empty($items_input)) {
            return new WP_REST_Response(['message'=>'لیست آیتم‌ها الزامی است'], 400);
        }

        $year = date('Y');
        $last = (int)$wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$pre}ship_shipments WHERE shipment_no LIKE %s", "OUT-{$year}-%"
        ));
        $shipment_no = sprintf('OUT-%s-%04d', $year, $last + 1);

        $wpdb->insert("{$pre}ship_shipments", [
            'shipment_no'      => $shipment_no,
            'direction'        => 'outbound',
            'status'           => 'pending',
            'method_id'        => $req->get_param('method_id') ? (int)$req->get_param('method_id') : null,
            'carrier_id'       => $req->get_param('carrier_id') ? (int)$req->get_param('carrier_id') : null,
            'ref_type'         => 'sale_invoice',
            'ref_id'           => $iid,
            'ref_no'           => $invoice['invoice_no'],
            'from_warehouse_id'=> $invoice['warehouse_id'] ? (int)$invoice['warehouse_id'] : null,
            'recipient_name'   => $invoice['customer_name'] ?? '',
            'recipient_phone'  => $invoice['customer_phone'] ?? '',
            'destination_address' => sanitize_textarea_field($req->get_param('destination_address') ?? ''),
            'planned_delivery_date' => sanitize_text_field($req->get_param('planned_delivery_date') ?? ''),
            'notes'            => sanitize_textarea_field($req->get_param('notes') ?? ''),
        ]);
        $sid = (int)$wpdb->insert_id;

        foreach ($items_input as $it) {
            $inv_item = $wpdb->get_row($wpdb->prepare(
                "SELECT ii.*, sp.name AS product_name, sp.unit, sp.inv_product_id
                 FROM {$pre}ai_sale_invoice_items ii
                 LEFT JOIN {$pre}ai_sale_products sp ON sp.id = ii.product_id
                 WHERE ii.id=%d", (int)($it['ref_item_id'] ?? 0)
            ), ARRAY_A);
            if (!$inv_item) continue;

            $wpdb->insert("{$pre}ship_items", [
                'shipment_id'    => $sid,
                'ref_item_id'    => (int)$inv_item['id'],
                'product_id'     => $inv_item['inv_product_id'] ? (int)$inv_item['inv_product_id'] : null,
                'product_name'   => $inv_item['product_name'] ?? '',
                'qty_planned'    => (float)($it['qty_planned'] ?? $inv_item['quantity']),
                'unit'           => $inv_item['unit'] ?? '',
            ]);
        }

        self::_log_stage($sid, 'pending', "محموله از فاکتور فروش {$invoice['invoice_no']} ایجاد شد");
        return new WP_REST_Response(['id'=>$sid, 'shipment_no'=>$shipment_no, 'success'=>true], 201);
    }

    public static function from_arag_order( WP_REST_Request $req ): WP_REST_Response {
        if ( ! file_exists( ACC_PATH . 'includes/helpers/arag-erp.php' ) ) {
            return new WP_REST_Response( [ 'message' => 'ماژول ERP فروشگاه نصب نیست' ], 500 );
        }
        require_once ACC_PATH . 'includes/helpers/arag-erp.php';
        $order_id = (int) $req->get_param( 'order_id' );
        $order    = Arag_Erp::get_order( $order_id );
        if ( ! $order ) {
            return new WP_REST_Response( [ 'message' => 'سفارش یافت نشد' ], 404 );
        }
        $res = Arag_Erp::create_shipment( $order );
        if ( empty( $res['success'] ) ) {
            return new WP_REST_Response( $res, 400 );
        }
        return new WP_REST_Response( $res, 201 );
    }

    /* ════════════ Dashboard ════════════ */
    public static function dashboard( WP_REST_Request $req ): WP_REST_Response {
        global $wpdb;
        $pre = $wpdb->prefix;

        $by_status = $wpdb->get_results(
            "SELECT direction, status, COUNT(*) AS cnt
             FROM {$pre}ship_shipments
             WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
             GROUP BY direction, status",
            ARRAY_A
        );

        $avg_time = $wpdb->get_row(
            "SELECT
                direction,
                AVG(TIMESTAMPDIFF(HOUR, actual_pickup_date, actual_delivery_date)) AS avg_hours
             FROM {$pre}ship_shipments
             WHERE actual_pickup_date IS NOT NULL AND actual_delivery_date IS NOT NULL
             GROUP BY direction",
            ARRAY_A
        );

        $quality = $wpdb->get_var(
            "SELECT AVG(quality_score) FROM {$pre}ship_inspections"
        );

        $active = $wpdb->get_var(
            "SELECT COUNT(*) FROM {$pre}ship_shipments
             WHERE status NOT IN ('received','delivered','cancelled','rejected','returned')"
        );

        $delayed = $wpdb->get_var(
            "SELECT COUNT(*) FROM {$pre}ship_shipments
             WHERE planned_delivery_date < NOW()
               AND actual_delivery_date IS NULL
               AND status NOT IN ('cancelled','rejected','returned')"
        );

        $shop_active = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$pre}ship_shipments
             WHERE ref_type = 'arag_order'
               AND status NOT IN ('delivered','cancelled','returned')"
        );

        return new WP_REST_Response([
            'by_status'      => $by_status ?? [],
            'avg_time'       => $avg_time,
            'avg_quality'    => round((float)$quality, 1),
            'active_count'   => (int)$active,
            'delayed_count'  => (int)$delayed,
            'shop_active'    => $shop_active,
        ]);
    }

    /* ════════════ توابع داخلی ════════════ */
    private static function _shipment_data( WP_REST_Request $req, bool $partial = false ): array {
        $fields = [
            'shipment_no'=>'text','direction'=>'text','status'=>'text',
            'method_id'=>'int_n','carrier_id'=>'int_n','carrier_name'=>'text',
            'ref_type'=>'text','ref_id'=>'int_n','ref_no'=>'text',
            'warehouse_id'=>'int_n','from_warehouse_id'=>'int_n',
            'origin_address'=>'textarea','origin_lat'=>'float','origin_lng'=>'float',
            'destination_address'=>'textarea','destination_lat'=>'float','destination_lng'=>'float',
            'recipient_name'=>'text','recipient_phone'=>'text',
            'driver_name'=>'text','driver_phone'=>'text','vehicle_plate'=>'text','tracking_code'=>'text',
            'planned_pickup_date'=>'text','planned_delivery_date'=>'text',
            'actual_pickup_date'=>'text','actual_delivery_date'=>'text',
            'shipping_cost'=>'float','total_weight_kg'=>'float','total_packages'=>'int',
            'notes'=>'textarea',
        ];
        $data = [];
        foreach ($fields as $k => $type) {
            $v = $req->get_param($k);
            if ($partial && $v === null) continue;
            switch ($type) {
                case 'int':       $data[$k] = (int)$v; break;
                case 'int_n':     $data[$k] = $v ? (int)$v : null; break;
                case 'float':     $data[$k] = $v !== null && $v !== '' ? (float)$v : null; break;
                case 'textarea':  $data[$k] = sanitize_textarea_field((string)$v); break;
                default:          $data[$k] = sanitize_text_field((string)$v);
            }
        }
        if (!$partial && empty($data['direction'])) $data['direction'] = 'inbound';
        if (!$partial && empty($data['status']))    $data['status']    = 'pending';
        return $data;
    }

    private static function _log_stage( int $shipment_id, string $stage, string $title, string $notes = '', ?float $lat = null, ?float $lng = null ): void {
        global $wpdb;
        $uid = (int)get_current_user_id();
        $uname = '';
        if ($uid) { $u = get_user_by('id', $uid); $uname = $u ? $u->display_name : ''; }
        $wpdb->insert($wpdb->prefix.'ship_stages', [
            'shipment_id' => $shipment_id,
            'stage'       => $stage,
            'title'       => $title,
            'notes'       => $notes,
            'lat'         => $lat,
            'lng'         => $lng,
            'actor_id'    => $uid ?: null,
            'actor_name'  => $uname,
        ]);
    }

    /** ساخت سند ورود انبار از shipment خرید */
    private static function _create_purchase_receipt( int $shipment_id ): void {
        global $wpdb;
        $pre = $wpdb->prefix;

        $shipment = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$pre}ship_shipments WHERE id=%d", $shipment_id
        ), ARRAY_A);
        if (!$shipment || !$shipment['warehouse_id']) return;

        // جلوگیری از ثبت دوبار
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$pre}inv_vouchers WHERE ref_type='shipment' AND ref_id=%d AND type='purchase_receipt'",
            $shipment_id
        ));
        if ($exists) return;

        $items = $wpdb->get_results($wpdb->prepare(
            "SELECT i.*, p.costing_method
             FROM {$pre}ship_items i
             LEFT JOIN {$pre}inv_products p ON p.id = i.product_id
             WHERE i.shipment_id=%d AND i.product_id IS NOT NULL",
            $shipment_id
        ), ARRAY_A);
        if (empty($items)) return;

        $year = date('Y');
        $last = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$pre}inv_vouchers WHERE type='purchase_receipt'");
        $voucher_no = 'PUR-' . str_pad($last + 1, 5, '0', STR_PAD_LEFT);

        $wpdb->insert("{$pre}inv_vouchers", [
            'voucher_no'   => $voucher_no,
            'type'         => 'purchase_receipt',
            'status'       => 'confirmed',
            'ref_id'       => $shipment_id,
            'ref_type'     => 'shipment',
            'ref_no'       => $shipment['shipment_no'],
            'date'         => date('Y-m-d'),
            'description'  => "از محموله {$shipment['shipment_no']}",
            'confirmed_at' => current_time('mysql'),
        ]);
        $vid = (int)$wpdb->insert_id;

        foreach ($items as $it) {
            $qty = (float)($it['qty_received'] > 0 ? $it['qty_received'] : $it['qty_planned']);
            $wpdb->insert("{$pre}inv_voucher_items", [
                'voucher_id'      => $vid,
                'product_id'      => (int)$it['product_id'],
                'to_warehouse_id' => (int)$shipment['warehouse_id'],
                'qty'             => $qty,
            ]);

            // افزایش موجودی
            $existing = $wpdb->get_row($wpdb->prepare(
                "SELECT qty FROM {$pre}inv_stock WHERE product_id=%d AND warehouse_id=%d",
                $it['product_id'], $shipment['warehouse_id']
            ), ARRAY_A);
            if ($existing) {
                $wpdb->update("{$pre}inv_stock",
                    ['qty' => (float)$existing['qty'] + $qty],
                    ['product_id'=>$it['product_id'], 'warehouse_id'=>$shipment['warehouse_id']]
                );
            } else {
                $wpdb->insert("{$pre}inv_stock", [
                    'product_id'   => (int)$it['product_id'],
                    'warehouse_id' => (int)$shipment['warehouse_id'],
                    'qty'          => $qty,
                ]);
            }
        }
    }

    /** ساخت سند خروج فروش از shipment فروش */
    private static function _create_sale_issue( int $shipment_id ): void {
        global $wpdb;
        $pre = $wpdb->prefix;

        $shipment = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$pre}ship_shipments WHERE id=%d", $shipment_id
        ), ARRAY_A);
        if (!$shipment || !$shipment['from_warehouse_id']) return;

        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$pre}inv_vouchers WHERE ref_type='shipment' AND ref_id=%d AND type='sale_issue'",
            $shipment_id
        ));
        if ($exists) return;

        $items = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$pre}ship_items WHERE shipment_id=%d AND product_id IS NOT NULL",
            $shipment_id
        ), ARRAY_A);
        if (empty($items)) return;

        $last = (int)$wpdb->get_var("SELECT COUNT(*) FROM {$pre}inv_vouchers WHERE type='sale_issue'");
        $voucher_no = 'SAL-' . str_pad($last + 1, 5, '0', STR_PAD_LEFT);

        $wpdb->insert("{$pre}inv_vouchers", [
            'voucher_no'   => $voucher_no,
            'type'         => 'sale_issue',
            'status'       => 'confirmed',
            'ref_id'       => $shipment_id,
            'ref_type'     => 'shipment',
            'ref_no'       => $shipment['shipment_no'],
            'date'         => date('Y-m-d'),
            'description'  => "تحویل به مشتری از محموله {$shipment['shipment_no']}",
            'confirmed_at' => current_time('mysql'),
        ]);
        $vid = (int)$wpdb->insert_id;

        foreach ($items as $it) {
            $qty = (float)$it['qty_planned'];
            $wpdb->insert("{$pre}inv_voucher_items", [
                'voucher_id'        => $vid,
                'product_id'        => (int)$it['product_id'],
                'from_warehouse_id' => (int)$shipment['from_warehouse_id'],
                'qty'               => $qty,
            ]);
            // کاهش موجودی
            $wpdb->query($wpdb->prepare(
                "UPDATE {$pre}inv_stock SET qty = qty - %f WHERE product_id=%d AND warehouse_id=%d",
                $qty, $it['product_id'], $shipment['from_warehouse_id']
            ));
        }
    }
}
