<?php
// includes/class-vmo-pay.php
if (!defined('ABSPATH')) exit;

/**
 * VMO Pay — Invoices, Payment Links, Customer Lifetime Value
 */
class VMO_Pay {

    private static $instance = null;

    public static function get_instance() {
        if (null === self::$instance) self::$instance = new self();
        return self::$instance;
    }

    public function init() {
        add_action('init', [$this, 'ensure_tables']);
        add_action('wp_ajax_vmo_pay_create_invoice',    [$this, 'ajax_create_invoice']);
        add_action('wp_ajax_vmo_pay_list_invoices',     [$this, 'ajax_list_invoices']);
        add_action('wp_ajax_vmo_pay_send_invoice',      [$this, 'ajax_send_invoice']);
        add_action('wp_ajax_vmo_pay_delete_invoice',    [$this, 'ajax_delete_invoice']);
        add_action('wp_ajax_vmo_pay_create_paylink',    [$this, 'ajax_create_paylink']);
        add_action('wp_ajax_vmo_pay_list_paylinks',     [$this, 'ajax_list_paylinks']);
        add_action('wp_ajax_vmo_pay_delete_paylink',    [$this, 'ajax_delete_paylink']);
        add_action('wp_ajax_vmo_pay_customer_ltv',      [$this, 'ajax_customer_ltv']);
        add_action('wp_ajax_vmo_pay_list_products',     [$this, 'ajax_list_products']);
        // Phase 10: Refunds, Deposits, Subscriptions, Quotes
        add_action('wp_ajax_vmo_pay_refund_invoice',    [$this, 'ajax_refund_invoice']);
        add_action('wp_ajax_vmo_pay_create_deposit',    [$this, 'ajax_create_deposit']);
        add_action('wp_ajax_vmo_pay_list_deposits',     [$this, 'ajax_list_deposits']);
        add_action('wp_ajax_vmo_pay_create_subscription', [$this, 'ajax_create_subscription']);
        add_action('wp_ajax_vmo_pay_list_subscriptions',  [$this, 'ajax_list_subscriptions']);
        add_action('wp_ajax_vmo_pay_cancel_subscription', [$this, 'ajax_cancel_subscription']);
        add_action('wp_ajax_vmo_pay_create_quote',      [$this, 'ajax_create_quote']);
        add_action('wp_ajax_vmo_pay_list_quotes',       [$this, 'ajax_list_quotes']);
        add_action('wp_ajax_vmo_pay_convert_quote',     [$this, 'ajax_convert_quote']);
        add_action('wp_ajax_vmo_pay_get_readiness',     [$this, 'ajax_get_readiness']);
        add_action('rest_api_init', [$this, 'register_routes']);
    }

    /* ── DB Tables ─────────────────────────── */

    public function ensure_tables() {
        global $wpdb;
        $charset = $wpdb->get_charset_collate();
        $inv = $wpdb->prefix . 'vmo_invoices';
        $pl  = $wpdb->prefix . 'vmo_payment_links';

        if ($wpdb->get_var("SHOW TABLES LIKE '$inv'") !== $inv) {
            $wpdb->query("CREATE TABLE $inv (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                invoice_number VARCHAR(32) NOT NULL,
                client_name VARCHAR(255) NOT NULL DEFAULT '',
                client_email VARCHAR(255) NOT NULL DEFAULT '',
                items LONGTEXT NOT NULL,
                subtotal DECIMAL(12,2) NOT NULL DEFAULT 0,
                tax DECIMAL(12,2) NOT NULL DEFAULT 0,
                total DECIMAL(12,2) NOT NULL DEFAULT 0,
                currency VARCHAR(3) NOT NULL DEFAULT 'usd',
                due_date DATE DEFAULT NULL,
                notes TEXT DEFAULT NULL,
                status ENUM('draft','sent','viewed','paid','overdue','cancelled') NOT NULL DEFAULT 'draft',
                pay_token VARCHAR(64) NOT NULL,
                stripe_payment_intent_id VARCHAR(128) DEFAULT NULL,
                sent_at DATETIME DEFAULT NULL,
                paid_at DATETIME DEFAULT NULL,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                INDEX idx_user (user_id),
                INDEX idx_status (status),
                UNIQUE KEY idx_token (pay_token)
            ) $charset");
        }

        if ($wpdb->get_var("SHOW TABLES LIKE '$pl'") !== $pl) {
            $wpdb->query("CREATE TABLE $pl (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                name VARCHAR(255) NOT NULL DEFAULT '',
                description TEXT DEFAULT NULL,
                amount_cents INT NOT NULL DEFAULT 0,
                currency VARCHAR(3) NOT NULL DEFAULT 'usd',
                allow_custom_amount TINYINT(1) NOT NULL DEFAULT 0,
                stripe_price_id VARCHAR(128) DEFAULT NULL,
                stripe_product_id VARCHAR(128) DEFAULT NULL,
                stripe_payment_link_id VARCHAR(128) DEFAULT NULL,
                stripe_payment_link_url VARCHAR(512) DEFAULT NULL,
                status ENUM('active','inactive') NOT NULL DEFAULT 'active',
                total_paid INT NOT NULL DEFAULT 0,
                total_revenue_cents BIGINT NOT NULL DEFAULT 0,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                INDEX idx_user (user_id),
                INDEX idx_status (status)
            ) $charset");
        }

        /* ── Quotes table ─────────────────────── */
        $qt = $wpdb->prefix . 'vmo_quotes';
        if ($wpdb->get_var("SHOW TABLES LIKE '$qt'") !== $qt) {
            $wpdb->query("CREATE TABLE $qt (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                quote_number VARCHAR(32) NOT NULL,
                client_name VARCHAR(255) NOT NULL DEFAULT '',
                client_email VARCHAR(255) NOT NULL DEFAULT '',
                items LONGTEXT NOT NULL,
                subtotal DECIMAL(12,2) NOT NULL DEFAULT 0,
                tax DECIMAL(12,2) NOT NULL DEFAULT 0,
                total DECIMAL(12,2) NOT NULL DEFAULT 0,
                currency VARCHAR(3) NOT NULL DEFAULT 'usd',
                valid_until DATE DEFAULT NULL,
                notes TEXT DEFAULT NULL,
                status ENUM('draft','sent','accepted','declined','converted','expired') NOT NULL DEFAULT 'draft',
                converted_invoice_id BIGINT UNSIGNED DEFAULT NULL,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                INDEX idx_user (user_id),
                INDEX idx_status (status)
            ) $charset");
        }

        /* ── Subscriptions table ──────────────── */
        $sub = $wpdb->prefix . 'vmo_subscriptions';
        if ($wpdb->get_var("SHOW TABLES LIKE '$sub'") !== $sub) {
            $wpdb->query("CREATE TABLE $sub (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                client_name VARCHAR(255) NOT NULL DEFAULT '',
                client_email VARCHAR(255) NOT NULL DEFAULT '',
                plan_name VARCHAR(255) NOT NULL DEFAULT '',
                amount_cents INT NOT NULL DEFAULT 0,
                currency VARCHAR(3) NOT NULL DEFAULT 'usd',
                billing_interval ENUM('week','month','year') NOT NULL DEFAULT 'month',
                stripe_subscription_id VARCHAR(128) DEFAULT NULL,
                stripe_customer_id VARCHAR(128) DEFAULT NULL,
                stripe_price_id VARCHAR(128) DEFAULT NULL,
                status ENUM('active','past_due','canceled','incomplete','trialing') NOT NULL DEFAULT 'incomplete',
                current_period_end DATETIME DEFAULT NULL,
                canceled_at DATETIME DEFAULT NULL,
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                INDEX idx_user (user_id),
                INDEX idx_status (status)
            ) $charset");
        }

        /* ── Refunds log ──────────────────────── */
        $ref = $wpdb->prefix . 'vmo_refunds';
        if ($wpdb->get_var("SHOW TABLES LIKE '$ref'") !== $ref) {
            $wpdb->query("CREATE TABLE $ref (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                invoice_id BIGINT UNSIGNED NOT NULL,
                amount DECIMAL(12,2) NOT NULL DEFAULT 0,
                reason TEXT DEFAULT NULL,
                stripe_refund_id VARCHAR(128) DEFAULT NULL,
                status ENUM('pending','succeeded','failed') NOT NULL DEFAULT 'pending',
                created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
                INDEX idx_user (user_id),
                INDEX idx_invoice (invoice_id)
            ) $charset");
        }

        /* ── Add deposit columns to invoices if missing ── */
        $has_deposit = $wpdb->get_var("SHOW COLUMNS FROM $inv LIKE 'is_deposit'");
        if (!$has_deposit) {
            $wpdb->query("ALTER TABLE $inv
                ADD COLUMN is_deposit TINYINT(1) NOT NULL DEFAULT 0 AFTER status,
                ADD COLUMN deposit_amount DECIMAL(12,2) NOT NULL DEFAULT 0 AFTER is_deposit,
                ADD COLUMN deposit_paid TINYINT(1) NOT NULL DEFAULT 0 AFTER deposit_amount,
                ADD COLUMN parent_invoice_id BIGINT UNSIGNED DEFAULT NULL AFTER deposit_paid");
        }

        /* ── Add refund columns to invoices if missing ── */
        $has_refund = $wpdb->get_var("SHOW COLUMNS FROM $inv LIKE 'refunded_amount'");
        if (!$has_refund) {
            $wpdb->query("ALTER TABLE $inv
                ADD COLUMN refunded_amount DECIMAL(12,2) NOT NULL DEFAULT 0 AFTER paid_at,
                ADD COLUMN refund_status ENUM('none','partial','full') NOT NULL DEFAULT 'none' AFTER refunded_amount");
        }
    }

    /* ── REST Routes (public invoice pay page) ─── */

    public function register_routes() {
        register_rest_route('vmo/v1', '/invoice/(?P<token>[a-zA-Z0-9]{32,64})', [
            'methods'  => 'GET',
            'callback' => [$this, 'render_invoice_page'],
            'permission_callback' => '__return_true',
        ]);
        register_rest_route('vmo/v1', '/invoice/(?P<token>[a-zA-Z0-9]{32,64})/pay', [
            'methods'  => 'POST',
            'callback' => [$this, 'create_invoice_checkout'],
            'permission_callback' => '__return_true',
        ]);
    }

    /* ── Invoice AJAX ──────────────────────── */

    public function ajax_create_invoice() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $client_name  = sanitize_text_field(wp_unslash($_POST['client_name'] ?? ''));
        $client_email = sanitize_email(wp_unslash($_POST['client_email'] ?? ''));
        $items_raw    = wp_unslash($_POST['items'] ?? '[]');
        $currency     = sanitize_text_field(wp_unslash($_POST['currency'] ?? 'usd'));
        $due_date     = sanitize_text_field(wp_unslash($_POST['due_date'] ?? ''));
        $notes        = sanitize_textarea_field(wp_unslash($_POST['notes'] ?? ''));
        $send_now     = !empty($_POST['send_now']);

        if (!$client_name || !$client_email) wp_send_json_error('Client name and email required');

        $items = json_decode($items_raw, true);
        if (!is_array($items) || empty($items)) wp_send_json_error('At least one line item is required');

        $subtotal = 0;
        $clean_items = [];
        foreach ($items as $item) {
            $desc  = sanitize_text_field($item['description'] ?? '');
            $qty   = max(1, intval($item['qty'] ?? 1));
            $price = round(floatval($item['price'] ?? 0), 2);
            if (!$desc || $price <= 0) continue;
            $line_total = round($qty * $price, 2);
            $subtotal += $line_total;
            $clean_items[] = ['description' => $desc, 'qty' => $qty, 'price' => $price, 'total' => $line_total];
        }
        if (empty($clean_items)) wp_send_json_error('At least one valid line item required');

        $total = round($subtotal, 2);
        $token = bin2hex(random_bytes(24));

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_invoices';

        // Generate invoice number INV-YYYYMMDD-XXXX
        $count = (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table WHERE user_id = %d", $uid
        ));
        $inv_number = 'INV-' . gmdate('Ymd') . '-' . str_pad($count + 1, 4, '0', STR_PAD_LEFT);

        $wpdb->insert($table, [
            'user_id'        => $uid,
            'invoice_number' => $inv_number,
            'client_name'    => $client_name,
            'client_email'   => $client_email,
            'items'          => wp_json_encode($clean_items),
            'subtotal'       => $subtotal,
            'total'          => $total,
            'currency'       => strtolower($currency),
            'due_date'       => $due_date ?: null,
            'notes'          => $notes,
            'status'         => $send_now ? 'sent' : 'draft',
            'pay_token'      => $token,
            'sent_at'        => $send_now ? current_time('mysql') : null,
            'created_at'     => current_time('mysql'),
        ], ['%d','%s','%s','%s','%s','%f','%f','%s','%s','%s','%s','%s','%s']);

        $invoice_id = $wpdb->insert_id;
        if (!$invoice_id) wp_send_json_error('Failed to save invoice');

        if ($send_now) {
            $this->send_invoice_email($invoice_id);
        }

        // Log interaction in CRM
        if (function_exists('vmo_upsert_contact') && function_exists('vmo_crm_log_interaction')) {
            $contact_id = vmo_upsert_contact((int) $uid, [
                'name' => $client_name,
                'email' => $client_email,
                'source' => 'vmo_pay',
                'primary_category' => 'lead',
                'lifecycle_stage' => 'engaged',
                'tags' => 'invoice,vmo_pay',
            ]);
            if ($contact_id) {
                vmo_crm_log_interaction((int) $uid, $contact_id, [
                    'interaction_type' => 'follow_up',
                    'subtype' => 'invoice_created',
                    'direction' => 'outbound',
                    'subject' => 'Invoice ' . $inv_number . ' created',
                    'summary' => "Invoice $inv_number created ($total " . strtoupper($currency) . ")",
                    'outcome' => $send_now ? 'pending' : 'successful',
                    'conversion_value' => $total,
                    'metadata' => [
                        'invoice_id' => $invoice_id,
                        'invoice_number' => $inv_number,
                        'status' => $send_now ? 'sent' : 'draft',
                    ],
                ]);
            }
        }

        wp_send_json_success([
            'id'             => $invoice_id,
            'invoice_number' => $inv_number,
            'total'          => $total,
            'status'         => $send_now ? 'sent' : 'draft',
            'pay_url'        => rest_url("vmo/v1/invoice/$token"),
        ]);
    }

    public function ajax_list_invoices() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_invoices';

        // Mark overdue invoices
        $wpdb->query($wpdb->prepare(
            "UPDATE $table SET status='overdue' WHERE user_id=%d AND status IN ('sent','viewed') AND due_date IS NOT NULL AND due_date < CURDATE()",
            $uid
        ));

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, invoice_number, client_name, client_email, total, currency, status, due_date, sent_at, paid_at, pay_token, created_at
             FROM $table WHERE user_id=%d ORDER BY created_at DESC LIMIT 100",
            $uid
        ), ARRAY_A);

        $stats = [
            'total_invoiced' => 0,
            'total_paid'     => 0,
            'total_pending'  => 0,
            'count'          => count($rows),
        ];
        foreach ($rows as &$r) {
            $r['pay_url'] = rest_url("vmo/v1/invoice/" . $r['pay_token']);
            $stats['total_invoiced'] += floatval($r['total']);
            if ($r['status'] === 'paid')   $stats['total_paid'] += floatval($r['total']);
            if (in_array($r['status'], ['sent','viewed','overdue'])) $stats['total_pending'] += floatval($r['total']);
        }
        unset($r);

        wp_send_json_success(['invoices' => $rows, 'stats' => $stats]);
    }

    public function ajax_send_invoice() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        $id  = intval($_POST['invoice_id'] ?? 0);
        if (!$uid || !$id) wp_send_json_error('Invalid request');

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_invoices';
        $inv = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table WHERE id=%d AND user_id=%d", $id, $uid
        ), ARRAY_A);
        if (!$inv) wp_send_json_error('Invoice not found');

        $wpdb->update($table, [
            'status'  => 'sent',
            'sent_at' => current_time('mysql'),
        ], ['id' => $id], ['%s','%s'], ['%d']);

        $this->send_invoice_email($id);
        wp_send_json_success(['message' => 'Invoice sent']);
    }

    public function ajax_delete_invoice() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        $id  = intval($_POST['invoice_id'] ?? 0);
        if (!$uid || !$id) wp_send_json_error('Invalid request');

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_invoices';
        $wpdb->delete($table, ['id' => $id, 'user_id' => $uid], ['%d','%d']);
        wp_send_json_success(['message' => 'Invoice deleted']);
    }

    /* ── Invoice Email ─────────────────────── */

    private function send_invoice_email($invoice_id) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_invoices';
        $inv = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE id=%d", $invoice_id), ARRAY_A);
        if (!$inv) return false;

        $pay_url = rest_url("vmo/v1/invoice/" . $inv['pay_token']);
        $items   = json_decode($inv['items'], true);
        $user    = get_userdata($inv['user_id']);
        $biz_name = $user ? $user->display_name : 'VMO Business';
        $cur_sym  = $this->currency_symbol(strtoupper($inv['currency']));

        $lines_html = '';
        foreach ($items as $it) {
            $lines_html .= '<tr>'
                . '<td style="padding:8px 12px;border-bottom:1px solid #eee">' . esc_html($it['description']) . '</td>'
                . '<td style="padding:8px 12px;border-bottom:1px solid #eee;text-align:center">' . intval($it['qty']) . '</td>'
                . '<td style="padding:8px 12px;border-bottom:1px solid #eee;text-align:right">' . $cur_sym . number_format($it['price'], 2) . '</td>'
                . '<td style="padding:8px 12px;border-bottom:1px solid #eee;text-align:right">' . $cur_sym . number_format($it['total'], 2) . '</td>'
                . '</tr>';
        }

        $due_text = $inv['due_date'] ? 'Due by ' . date('M j, Y', strtotime($inv['due_date'])) : '';

        $body = '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body style="font-family:Arial,sans-serif;background:#f4f6f9;margin:0;padding:20px">'
            . '<div style="max-width:600px;margin:0 auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,.08)">'
            . '<div style="background:linear-gradient(135deg,#0a0f16 0%,#1a1f2e 100%);padding:30px;text-align:center">'
            . '<h1 style="color:#0fe1ff;margin:0;font-size:24px">' . esc_html($biz_name) . '</h1>'
            . '<p style="color:#9fb2c8;margin:8px 0 0;font-size:14px">Invoice ' . esc_html($inv['invoice_number']) . '</p>'
            . '</div>'
            . '<div style="padding:30px">'
            . '<p style="color:#333;margin-bottom:20px">Hi ' . esc_html($inv['client_name']) . ',</p>'
            . '<p style="color:#666;margin-bottom:20px">Please find your invoice below. ' . esc_html($due_text) . '</p>'
            . '<table style="width:100%;border-collapse:collapse;margin-bottom:20px">'
            . '<thead><tr style="background:#f8f9fa"><th style="padding:10px 12px;text-align:left;font-size:12px;color:#666">Description</th><th style="padding:10px 12px;text-align:center;font-size:12px;color:#666">Qty</th><th style="padding:10px 12px;text-align:right;font-size:12px;color:#666">Price</th><th style="padding:10px 12px;text-align:right;font-size:12px;color:#666">Total</th></tr></thead>'
            . '<tbody>' . $lines_html . '</tbody>'
            . '<tfoot><tr style="background:#f0f7ff"><td colspan="3" style="padding:12px;text-align:right;font-weight:700;font-size:16px">Total</td><td style="padding:12px;text-align:right;font-weight:700;font-size:16px;color:#0a0f16">' . $cur_sym . number_format($inv['total'], 2) . '</td></tr></tfoot>'
            . '</table>'
            . ($inv['notes'] ? '<div style="background:#f8f9fa;padding:12px;border-radius:8px;margin-bottom:20px;color:#666;font-size:13px">' . esc_html($inv['notes']) . '</div>' : '')
            . '<div style="text-align:center;margin-top:24px">'
            . '<a href="' . esc_url($pay_url) . '" style="display:inline-block;padding:14px 36px;background:linear-gradient(135deg,#7c5cff,#0fe1ff);color:#fff;text-decoration:none;border-radius:8px;font-weight:700;font-size:16px">Pay Now</a>'
            . '</div>'
            . '</div>'
            . '<div style="background:#f8f9fa;padding:16px;text-align:center;color:#999;font-size:11px">Powered by VMO Pay</div>'
            . '</div></body></html>';

        $headers = ['Content-Type: text/html; charset=UTF-8'];
        return wp_mail(
            $inv['client_email'],
            'Invoice ' . $inv['invoice_number'] . ' from ' . $biz_name,
            $body,
            $headers
        );
    }

    /* ── Public Invoice Page ───────────────── */

    public function render_invoice_page(\WP_REST_Request $req) {
        global $wpdb;
        $token = sanitize_text_field($req->get_param('token'));
        $table = $wpdb->prefix . 'vmo_invoices';
        $inv = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table WHERE pay_token=%s", $token
        ), ARRAY_A);
        if (!$inv) return new \WP_REST_Response(['error' => 'Invoice not found'], 404);

        if ($inv['status'] === 'sent') {
            $wpdb->update($table, ['status' => 'viewed'], ['id' => $inv['id']]);
            $inv['status'] = 'viewed';
        }

        $items = json_decode($inv['items'], true);
        $user  = get_userdata($inv['user_id']);
        $biz   = $user ? $user->display_name : 'VMO Business';
        $cur   = $this->currency_symbol(strtoupper($inv['currency']));
        $is_paid = ($inv['status'] === 'paid');

        $lines_html = '';
        foreach ($items as $it) {
            $lines_html .= '<tr>'
                . '<td style="padding:12px 16px;border-bottom:1px solid #eee">' . esc_html($it['description']) . '</td>'
                . '<td style="padding:12px 16px;border-bottom:1px solid #eee;text-align:center">' . intval($it['qty']) . '</td>'
                . '<td style="padding:12px 16px;border-bottom:1px solid #eee;text-align:right">' . $cur . number_format($it['price'], 2) . '</td>'
                . '<td style="padding:12px 16px;border-bottom:1px solid #eee;text-align:right">' . $cur . number_format($it['total'], 2) . '</td>'
                . '</tr>';
        }

        $due_text = $inv['due_date'] ? '<p style="color:#666;margin:0 0 4px"><strong>Due:</strong> ' . date('M j, Y', strtotime($inv['due_date'])) . '</p>' : '';
        $status_badge = $is_paid
            ? '<span style="display:inline-block;padding:4px 12px;background:#d1fae5;color:#065f46;border-radius:20px;font-size:12px;font-weight:600">PAID</span>'
            : '<span style="display:inline-block;padding:4px 12px;background:#fef3c7;color:#92400e;border-radius:20px;font-size:12px;font-weight:600">' . strtoupper($inv['status']) . '</span>';

        $pay_btn = !$is_paid
            ? '<div style="text-align:center;margin-top:30px"><button onclick="payInvoice()" style="padding:16px 48px;background:linear-gradient(135deg,#7c5cff,#0fe1ff);color:#fff;border:none;border-radius:10px;font-size:18px;font-weight:700;cursor:pointer">Pay ' . $cur . number_format($inv['total'], 2) . '</button></div>'
            : '<div style="text-align:center;margin-top:30px;padding:20px;background:#d1fae5;border-radius:10px;color:#065f46;font-weight:600;font-size:16px">This invoice has been paid. Thank you!</div>';

        $html = '<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Invoice ' . esc_attr($inv['invoice_number']) . '</title>'
            . '<script src="https://js.stripe.com/v3/"></script>'
            . '</head><body style="font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background:#f4f6f9;margin:0;padding:20px">'
            . '<div style="max-width:680px;margin:0 auto;background:#fff;border-radius:16px;overflow:hidden;box-shadow:0 4px 24px rgba(0,0,0,.08)">'
            . '<div style="background:linear-gradient(135deg,#0a0f16 0%,#1a1f2e 100%);padding:36px;text-align:center">'
            . '<h1 style="color:#0fe1ff;margin:0;font-size:28px">' . esc_html($biz) . '</h1>'
            . '<p style="color:#9fb2c8;margin:10px 0 0;font-size:15px">Invoice ' . esc_html($inv['invoice_number']) . '</p>'
            . '</div>'
            . '<div style="padding:36px">'
            . '<div style="display:flex;justify-content:space-between;align-items:start;margin-bottom:24px">'
            . '<div>' . $due_text . '<p style="color:#666;margin:0"><strong>To:</strong> ' . esc_html($inv['client_name']) . '</p></div>'
            . '<div>' . $status_badge . '</div>'
            . '</div>'
            . '<table style="width:100%;border-collapse:collapse;margin-bottom:20px">'
            . '<thead><tr style="background:#f8f9fa"><th style="padding:12px 16px;text-align:left;font-size:13px;color:#666">Description</th><th style="padding:12px 16px;text-align:center;font-size:13px;color:#666">Qty</th><th style="padding:12px 16px;text-align:right;font-size:13px;color:#666">Price</th><th style="padding:12px 16px;text-align:right;font-size:13px;color:#666">Total</th></tr></thead>'
            . '<tbody>' . $lines_html . '</tbody>'
            . '<tfoot><tr style="background:#f0f7ff"><td colspan="3" style="padding:14px 16px;text-align:right;font-weight:700;font-size:18px">Total</td><td style="padding:14px 16px;text-align:right;font-weight:700;font-size:18px;color:#0a0f16">' . $cur . number_format($inv['total'], 2) . '</td></tr></tfoot>'
            . '</table>'
            . ($inv['notes'] ? '<div style="background:#f8f9fa;padding:14px;border-radius:10px;margin-bottom:20px;color:#666;font-size:13px;line-height:1.5">' . nl2br(esc_html($inv['notes'])) . '</div>' : '')
            . $pay_btn
            . '</div>'
            . '<div style="background:#f8f9fa;padding:16px;text-align:center;color:#999;font-size:11px">Powered by VMO Pay</div>'
            . '</div>';

        if (!$is_paid) {
            $html .= '<script>'
                . 'async function payInvoice(){'
                . 'var btn=document.querySelector("button");btn.disabled=true;btn.textContent="Processing…";'
                . 'try{'
                . 'var r=await fetch("' . esc_url(rest_url("vmo/v1/invoice/$token/pay")) . '",{method:"POST",headers:{"Content-Type":"application/json"}});'
                . 'var d=await r.json();'
                . 'if(d.url){window.location.href=d.url;}'
                . 'else{alert(d.error||"Payment failed");btn.disabled=false;btn.textContent="Retry Payment";}'
                . '}catch(e){alert("Payment error");btn.disabled=false;btn.textContent="Retry Payment";}'
                . '}'
                . '</script>';
        }

        $html .= '</body></html>';

        $response = new \WP_REST_Response();
        $response->set_headers(['Content-Type' => 'text/html; charset=UTF-8']);
        // Output raw HTML
        echo $html;
        exit;
    }

    /* ── Invoice Checkout via Stripe ───────── */

    public function create_invoice_checkout(\WP_REST_Request $req) {
        global $wpdb;
        $token = sanitize_text_field($req->get_param('token'));
        $table = $wpdb->prefix . 'vmo_invoices';
        $inv = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table WHERE pay_token=%s", $token
        ), ARRAY_A);
        if (!$inv) return new \WP_REST_Response(['error' => 'Invoice not found'], 404);
        if ($inv['status'] === 'paid') return new \WP_REST_Response(['error' => 'Already paid'], 400);

        require_once VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php';
        $sk = get_option('vmo_stripe_secret_key');
        if (!$sk) return new \WP_REST_Response(['error' => 'Payments not configured'], 500);
        \Stripe\Stripe::setApiKey($sk);

        $account_id = $this->get_merchant_account($inv['user_id']);
        $amount_cents = intval(round($inv['total'] * 100));
        $pay_url = rest_url("vmo/v1/invoice/$token");

        try {
            $params = [
                'mode'        => 'payment',
                'line_items'  => [[
                    'price_data' => [
                        'currency'     => $inv['currency'],
                        'product_data' => ['name' => 'Invoice ' . $inv['invoice_number']],
                        'unit_amount'  => $amount_cents,
                    ],
                    'quantity' => 1,
                ]],
                'customer_email' => $inv['client_email'],
                'success_url'    => $pay_url . '?paid=1',
                'cancel_url'     => $pay_url,
                'metadata'       => [
                    'vmo_invoice_id' => $inv['id'],
                    'invoice_number' => $inv['invoice_number'],
                ],
            ];

            if ($account_id) {
                $fee = intval(round($amount_cents * 0.029 + 30));
                $params['payment_intent_data'] = [
                    'application_fee_amount' => $fee,
                    'transfer_data' => ['destination' => $account_id],
                ];
            }

            $session = \Stripe\Checkout\Session::create($params);

            // Mark paid on webhook; for now redirect to Stripe checkout
            return new \WP_REST_Response(['url' => $session->url], 200);

        } catch (\Exception $e) {
            return new \WP_REST_Response(['error' => $e->getMessage()], 500);
        }
    }

    /* ── Payment Links AJAX ────────────────── */

    public function ajax_create_paylink() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $name        = sanitize_text_field(wp_unslash($_POST['name'] ?? ''));
        $description = sanitize_text_field(wp_unslash($_POST['description'] ?? ''));
        $amount      = floatval($_POST['amount'] ?? 0);
        $currency    = sanitize_text_field(wp_unslash($_POST['currency'] ?? 'usd'));
        $allow_custom = !empty($_POST['allow_custom']);

        if (!$name)       wp_send_json_error('Link name is required');
        if ($amount < 0.50 && !$allow_custom) wp_send_json_error('Minimum amount is $0.50');

        require_once VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php';
        $sk = get_option('vmo_stripe_secret_key');
        if (!$sk) wp_send_json_error('VMO Pay is not configured');
        \Stripe\Stripe::setApiKey($sk);

        $account_id = $this->get_merchant_account($uid);
        $amount_cents = intval(round($amount * 100));

        try {
            // Create Stripe product
            $stripe_params = ['name' => $name];
            if ($description) $stripe_params['description'] = $description;
            if ($account_id) {
                $product = \Stripe\Product::create($stripe_params, ['stripe_account' => $account_id]);
            } else {
                $product = \Stripe\Product::create($stripe_params);
            }

            // Create Stripe price
            $price_params = [
                'product'     => $product->id,
                'currency'    => $currency,
            ];
            if ($allow_custom) {
                $price_params['custom_unit_amount'] = [
                    'enabled' => true,
                    'preset'  => $amount_cents > 0 ? $amount_cents : null,
                ];
            } else {
                $price_params['unit_amount'] = $amount_cents;
            }

            if ($account_id) {
                $price = \Stripe\Price::create($price_params, ['stripe_account' => $account_id]);
                $link = \Stripe\PaymentLink::create([
                    'line_items'            => [['price' => $price->id, 'quantity' => 1]],
                    'application_fee_percent' => 2.9,
                ], ['stripe_account' => $account_id]);
            } else {
                $price = \Stripe\Price::create($price_params);
                $link = \Stripe\PaymentLink::create([
                    'line_items' => [['price' => $price->id, 'quantity' => 1]],
                ]);
            }

            global $wpdb;
            $table = $wpdb->prefix . 'vmo_payment_links';
            $wpdb->insert($table, [
                'user_id'                 => $uid,
                'name'                    => $name,
                'description'             => $description,
                'amount_cents'            => $amount_cents,
                'currency'                => strtolower($currency),
                'allow_custom_amount'     => $allow_custom ? 1 : 0,
                'stripe_product_id'       => $product->id,
                'stripe_price_id'         => $price->id,
                'stripe_payment_link_id'  => $link->id,
                'stripe_payment_link_url' => $link->url,
                'created_at'              => current_time('mysql'),
            ]);

            wp_send_json_success([
                'id'  => $wpdb->insert_id,
                'url' => $link->url,
                'name' => $name,
            ]);

        } catch (\Exception $e) {
            wp_send_json_error('VMO Pay error: ' . $e->getMessage());
        }
    }

    public function ajax_list_paylinks() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_payment_links';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, name, description, amount_cents, currency, allow_custom_amount, stripe_payment_link_url, status, total_paid, total_revenue_cents, created_at
             FROM $table WHERE user_id=%d ORDER BY created_at DESC LIMIT 100",
            $uid
        ), ARRAY_A);

        wp_send_json_success(['links' => $rows ?: []]);
    }

    public function ajax_delete_paylink() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        $id  = intval($_POST['link_id'] ?? 0);
        if (!$uid || !$id) wp_send_json_error('Invalid request');

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_payment_links';

        // Deactivate Stripe payment link if possible
        $row = $wpdb->get_row($wpdb->prepare("SELECT stripe_payment_link_id FROM $table WHERE id=%d AND user_id=%d", $id, $uid), ARRAY_A);
        if ($row && $row['stripe_payment_link_id']) {
            try {
                require_once VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php';
                $sk = get_option('vmo_stripe_secret_key');
                if ($sk) {
                    \Stripe\Stripe::setApiKey($sk);
                    $account_id = $this->get_merchant_account($uid);
                    $params = ['active' => false];
                    if ($account_id) {
                        \Stripe\PaymentLink::update($row['stripe_payment_link_id'], $params, ['stripe_account' => $account_id]);
                    } else {
                        \Stripe\PaymentLink::update($row['stripe_payment_link_id'], $params);
                    }
                }
            } catch (\Exception $e) {
                // Best-effort deactivation
            }
        }

        $wpdb->update($table, ['status' => 'inactive'], ['id' => $id, 'user_id' => $uid]);
        wp_send_json_success(['message' => 'Payment link deactivated']);
    }

    /* ── Customer LTV AJAX ─────────────────── */

    public function ajax_customer_ltv() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $purchases = $wpdb->prefix . 'vmo_contact_purchases';
        $contacts  = $wpdb->prefix . 'vmo_contacts';

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT c.contact_id, c.full_name, c.email, c.lifecycle_stage,
                    SUM(p.amount) as lifetime_value, COUNT(p.id) as purchase_count,
                    MAX(p.purchase_date) as last_purchase
             FROM $purchases p
             JOIN $contacts c ON c.contact_id = p.contact_id AND c.user_id = p.user_id
             WHERE p.user_id = %d AND p.status = 'completed'
             GROUP BY c.contact_id, c.full_name, c.email, c.lifecycle_stage
             ORDER BY lifetime_value DESC
             LIMIT 50",
            $uid
        ), ARRAY_A);

        $total_customers = count($rows);
        $total_revenue   = 0;
        foreach ($rows as &$r) {
            $r['lifetime_value'] = round(floatval($r['lifetime_value']), 2);
            $total_revenue += $r['lifetime_value'];
        }
        unset($r);

        $avg_ltv = $total_customers > 0 ? round($total_revenue / $total_customers, 2) : 0;
        $top_ltv = !empty($rows) ? $rows[0]['lifetime_value'] : 0;

        // Update contacts table with computed LTV (best-effort add column if missing)
        $has_col = $wpdb->get_var("SHOW COLUMNS FROM $contacts LIKE 'lifetime_value'");
        if (!$has_col) {
            $wpdb->query("ALTER TABLE $contacts ADD COLUMN lifetime_value DECIMAL(12,2) NOT NULL DEFAULT 0 AFTER value_potential");
        }
        foreach ($rows as $r) {
            $wpdb->query($wpdb->prepare(
                "UPDATE $contacts SET lifetime_value = %f WHERE contact_id = %s AND user_id = %d",
                $r['lifetime_value'], $r['contact_id'], $uid
            ));
        }

        wp_send_json_success([
            'customers'       => $rows,
            'total_customers' => $total_customers,
            'total_revenue'   => $total_revenue,
            'avg_ltv'         => $avg_ltv,
            'top_ltv'         => $top_ltv,
        ]);
    }

    /* ── Products List AJAX ────────────────── */

    public function ajax_list_products() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_store_products';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, name, product_type, price_cents, currency, stock_qty, stock_tracking, status
             FROM $table WHERE user_id = %d ORDER BY created_at DESC LIMIT 100",
            $uid
        ), ARRAY_A);
        wp_send_json_success(['products' => $rows ?: []]);
    }

    /* ── Helpers ───────────────────────────── */

    private function get_merchant_account($user_id) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_connected_accounts';
        return $wpdb->get_var($wpdb->prepare(
            "SELECT stripe_account_id FROM $table WHERE user_id = %d AND charges_enabled = 1",
            $user_id
        ));
    }

    private function currency_symbol($code) {
        $map = [
            'USD' => '$', 'EUR' => '€', 'GBP' => '£', 'CAD' => 'C$', 'AUD' => 'A$',
            'NGN' => '₦', 'KES' => 'KSh', 'ZAR' => 'R', 'INR' => '₹', 'JPY' => '¥',
        ];
        return $map[$code] ?? ($code . ' ');
    }

    private function load_stripe() {
        $sdk = defined('VMO_AI_BUILDER_DIR') ? VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php' : '';
        if ($sdk && file_exists($sdk)) {
            require_once $sdk;
            $key = get_option('vmo_stripe_secret_key', '');
            if ($key) {
                \Stripe\Stripe::setApiKey($key);
                return true;
            }
        }
        return false;
    }

    /* ══════════════════════════════════════════
       REFUND PROCESSING
       ══════════════════════════════════════════ */

    public function ajax_refund_invoice() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $invoice_id = intval($_POST['invoice_id'] ?? 0);
        $amount     = floatval($_POST['amount'] ?? 0);
        $reason     = sanitize_textarea_field($_POST['reason'] ?? '');

        if (!$invoice_id) wp_send_json_error('Invoice ID required');

        global $wpdb;
        $inv_table = $wpdb->prefix . 'vmo_invoices';
        $ref_table = $wpdb->prefix . 'vmo_refunds';

        $invoice = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $inv_table WHERE id = %d AND user_id = %d",
            $invoice_id, $uid
        ), ARRAY_A);

        if (!$invoice)                            wp_send_json_error('Invoice not found');
        if ($invoice['status'] !== 'paid')        wp_send_json_error('Only paid invoices can be refunded');
        if (empty($invoice['stripe_payment_intent_id'])) wp_send_json_error('No VMO Pay payment to refund');

        $max_refundable = $invoice['total'] - $invoice['refunded_amount'];
        if ($amount <= 0) $amount = $max_refundable;
        if ($amount > $max_refundable) wp_send_json_error('Refund exceeds available amount (' . $max_refundable . ')');

        if (!$this->load_stripe()) wp_send_json_error('VMO Pay is not configured');

        try {
            $params = [
                'payment_intent' => $invoice['stripe_payment_intent_id'],
                'amount'         => intval(round($amount * 100)),
            ];
            if ($reason) $params['reason'] = 'requested_by_customer';

            $acct = $this->get_merchant_account($uid);
            $opts = $acct ? ['stripe_account' => $acct] : [];

            $refund = \Stripe\Refund::create($params, $opts);

            $wpdb->insert($ref_table, [
                'user_id'          => $uid,
                'invoice_id'       => $invoice_id,
                'amount'           => $amount,
                'reason'           => $reason,
                'stripe_refund_id' => $refund->id,
                'status'           => $refund->status === 'succeeded' ? 'succeeded' : 'pending',
            ]);

            $new_refunded = $invoice['refunded_amount'] + $amount;
            $refund_status = ($new_refunded >= $invoice['total']) ? 'full' : 'partial';

            $wpdb->update($inv_table, [
                'refunded_amount' => $new_refunded,
                'refund_status'   => $refund_status,
                'status'          => $refund_status === 'full' ? 'cancelled' : 'paid',
            ], ['id' => $invoice_id]);

            wp_send_json_success([
                'message'         => 'Refund of ' . $this->currency_symbol(strtoupper($invoice['currency'])) . number_format($amount, 2) . ' processed',
                'stripe_refund'   => $refund->id,
                'refund_status'   => $refund_status,
                'refunded_total'  => $new_refunded,
            ]);
        } catch (\Stripe\Exception\ApiErrorException $e) {
            $wpdb->insert($ref_table, [
                'user_id'    => $uid,
                'invoice_id' => $invoice_id,
                'amount'     => $amount,
                'reason'     => $reason,
                'status'     => 'failed',
            ]);
            wp_send_json_error('VMO Pay refund failed: ' . $e->getMessage());
        }
    }

    /* ══════════════════════════════════════════
       DEPOSIT / PARTIAL PAYMENT
       ══════════════════════════════════════════ */

    public function ajax_create_deposit() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $client_name  = sanitize_text_field($_POST['client_name'] ?? '');
        $client_email = sanitize_email($_POST['client_email'] ?? '');
        $items_raw    = $_POST['items'] ?? '[]';
        $items        = json_decode(wp_unslash($items_raw), true);
        $currency     = strtolower(sanitize_text_field($_POST['currency'] ?? 'usd'));
        $deposit_pct  = floatval($_POST['deposit_percent'] ?? 50);
        $due_date     = sanitize_text_field($_POST['due_date'] ?? '');
        $notes        = sanitize_textarea_field($_POST['notes'] ?? '');

        if (!$client_name || !$client_email) wp_send_json_error('Client name and email required');
        if (!is_array($items) || empty($items)) wp_send_json_error('At least one item required');
        if ($deposit_pct <= 0 || $deposit_pct >= 100) wp_send_json_error('Deposit percent must be between 1-99');

        $subtotal = 0;
        foreach ($items as &$it) {
            $it['description'] = sanitize_text_field($it['description'] ?? '');
            $it['qty']         = max(1, intval($it['qty'] ?? 1));
            $it['unit_price']  = round(floatval($it['unit_price'] ?? 0), 2);
            $it['total']       = round($it['qty'] * $it['unit_price'], 2);
            $subtotal         += $it['total'];
        }
        unset($it);

        $tax   = round(floatval($_POST['tax'] ?? 0), 2);
        $total = round($subtotal + $tax, 2);
        $deposit_amount = round($total * ($deposit_pct / 100), 2);

        global $wpdb;
        $inv_table = $wpdb->prefix . 'vmo_invoices';

        $inv_num   = 'DEP-' . strtoupper(wp_generate_password(8, false));
        $pay_token = wp_generate_password(48, false);

        $wpdb->insert($inv_table, [
            'user_id'        => $uid,
            'invoice_number' => $inv_num,
            'client_name'    => $client_name,
            'client_email'   => $client_email,
            'items'          => wp_json_encode($items),
            'subtotal'       => $subtotal,
            'tax'            => $tax,
            'total'          => $deposit_amount,
            'currency'       => $currency,
            'due_date'       => $due_date ?: null,
            'notes'          => $notes . "\n[Deposit: {$deposit_pct}% of " . $this->currency_symbol(strtoupper($currency)) . number_format($total, 2) . "]",
            'status'         => 'draft',
            'pay_token'      => $pay_token,
            'is_deposit'     => 1,
            'deposit_amount' => $deposit_amount,
        ]);
        $deposit_id = $wpdb->insert_id;

        $balance_num   = 'BAL-' . strtoupper(wp_generate_password(8, false));
        $balance_token = wp_generate_password(48, false);
        $balance_amt   = round($total - $deposit_amount, 2);

        $wpdb->insert($inv_table, [
            'user_id'           => $uid,
            'invoice_number'    => $balance_num,
            'client_name'       => $client_name,
            'client_email'      => $client_email,
            'items'             => wp_json_encode($items),
            'subtotal'          => $subtotal,
            'tax'               => $tax,
            'total'             => $balance_amt,
            'currency'          => $currency,
            'due_date'          => $due_date ?: null,
            'notes'             => "Remaining balance after deposit invoice #{$inv_num}",
            'status'            => 'draft',
            'pay_token'         => $balance_token,
            'is_deposit'        => 0,
            'deposit_amount'    => 0,
            'parent_invoice_id' => $deposit_id,
        ]);

        wp_send_json_success([
            'deposit_invoice_id' => $deposit_id,
            'deposit_number'     => $inv_num,
            'deposit_amount'     => $deposit_amount,
            'balance_number'     => $balance_num,
            'balance_amount'     => $balance_amt,
            'total'              => $total,
            'deposit_percent'    => $deposit_pct,
        ]);
    }

    public function ajax_list_deposits() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $inv_table = $wpdb->prefix . 'vmo_invoices';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, invoice_number, client_name, client_email, total, deposit_amount, 
                    deposit_paid, currency, status, due_date, created_at
             FROM $inv_table WHERE user_id = %d AND is_deposit = 1
             ORDER BY created_at DESC LIMIT 100",
            $uid
        ), ARRAY_A);

        foreach ($rows as &$r) {
            $balance = $wpdb->get_row($wpdb->prepare(
                "SELECT id, invoice_number, total, status FROM $inv_table 
                 WHERE parent_invoice_id = %d AND user_id = %d LIMIT 1",
                $r['id'], $uid
            ), ARRAY_A);
            $r['balance_invoice'] = $balance ?: null;
        }
        unset($r);

        wp_send_json_success(['deposits' => $rows ?: []]);
    }

    /* ══════════════════════════════════════════
       SUBSCRIPTION / RECURRING BILLING
       ══════════════════════════════════════════ */

    public function ajax_create_subscription() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $client_name  = sanitize_text_field($_POST['client_name'] ?? '');
        $client_email = sanitize_email($_POST['client_email'] ?? '');
        $plan_name    = sanitize_text_field($_POST['plan_name'] ?? '');
        $amount_cents = intval($_POST['amount_cents'] ?? 0);
        $currency     = strtolower(sanitize_text_field($_POST['currency'] ?? 'usd'));
        $interval     = sanitize_text_field($_POST['interval'] ?? 'month');

        if (!$client_name || !$client_email) wp_send_json_error('Client name and email required');
        if (!$plan_name)                     wp_send_json_error('Plan name required');
        if ($amount_cents <= 0)              wp_send_json_error('Amount must be positive');
        if (!in_array($interval, ['week', 'month', 'year'])) wp_send_json_error('Invalid interval');

        if (!$this->load_stripe()) wp_send_json_error('VMO Pay is not configured');

        $acct = $this->get_merchant_account($uid);
        $opts = $acct ? ['stripe_account' => $acct] : [];

        try {
            $customer = \Stripe\Customer::create([
                'name'  => $client_name,
                'email' => $client_email,
            ], $opts);

            $product = \Stripe\Product::create([
                'name' => $plan_name,
            ], $opts);

            $price = \Stripe\Price::create([
                'unit_amount' => $amount_cents,
                'currency'    => $currency,
                'recurring'   => ['interval' => $interval],
                'product'     => $product->id,
            ], $opts);

            $subscription = \Stripe\Subscription::create([
                'customer'               => $customer->id,
                'items'                  => [['price' => $price->id]],
                'payment_behavior'       => 'default_incomplete',
                'payment_settings'       => ['save_default_payment_method' => 'on_subscription'],
                'expand'                 => ['latest_invoice.payment_intent'],
            ], $opts);

            global $wpdb;
            $sub_table = $wpdb->prefix . 'vmo_subscriptions';
            $wpdb->insert($sub_table, [
                'user_id'                => $uid,
                'client_name'            => $client_name,
                'client_email'           => $client_email,
                'plan_name'              => $plan_name,
                'amount_cents'           => $amount_cents,
                'currency'               => $currency,
                'billing_interval'       => $interval,
                'stripe_subscription_id' => $subscription->id,
                'stripe_customer_id'     => $customer->id,
                'stripe_price_id'        => $price->id,
                'status'                 => $subscription->status,
                'current_period_end'     => date('Y-m-d H:i:s', $subscription->current_period_end),
            ]);

            $client_secret = null;
            if (isset($subscription->latest_invoice->payment_intent->client_secret)) {
                $client_secret = $subscription->latest_invoice->payment_intent->client_secret;
            }

            wp_send_json_success([
                'subscription_id' => $subscription->id,
                'status'          => $subscription->status,
                'client_secret'   => $client_secret,
                'plan'            => $plan_name,
                'amount'          => $amount_cents / 100,
                'interval'        => $interval,
            ]);
        } catch (\Stripe\Exception\ApiErrorException $e) {
            wp_send_json_error('VMO Pay error: ' . $e->getMessage());
        }
    }

    public function ajax_list_subscriptions() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $sub_table = $wpdb->prefix . 'vmo_subscriptions';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, client_name, client_email, plan_name, amount_cents, currency, 
                    billing_interval, stripe_subscription_id, status, current_period_end, 
                    canceled_at, created_at
             FROM $sub_table WHERE user_id = %d ORDER BY created_at DESC LIMIT 100",
            $uid
        ), ARRAY_A);

        wp_send_json_success(['subscriptions' => $rows ?: []]);
    }

    public function ajax_cancel_subscription() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $sub_id = intval($_POST['subscription_id'] ?? 0);
        if (!$sub_id) wp_send_json_error('Subscription ID required');

        global $wpdb;
        $sub_table = $wpdb->prefix . 'vmo_subscriptions';
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $sub_table WHERE id = %d AND user_id = %d",
            $sub_id, $uid
        ), ARRAY_A);

        if (!$row)                          wp_send_json_error('Subscription not found');
        if ($row['status'] === 'canceled')  wp_send_json_error('Already canceled');
        if (empty($row['stripe_subscription_id'])) wp_send_json_error('No VMO Pay subscription linked');

        if (!$this->load_stripe()) wp_send_json_error('VMO Pay is not configured');

        try {
            $acct = $this->get_merchant_account($uid);
            $opts = $acct ? ['stripe_account' => $acct] : [];

            $stripe_sub = \Stripe\Subscription::retrieve($row['stripe_subscription_id'], $opts);
            $stripe_sub->cancel();

            $wpdb->update($sub_table, [
                'status'      => 'canceled',
                'canceled_at' => current_time('mysql'),
            ], ['id' => $sub_id]);

            wp_send_json_success([
                'message' => 'Subscription canceled',
                'plan'    => $row['plan_name'],
            ]);
        } catch (\Stripe\Exception\ApiErrorException $e) {
            wp_send_json_error('VMO Pay error: ' . $e->getMessage());
        }
    }

    /* ══════════════════════════════════════════
       QUOTE-TO-PAYMENT FLOW
       ══════════════════════════════════════════ */

    public function ajax_create_quote() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $client_name  = sanitize_text_field($_POST['client_name'] ?? '');
        $client_email = sanitize_email($_POST['client_email'] ?? '');
        $items_raw    = $_POST['items'] ?? '[]';
        $items        = json_decode(wp_unslash($items_raw), true);
        $currency     = strtolower(sanitize_text_field($_POST['currency'] ?? 'usd'));
        $valid_days   = intval($_POST['valid_days'] ?? 30);
        $notes        = sanitize_textarea_field($_POST['notes'] ?? '');

        if (!$client_name || !$client_email) wp_send_json_error('Client name and email required');
        if (!is_array($items) || empty($items)) wp_send_json_error('At least one item required');

        $subtotal = 0;
        foreach ($items as &$it) {
            $it['description'] = sanitize_text_field($it['description'] ?? '');
            $it['qty']         = max(1, intval($it['qty'] ?? 1));
            $it['unit_price']  = round(floatval($it['unit_price'] ?? 0), 2);
            $it['total']       = round($it['qty'] * $it['unit_price'], 2);
            $subtotal         += $it['total'];
        }
        unset($it);

        $tax   = round(floatval($_POST['tax'] ?? 0), 2);
        $total = round($subtotal + $tax, 2);

        global $wpdb;
        $qt_table = $wpdb->prefix . 'vmo_quotes';

        $quote_num = 'QT-' . strtoupper(wp_generate_password(8, false));
        $valid_until = date('Y-m-d', strtotime("+{$valid_days} days"));

        $wpdb->insert($qt_table, [
            'user_id'      => $uid,
            'quote_number' => $quote_num,
            'client_name'  => $client_name,
            'client_email' => $client_email,
            'items'        => wp_json_encode($items),
            'subtotal'     => $subtotal,
            'tax'          => $tax,
            'total'        => $total,
            'currency'     => $currency,
            'valid_until'  => $valid_until,
            'notes'        => $notes,
            'status'       => 'draft',
        ]);

        wp_send_json_success([
            'quote_id'     => $wpdb->insert_id,
            'quote_number' => $quote_num,
            'total'        => $total,
            'valid_until'  => $valid_until,
        ]);
    }

    public function ajax_list_quotes() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $qt_table = $wpdb->prefix . 'vmo_quotes';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, quote_number, client_name, client_email, total, currency, 
                    valid_until, status, converted_invoice_id, created_at
             FROM $qt_table WHERE user_id = %d ORDER BY created_at DESC LIMIT 100",
            $uid
        ), ARRAY_A);

        wp_send_json_success(['quotes' => $rows ?: []]);
    }

    public function ajax_convert_quote() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        $quote_id = intval($_POST['quote_id'] ?? 0);
        if (!$quote_id) wp_send_json_error('Quote ID required');

        global $wpdb;
        $qt_table  = $wpdb->prefix . 'vmo_quotes';
        $inv_table = $wpdb->prefix . 'vmo_invoices';

        $quote = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $qt_table WHERE id = %d AND user_id = %d",
            $quote_id, $uid
        ), ARRAY_A);

        if (!$quote) wp_send_json_error('Quote not found');
        if ($quote['status'] === 'converted') wp_send_json_error('Quote already converted');
        if ($quote['status'] === 'declined')  wp_send_json_error('Cannot convert a declined quote');

        $inv_num   = 'INV-' . strtoupper(wp_generate_password(8, false));
        $pay_token = wp_generate_password(48, false);

        $wpdb->insert($inv_table, [
            'user_id'        => $uid,
            'invoice_number' => $inv_num,
            'client_name'    => $quote['client_name'],
            'client_email'   => $quote['client_email'],
            'items'          => $quote['items'],
            'subtotal'       => $quote['subtotal'],
            'tax'            => $quote['tax'],
            'total'          => $quote['total'],
            'currency'       => $quote['currency'],
            'notes'          => "Converted from quote #{$quote['quote_number']}" . ($quote['notes'] ? "\n" . $quote['notes'] : ''),
            'status'         => 'draft',
            'pay_token'      => $pay_token,
        ]);
        $invoice_id = $wpdb->insert_id;

        $wpdb->update($qt_table, [
            'status'               => 'converted',
            'converted_invoice_id' => $invoice_id,
        ], ['id' => $quote_id]);

        wp_send_json_success([
            'invoice_id'     => $invoice_id,
            'invoice_number' => $inv_num,
            'quote_number'   => $quote['quote_number'],
            'total'          => $quote['total'],
            'message'        => "Quote #{$quote['quote_number']} converted to Invoice #{$inv_num}",
        ]);
    }

    /* ══════════════════════════════════════════
       PAYMENT READINESS CHECK
       ══════════════════════════════════════════ */

    public function ajax_get_readiness() {
        check_ajax_referer('vmo_nonce', 'nonce');
        $uid = get_current_user_id();
        if (!$uid) wp_send_json_error('Not logged in');

        global $wpdb;
        $ca_table = $wpdb->prefix . 'vmo_connected_accounts';
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT stripe_account_id, charges_enabled, payouts_enabled, 
                    details_submitted, created_at
             FROM $ca_table WHERE user_id = %d ORDER BY created_at DESC LIMIT 1",
            $uid
        ), ARRAY_A);

        if (!$row) {
            wp_send_json_success([
                'status'  => 'not_started',
                'label'   => 'Not Connected',
                'message' => 'You have not started VMO Pay setup yet.',
                'can_accept_payments' => false,
            ]);
            return;
        }

        $charges  = (bool) $row['charges_enabled'];
        $payouts  = (bool) $row['payouts_enabled'];
        $details  = (bool) $row['details_submitted'];

        if ($charges && $payouts && $details) {
            $status = 'active';
            $label  = 'Active';
            $msg    = 'VMO Pay is fully active. You can accept payments and receive payouts.';
        } elseif ($charges && !$payouts) {
            $status = 'restricted';
            $label  = 'Restricted';
            $msg    = 'You can accept payments but payouts are paused. Check VMO Pay verification requirements.';
        } elseif ($details && !$charges) {
            $status = 'verification_pending';
            $label  = 'Verification Pending';
            $msg    = 'Your details are submitted and VMO Pay verification is in progress.';
        } else {
            $status = 'onboarding_in_progress';
            $label  = 'Onboarding In Progress';
            $msg    = 'Your VMO Pay setup is incomplete. Please finish the onboarding process.';
        }

        wp_send_json_success([
            'status'              => $status,
            'label'               => $label,
            'message'             => $msg,
            'can_accept_payments' => $charges,
            'can_receive_payouts' => $payouts,
            'details_submitted'   => $details,
            'stripe_account_id'   => $row['stripe_account_id'],
            'connected_since'     => $row['created_at'],
        ]);
    }
}
