<?php
if (!defined('ABSPATH')) exit;

/**
 * ACC_Sales_Controller
 * REST API for Sales: Customers, Products, Invoices
 * Integrated with Event-Driven Accounting Engine
 */
class ACC_Sales_Controller {

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

        // ── ACL-aware permission callbacks ────────────────────
        $pView = function_exists('acl_permission_callback') ? acl_permission_callback('sales.view')              : $p;
        $pCust = function_exists('acl_permission_callback') ? acl_permission_callback('sales.customers.manage')  : $p;
        $pCre  = function_exists('acl_permission_callback') ? acl_permission_callback('sales.invoices.create')   : $p;
        $pEdit = function_exists('acl_permission_callback') ? acl_permission_callback('sales.invoices.edit')     : $p;
        $pFin  = function_exists('acl_permission_callback') ? acl_permission_callback('sales.invoices.finalize') : $p;
        $pDel  = function_exists('acl_permission_callback') ? acl_permission_callback('sales.invoices.delete')   : $p;

        // ── Customers ──────────────────────────────────────────────────────
        register_rest_route($ns, '/sales/customers', [
            ['methods' => 'GET',  'callback' => [self::class,'get_customers'],   'permission_callback' => $pView],
            ['methods' => 'POST', 'callback' => [self::class,'create_customer'], 'permission_callback' => $pCust],
        ]);
        register_rest_route($ns, '/sales/customers/(?P<id>\d+)', [
            ['methods' => 'PUT',    'callback' => [self::class,'update_customer'], 'permission_callback' => $pCust],
            ['methods' => 'DELETE', 'callback' => [self::class,'delete_customer'], 'permission_callback' => $pCust],
        ]);

        // ── Products ───────────────────────────────────────────────────────
        register_rest_route($ns, '/sales/products', [
            ['methods' => 'GET',  'callback' => [self::class,'get_products'],   'permission_callback' => $pView],
            ['methods' => 'POST', 'callback' => [self::class,'create_product'], 'permission_callback' => $pCust],
        ]);
        register_rest_route($ns, '/sales/products/(?P<id>\d+)', [
            ['methods' => 'PUT',    'callback' => [self::class,'update_product'], 'permission_callback' => $pCust],
            ['methods' => 'DELETE', 'callback' => [self::class,'delete_product'], 'permission_callback' => $pCust],
        ]);

        // ── Sales Invoices ─────────────────────────────────────────────────
        register_rest_route($ns, '/sales/invoices', [
            ['methods' => 'GET',  'callback' => [self::class,'get_invoices'],   'permission_callback' => $pView],
            ['methods' => 'POST', 'callback' => [self::class,'create_invoice'], 'permission_callback' => $pCre],
        ]);
        register_rest_route($ns, '/sales/invoices/(?P<id>\d+)', [
            ['methods' => 'GET',    'callback' => [self::class,'get_invoice'],   'permission_callback' => $pView],
            ['methods' => 'PUT',    'callback' => [self::class,'update_invoice'], 'permission_callback' => $pEdit],
            ['methods' => 'DELETE', 'callback' => [self::class,'delete_invoice'], 'permission_callback' => $pDel],
        ]);
        register_rest_route($ns, '/sales/invoices/(?P<id>\d+)/finalize', [
            'methods' => 'POST', 'callback' => [self::class,'finalize_invoice'], 'permission_callback' => $pFin,
        ]);
        register_rest_route($ns, '/sales/invoices/(?P<id>\d+)/cancel', [
            'methods' => 'POST', 'callback' => [self::class,'cancel_invoice'], 'permission_callback' => $pDel,
        ]);

        // ── Reports ────────────────────────────────────────────────────────
        register_rest_route($ns, '/sales/reports/summary', [
            'methods' => 'GET', 'callback' => [self::class,'report_summary'], 'permission_callback' => 'acc_jwt_check_permission',
        ]);

        // ── Schema install ────────────────────────────────────────────────
        register_rest_route($ns, '/sales/install', [
            'methods' => 'POST', 'callback' => [self::class,'run_install'], 'permission_callback' => 'acc_jwt_check_permission',
        ]);
    }

    // =========================================================
    // CUSTOMERS
    // =========================================================

    public static function get_customers(WP_REST_Request $req): array {
        global $wpdb;
        $search = sanitize_text_field($req->get_param('search') ?? '');
        $where  = $search
            ? $wpdb->prepare('WHERE name LIKE %s OR code LIKE %s', "%{$search}%", "%{$search}%")
            : "WHERE is_active = 1";

        $rows = $wpdb->get_results(
            "SELECT * FROM {$wpdb->prefix}ai_sale_customers {$where} ORDER BY name ASC",
            ARRAY_A
        );
        return ['success' => true, 'data' => $rows ?: []];
    }

    public static function create_customer(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $wpdb->insert($wpdb->prefix . 'ai_sale_customers', [
            'code'         => sanitize_text_field($req->get_param('code')   ?? ''),
            'name'         => sanitize_text_field($req->get_param('name')),
            'phone'        => sanitize_text_field($req->get_param('phone')  ?? ''),
            'email'        => sanitize_email($req->get_param('email')       ?? ''),
            'address'      => sanitize_textarea_field($req->get_param('address') ?? ''),
            'tax_id'       => sanitize_text_field($req->get_param('tax_id') ?? ''),
            'credit_limit' => (float) ($req->get_param('credit_limit') ?? 0),
            'is_active'    => 1,
        ]);
        return new WP_REST_Response(['success' => true, 'id' => $wpdb->insert_id], 201);
    }

    public static function update_customer(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $id   = (int) $req->get_param('id');
        $data = [];
        foreach (['code','name','phone','email','address','tax_id'] as $f) {
            if ($req->has_param($f)) $data[$f] = sanitize_text_field($req->get_param($f));
        }
        if ($req->has_param('credit_limit')) $data['credit_limit'] = (float) $req->get_param('credit_limit');
        if ($req->has_param('is_active'))    $data['is_active']    = (int)   $req->get_param('is_active');
        if (!empty($data)) $wpdb->update($wpdb->prefix . 'ai_sale_customers', $data, ['id' => $id]);
        return new WP_REST_Response(['success' => true], 200);
    }

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

    // =========================================================
    // PRODUCTS
    // =========================================================

    public static function get_products(WP_REST_Request $req): array {
        global $wpdb;
        $search = sanitize_text_field($req->get_param('search') ?? '');
        $type   = sanitize_key($req->get_param('type') ?? '');

        $conds = ['is_active = 1'];
        if ($search) $conds[] = $wpdb->prepare('(name LIKE %s OR sku LIKE %s)', "%{$search}%", "%{$search}%");
        if ($type)   $conds[] = $wpdb->prepare('type = %s', $type);
        $where = 'WHERE ' . implode(' AND ', $conds);

        $rows = $wpdb->get_results(
            "SELECT * FROM {$wpdb->prefix}ai_sale_products {$where} ORDER BY name ASC",
            ARRAY_A
        );
        return ['success' => true, 'data' => $rows ?: []];
    }

    public static function create_product(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $allowed_types = ['product','service','other'];
        $wpdb->insert($wpdb->prefix . 'ai_sale_products', [
            'sku'            => sanitize_text_field($req->get_param('sku')         ?? ''),
            'name'           => sanitize_text_field($req->get_param('name')),
            'type'           => in_array($req->get_param('type'), $allowed_types) ? $req->get_param('type') : 'product',
            'unit'           => sanitize_text_field($req->get_param('unit')        ?? 'عدد'),
            'sale_price'     => (float) ($req->get_param('sale_price')  ?? 0),
            'cost_price'     => (float) ($req->get_param('cost_price')  ?? 0),
            'vat_rate'       => (float) ($req->get_param('vat_rate')    ?? 9),
            'stock_qty'      => (float) ($req->get_param('stock_qty')   ?? 0),
            'inv_product_id' => $req->get_param('inv_product_id') ? (int) $req->get_param('inv_product_id') : null,
            'description'    => sanitize_textarea_field($req->get_param('description') ?? ''),
            'is_active'      => 1,
        ]);
        return new WP_REST_Response(['success' => true, 'id' => $wpdb->insert_id], 201);
    }

    public static function update_product(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $id            = (int) $req->get_param('id');
        $allowed_types = ['product','service','other'];
        $data          = [];
        foreach (['sku','name','unit'] as $f) {
            if ($req->has_param($f)) $data[$f] = sanitize_text_field($req->get_param($f));
        }
        if ($req->has_param('description')) $data['description'] = sanitize_textarea_field($req->get_param('description'));
        if ($req->has_param('type'))        $data['type']        = in_array($req->get_param('type'), $allowed_types) ? $req->get_param('type') : 'product';
        if ($req->has_param('sale_price'))  $data['sale_price']  = (float) $req->get_param('sale_price');
        if ($req->has_param('cost_price'))  $data['cost_price']  = (float) $req->get_param('cost_price');
        if ($req->has_param('vat_rate'))    $data['vat_rate']    = (float) $req->get_param('vat_rate');
        if ($req->has_param('stock_qty'))      $data['stock_qty']      = (float) $req->get_param('stock_qty');
        if ($req->has_param('inv_product_id')) $data['inv_product_id'] = $req->get_param('inv_product_id') ? (int) $req->get_param('inv_product_id') : null;
        if ($req->has_param('is_active'))      $data['is_active']      = (int)   $req->get_param('is_active');
        if (!empty($data)) $wpdb->update($wpdb->prefix . 'ai_sale_products', $data, ['id' => $id]);
        return new WP_REST_Response(['success' => true], 200);
    }

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

    // =========================================================
    // INVOICES
    // =========================================================

    public static function get_invoices(WP_REST_Request $req): array {
        global $wpdb;
        $page   = max(1, (int) ($req->get_param('page')     ?? 1));
        $size   = max(1, min(100, (int) ($req->get_param('per_page') ?? 20)));
        $offset = ($page - 1) * $size;
        $status = sanitize_key($req->get_param('status') ?? '');

        $conds = [];
        $args  = [];
        if ($status) { $conds[] = 'i.status = %s'; $args[] = $status; }

        $uid = acl_current_user_id();
        $wh  = acl_scope_sql_filter($uid, 'warehouse', 'i.warehouse_id');
        if ($wh) {
            $conds[] = trim($wh['sql'], ' AND ');
            $args    = array_merge($args, $wh['args']);
        }

        $where     = $conds ? 'WHERE ' . implode(' AND ', $conds) : '';
        $count_sql = "SELECT COUNT(*) FROM {$wpdb->prefix}ai_sale_invoices i {$where}";
        $list_sql  = "SELECT i.*, c.name AS customer_name
             FROM {$wpdb->prefix}ai_sale_invoices i
             LEFT JOIN {$wpdb->prefix}ai_sale_customers c ON c.id = i.customer_id
             {$where}
             ORDER BY i.id DESC LIMIT %d OFFSET %d";

        if ($args) {
            $total = (int) $wpdb->get_var($wpdb->prepare($count_sql, ...$args));
            $rows  = $wpdb->get_results($wpdb->prepare($list_sql, ...array_merge($args, [$size, $offset])), ARRAY_A);
        } else {
            $total = (int) $wpdb->get_var($count_sql);
            $rows  = $wpdb->get_results($wpdb->prepare($list_sql, $size, $offset), ARRAY_A);
        }

        return ['success' => true, 'data' => $rows ?: [], 'total' => $total];
    }

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

        $invoice = $wpdb->get_row($wpdb->prepare(
            "SELECT i.*, c.name AS customer_name
             FROM {$wpdb->prefix}ai_sale_invoices i
             LEFT JOIN {$wpdb->prefix}ai_sale_customers c ON c.id = i.customer_id
             WHERE i.id = %d",
            $id
        ), ARRAY_A);

        if (!$invoice) return new WP_REST_Response(['success' => false, 'message' => 'یافت نشد'], 404);

        $deny = acl_deny_scope( acl_current_user_id(), 'warehouse', $invoice['warehouse_id'] ? (int)$invoice['warehouse_id'] : null, 'sales.view' );
        if ( $deny ) return $deny;

        $items = $wpdb->get_results($wpdb->prepare(
            "SELECT ii.*, p.name AS product_name, p.unit
             FROM {$wpdb->prefix}ai_sale_invoice_items ii
             LEFT JOIN {$wpdb->prefix}ai_sale_products p ON p.id = ii.product_id
             WHERE ii.invoice_id = %d ORDER BY ii.sort_order ASC",
            $id
        ), ARRAY_A);

        $invoice['items'] = $items ?: [];
        return new WP_REST_Response(['success' => true, 'data' => $invoice], 200);
    }

    public static function create_invoice(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;

        // Auto invoice number
        $year    = date('Y');
        $last_no = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}ai_sale_invoices WHERE invoice_no LIKE 'SALE-{$year}-%'");
        $inv_no  = sprintf('SALE-%s-%04d', $year, $last_no + 1);

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

        $wh_id = $req->get_param('warehouse_id') ? (int) $req->get_param('warehouse_id') : null;
        $deny  = acl_deny_scope( acl_current_user_id(), 'warehouse', $wh_id, 'sales.invoices.create' );
        if ( $deny ) return $deny;

        $wpdb->insert($wpdb->prefix . 'ai_sale_invoices', [
            'invoice_no'      => $req->get_param('invoice_no') ?: $inv_no,
            'customer_id'     => $req->get_param('customer_id')  ? (int) $req->get_param('customer_id')  : null,
            'warehouse_id'    => $req->get_param('warehouse_id') ? (int) $req->get_param('warehouse_id') : null,
            'invoice_date'    => sanitize_text_field($req->get_param('invoice_date') ?: date('Y-m-d')),
            'due_date'        => $req->get_param('due_date') ? sanitize_text_field($req->get_param('due_date')) : null,
            'status'          => 'draft',
            'subtotal'        => $totals['subtotal'],
            'discount_amount' => $totals['discount_amount'],
            'net_amount'      => $totals['net_amount'],
            'vat_amount'      => $totals['vat_amount'],
            'total_amount'    => $totals['total_amount'],
            'description'     => sanitize_textarea_field($req->get_param('description') ?? ''),
        ]);
        $invoice_id = (int) $wpdb->insert_id;

        self::_save_items($invoice_id, $items, 'ai_sale_invoice_items');

        return new WP_REST_Response(['success' => true, 'id' => $invoice_id, 'invoice_no' => $inv_no], 201);
    }

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

        $current = $wpdb->get_row($wpdb->prepare(
            "SELECT status FROM {$wpdb->prefix}ai_sale_invoices WHERE id = %d", $id
        ));
        if (!$current || $current->status !== 'draft') {
            return new WP_REST_Response(['success' => false, 'message' => 'فقط فاکتورهای پیش‌نویس قابل ویرایش هستند'], 400);
        }

        $existing = $wpdb->get_row($wpdb->prepare(
            "SELECT warehouse_id FROM {$wpdb->prefix}ai_sale_invoices WHERE id = %d", $id
        ), ARRAY_A);
        $deny = acl_deny_scope( acl_current_user_id(), 'warehouse', $existing['warehouse_id'] ? (int)$existing['warehouse_id'] : null, 'sales.invoices.edit' );
        if ( $deny ) return $deny;

        $wh_id = $req->get_param('warehouse_id') ? (int) $req->get_param('warehouse_id') : null;
        $deny  = acl_deny_scope( acl_current_user_id(), 'warehouse', $wh_id, 'sales.invoices.edit' );
        if ( $deny ) return $deny;

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

        $data = [
            'customer_id'     => $req->get_param('customer_id')  ? (int) $req->get_param('customer_id')  : null,
            'warehouse_id'    => $req->get_param('warehouse_id') ? (int) $req->get_param('warehouse_id') : null,
            'invoice_date'    => sanitize_text_field($req->get_param('invoice_date') ?: date('Y-m-d')),
            'due_date'        => $req->get_param('due_date') ? sanitize_text_field($req->get_param('due_date')) : null,
            'description'     => sanitize_textarea_field($req->get_param('description') ?? ''),
            'subtotal'        => $totals['subtotal'],
            'discount_amount' => $totals['discount_amount'],
            'net_amount'      => $totals['net_amount'],
            'vat_amount'      => $totals['vat_amount'],
            'total_amount'    => $totals['total_amount'],
        ];

        $wpdb->update($wpdb->prefix . 'ai_sale_invoices', $data, ['id' => $id]);
        $wpdb->delete($wpdb->prefix . 'ai_sale_invoice_items', ['invoice_id' => $id]);
        self::_save_items($id, $items, 'ai_sale_invoice_items');

        return new WP_REST_Response(['success' => true], 200);
    }

    public static function delete_invoice(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $id = (int) $req->get_param('id');
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT status, warehouse_id FROM {$wpdb->prefix}ai_sale_invoices WHERE id=%d", $id
        ), ARRAY_A);
        if (!$row) return new WP_REST_Response(['success' => false, 'message' => 'یافت نشد'], 404);
        $deny = acl_deny_scope( acl_current_user_id(), 'warehouse', $row['warehouse_id'] ? (int)$row['warehouse_id'] : null, 'sales.invoices.delete' );
        if ( $deny ) return $deny;
        if ($row['status'] === 'finalized') {
            return new WP_REST_Response(['success' => false, 'message' => 'فاکتور نهایی‌شده قابل حذف نیست'], 400);
        }
        $wpdb->delete($wpdb->prefix . 'ai_sale_invoice_items', ['invoice_id' => $id]);
        $wpdb->delete($wpdb->prefix . 'ai_sale_invoices',      ['id'         => $id]);
        return new WP_REST_Response(['success' => true], 200);
    }

    // ─── Finalize: create acc_event + call SP ────────────────────
    public static function finalize_invoice(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $id = (int) $req->get_param('id');

        $invoice = $wpdb->get_row($wpdb->prepare(
            "SELECT i.*, c.name AS customer_name
             FROM {$wpdb->prefix}ai_sale_invoices i
             LEFT JOIN {$wpdb->prefix}ai_sale_customers c ON c.id = i.customer_id
             WHERE i.id = %d",
            $id
        ), ARRAY_A);

        if (!$invoice) return new WP_REST_Response(['success' => false, 'message' => 'یافت نشد'], 404);
        $deny = acl_deny_scope( acl_current_user_id(), 'warehouse', $invoice['warehouse_id'] ? (int)$invoice['warehouse_id'] : null, 'sales.invoices.finalize' );
        if ( $deny ) return $deny;
        if ($invoice['status'] !== 'draft') {
            return new WP_REST_Response(['success' => false, 'message' => 'فقط فاکتور پیش‌نویس قابل نهایی‌سازی است'], 400);
        }

        // Create accounting event + post via unified engine
        require_once ACC_PATH . 'includes/helpers/acc-event-engine.php';
        $payload = [
            'invoice_id'    => $id,
            'invoice_no'    => $invoice['invoice_no'],
            'customer_name' => $invoice['customer_name'] ?? '',
            'net_amount'    => (float) $invoice['net_amount'],
            'vat_amount'    => (float) $invoice['vat_amount'],
            'discount'      => (float) $invoice['discount_amount'],
            'total_amount'  => (float) $invoice['total_amount'],
        ];

        $result = Acc_Event_Engine::emit_and_post([
            'event_type'      => 'SALES_INVOICE',
            'amount'          => (float) $invoice['total_amount'],
            'payload'         => $payload,
            'reference_type'  => 'sale_invoice',
            'reference_id'    => $id,
            'idempotency_key' => 'SALE_INV_' . $id,
            'source_module'   => 'sales',
        ]);

        if (!$result['success']) {
            return new WP_REST_Response([
                'success' => false,
                'message' => $result['message'] ?? 'خطای حسابداری',
            ], 400);
        }

        $event_id = (int) $result['event_id'];
        $msg = $result['message'] ?? 'سند صادر شد';

        // Update invoice status
        $wpdb->update($wpdb->prefix . 'ai_sale_invoices', [
            'status'       => 'finalized',
            'acc_event_id' => $event_id,
        ], ['id' => $id]);

        // ── ادغام انبار: ثبت خودکار سند خروج ──────────────────
        if ( class_exists('InventoryIntegration') ) {
            InventoryIntegration::on_sale_finalized( $id );
        }

        return new WP_REST_Response([
            'success'    => true,
            'message'    => $msg,
            'event_id'   => $event_id,
            'invoice_no' => $invoice['invoice_no'],
        ], 200);
    }

    public static function cancel_invoice(WP_REST_Request $req): WP_REST_Response {
        global $wpdb;
        $id = (int) $req->get_param('id');
        $wpdb->update($wpdb->prefix . 'ai_sale_invoices', ['status' => 'cancelled'], ['id' => $id]);
        return new WP_REST_Response(['success' => true], 200);
    }

    // =========================================================
    // REPORTS
    // =========================================================

    public static function report_summary(WP_REST_Request $req): array {
        global $wpdb;
        $from = sanitize_text_field($req->get_param('date_from') ?? date('Y-01-01'));
        $to   = sanitize_text_field($req->get_param('date_to')   ?? date('Y-m-d'));

        $summary = $wpdb->get_row($wpdb->prepare(
            "SELECT
                COUNT(*)                                       AS total_count,
                SUM(CASE WHEN status='finalized' THEN 1 ELSE 0 END) AS finalized_count,
                SUM(CASE WHEN status='draft'     THEN 1 ELSE 0 END) AS draft_count,
                SUM(CASE WHEN status='finalized' THEN total_amount ELSE 0 END) AS total_revenue,
                SUM(CASE WHEN status='finalized' THEN vat_amount   ELSE 0 END) AS total_vat,
                SUM(CASE WHEN status='finalized' THEN net_amount   ELSE 0 END) AS total_net
             FROM {$wpdb->prefix}ai_sale_invoices
             WHERE invoice_date BETWEEN %s AND %s",
            $from, $to
        ), ARRAY_A);

        $by_customer = $wpdb->get_results($wpdb->prepare(
            "SELECT c.name AS customer_name,
                    COUNT(*)       AS invoice_count,
                    SUM(i.total_amount) AS total
             FROM {$wpdb->prefix}ai_sale_invoices i
             LEFT JOIN {$wpdb->prefix}ai_sale_customers c ON c.id = i.customer_id
             WHERE i.status='finalized' AND i.invoice_date BETWEEN %s AND %s
             GROUP BY i.customer_id, c.name
             ORDER BY total DESC LIMIT 10",
            $from, $to
        ), ARRAY_A);

        return ['success' => true, 'summary' => $summary, 'by_customer' => $by_customer ?: []];
    }

    // =========================================================
    // SCHEMA INSTALL
    // =========================================================

    public static function run_install(WP_REST_Request $req): WP_REST_Response {
        $sql_file = ACC_PATH . 'includes/sql/sales_purchase_schema.sql';
        if (!file_exists($sql_file)) {
            return new WP_REST_Response(['success' => false, 'message' => 'فایل SQL یافت نشد'], 404);
        }
        global $wpdb;
        $sql     = file_get_contents($sql_file);
        $errors  = [];
        $stmts   = array_filter(array_map('trim', explode(";\n", $sql)), fn($s) => strlen($s) > 10);
        foreach ($stmts as $stmt) {
            $wpdb->query($stmt);
            if ($wpdb->last_error) $errors[] = substr($wpdb->last_error, 0, 150);
        }
        return new WP_REST_Response(['success' => empty($errors), 'errors' => $errors], 200);
    }

    // =========================================================
    // PRIVATE HELPERS
    // =========================================================

    private static function _calculate_totals(array $items): array {
        $subtotal = $discount_amount = $net_amount = $vat_amount = 0;
        foreach ($items as $item) {
            $qty      = (float) ($item['quantity']     ?? 1);
            $price    = (float) ($item['unit_price']   ?? 0);
            $disc_pct = (float) ($item['discount_pct'] ?? 0);
            $vat_rate = (float) ($item['vat_rate']     ?? 9);
            $base     = $qty * $price;
            $disc     = $base * ($disc_pct / 100);
            $net      = $base - $disc;
            $vat      = $net * ($vat_rate / 100);
            $subtotal        += $base;
            $discount_amount += $disc;
            $net_amount      += $net;
            $vat_amount      += $vat;
        }
        return [
            'subtotal'        => round($subtotal,        4),
            'discount_amount' => round($discount_amount, 4),
            'net_amount'      => round($net_amount,      4),
            'vat_amount'      => round($vat_amount,      4),
            'total_amount'    => round($net_amount + $vat_amount, 4),
        ];
    }

    private static function _save_items(int $invoice_id, array $items, string $table): void {
        global $wpdb;
        foreach ($items as $i => $item) {
            $qty      = (float) ($item['quantity']     ?? 1);
            $price    = (float) ($item['unit_price']   ?? 0);
            $disc_pct = (float) ($item['discount_pct'] ?? 0);
            $vat_rate = (float) ($item['vat_rate']     ?? 9);
            $base     = $qty * $price;
            $disc     = $base * ($disc_pct / 100);
            $net      = $base - $disc;
            $vat      = $net * ($vat_rate / 100);

            $wpdb->insert($wpdb->prefix . $table, [
                'invoice_id'   => $invoice_id,
                'product_id'   => !empty($item['product_id']) ? (int) $item['product_id'] : null,
                'description'  => sanitize_text_field($item['description'] ?? ''),
                'quantity'     => $qty,
                'unit_price'   => $price,
                'discount_pct' => $disc_pct,
                'vat_rate'     => $vat_rate,
                'line_net'     => round($net, 4),
                'line_vat'     => round($vat, 4),
                'line_total'   => round($net + $vat, 4),
                'sort_order'   => $i,
            ]);
        }
    }
}
