<?php
namespace Modules\Vendas\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Audit\AuditLogger;
use Gestoreasy\Core\Auth\UserContext;
use Gestoreasy\Core\Mail\Mailer;

class SalesOrdersController {
    public function list(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $page = max(1, (int)($r->query['page'] ?? 1));
        $per = min(100, max(1, (int)($r->query['per_page'] ?? 20)));
        $offset = ($page - 1) * $per;

        // Filtros opcionais
        $q = trim((string)($r->query['q'] ?? ''));
        $status = trim((string)($r->query['status'] ?? ''));
        $start = (string)($r->query['start'] ?? '');
        $end = (string)($r->query['end'] ?? '');

        $where = ['1=1'];
        $params = [];
        if ($q !== '') { $where[] = '(numero LIKE :q OR status LIKE :q)'; $params[':q'] = '%' . $q . '%'; }
        if ($status !== '') { $where[] = 'status = :status'; $params[':status'] = $status; }
        if ($start !== '') { $where[] = 'DATE(created_at) >= :start'; $params[':start'] = $start; }
        if ($end !== '') { $where[] = 'DATE(created_at) <= :end'; $params[':end'] = $end; }
        $whereSql = implode(' AND ', $where);

        $sql = "SELECT id, numero, cliente_id, valor_total, status, created_at FROM sales_orders WHERE $whereSql ORDER BY id DESC LIMIT $per OFFSET $offset";
        $stmt = $pdo->prepare($sql);
        foreach ($params as $k => $v) { $stmt->bindValue($k, $v); }
        $stmt->execute();
        $rows = $stmt->fetchAll();
        // Calcular valor bruto (soma dos itens) de forma tolerante
        if (is_array($rows) && count($rows) > 0) {
            foreach ($rows as &$row) {
                $gross = null;
                try {
                    $sum = $pdo->prepare('SELECT SUM(total) AS s FROM sales_order_items WHERE order_id = ?');
                    $sum->execute([(int)($row['id'] ?? 0)]);
                    $gross = $sum->fetch()['s'] ?? null;
                } catch (\Throwable $e) {
                    $gross = null;
                }
                $row['valor_bruto'] = $gross !== null ? (float)$gross : (float)($row['valor_total'] ?? 0);
            }
            unset($row);
        }
        // Total com os mesmos filtros
        $totalStmt = $pdo->prepare("SELECT COUNT(*) AS c FROM sales_orders WHERE $whereSql");
        foreach ($params as $k => $v) { $totalStmt->bindValue($k, $v); }
        $totalStmt->execute();
        $total = (int)($totalStmt->fetch()['c'] ?? 0);
        $totalPages = (int)ceil($total / $per);
        AuditLogger::logRead('sales_orders', null, ['endpoint' => 'vendas.pedidos.list', 'page' => $page, 'per_page' => $per]);
        return Response::json(['data' => $rows, 'page' => $page, 'per_page' => $per, 'total' => $total, 'total_pages' => $totalPages]);
    }

    public function getById(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $stmt = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status, created_at FROM sales_orders WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
        $items = [];
        try {
            $it = $pdo->prepare('SELECT product_id, quantidade, preco, total FROM sales_order_items WHERE order_id = ? ORDER BY id ASC');
            $it->execute([$id]);
            $items = $it->fetchAll();
        } catch (\Throwable $e) {
            // Sem itens persistidos ainda
        }
        AuditLogger::logRead('sales_orders', $id, ['endpoint' => 'vendas.pedidos.get_by_id']);
        return Response::json(['data' => ['order' => $row, 'items' => $items]]);
    }

    public function create(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $data = $r->body;
        $clienteId = (int)($data['cliente_id'] ?? 0);
        $valorTotal = (float)($data['valor_total'] ?? 0);
        $items = is_array($data['items'] ?? null) ? $data['items'] : [];
        if ($clienteId <= 0) return new Response(422, ['error' => ['code' => 'cliente_id_required', 'message' => 'cliente_id é obrigatório']]);
        // Validar cliente
        $cst = $pdo->prepare('SELECT id FROM clients WHERE id = ? LIMIT 1');
        $cst->execute([$clienteId]);
        if (!$cst->fetch()) return new Response(404, ['error' => ['code' => 'client_not_found', 'message' => 'Cliente não encontrado']]);

        // Se itens foram enviados, calcular o total com base no preço do produto
        $summary = [];
        if (count($items) > 0) {
            $valorTotal = 0.0;
            foreach ($items as $it) {
                $pid = (int)($it['product_id'] ?? 0);
                $qtd = (float)($it['quantidade'] ?? 0);
                if ($pid <= 0 || $qtd <= 0) {
                    return new Response(422, ['error' => ['code' => 'invalid_item', 'message' => 'Item inválido: product_id e quantidade > 0 requeridos']]);
                }
                $st = $pdo->prepare('SELECT id, codigo, preco FROM products WHERE id = ? LIMIT 1');
                $st->execute([$pid]);
                $prod = $st->fetch();
                if (!$prod) return new Response(404, ['error' => ['code' => 'product_not_found', 'message' => 'Produto não encontrado']]);
                $linhaTotal = $qtd * (float)$prod['preco'];
                $valorTotal += $linhaTotal;
                $summary[] = ['product_id' => $pid, 'quantidade' => $qtd, 'preco' => (float)$prod['preco'], 'total' => $linhaTotal];
            }
        } else {
            if ($valorTotal <= 0) return new Response(422, ['error' => ['code' => 'invalid_total', 'message' => 'valor_total deve ser > 0']]);
        }
        // Numeração sequencial: PD-##########
        $numero = null;
        try {
            $stmtSeq = $pdo->query("SELECT MAX(CAST(SUBSTRING(numero, 4) AS UNSIGNED)) AS max_seq FROM sales_orders WHERE numero LIKE 'PD-%'");
            $rowSeq = $stmtSeq->fetch();
            $next = ((int)($rowSeq['max_seq'] ?? 0)) + 1;
            $numero = 'PD-' . str_pad((string)$next, 10, '0', STR_PAD_LEFT);
        } catch (\Throwable $e) { $numero = null; }

        // Inserir pedido com número calculado ou fallback via id
        if ($numero !== null) {
            $ins = $pdo->prepare('INSERT INTO sales_orders (numero, cliente_id, valor_total, status) VALUES (?,?,?,?)');
            $ins->execute([$numero, $clienteId, $valorTotal, 'aberto']);
            $newId = (int)$pdo->lastInsertId();
        } else {
            // Fallback: insere vazio e usa o id para compor a sequência
            $ins = $pdo->prepare('INSERT INTO sales_orders (numero, cliente_id, valor_total, status) VALUES (?,?,?,?)');
            $ins->execute(['', $clienteId, $valorTotal, 'aberto']);
            $newId = (int)$pdo->lastInsertId();
            $numero = 'PD-' . str_pad((string)$newId, 10, '0', STR_PAD_LEFT);
            $pdo->prepare('UPDATE sales_orders SET numero = ? WHERE id = ?')->execute([$numero, $newId]);
        }
        // Persistir itens (se houver)
        if (count($summary) > 0) {
            try {
                $itIns = $pdo->prepare('INSERT INTO sales_order_items (order_id, product_id, quantidade, preco, total) VALUES (?,?,?,?,?)');
                foreach ($summary as $s) {
                    $itIns->execute([$newId, (int)$s['product_id'], (float)$s['quantidade'], (float)$s['preco'], (float)$s['total']]);
                }
            } catch (\Throwable $e) {
                // Ignorar se tabela ainda não existir
            }
        }
        $user = UserContext::get($r) ?? [];
        AuditLogger::logCreate(
            'sales_orders',
            $newId,
            ['numero' => $numero, 'cliente_id' => $clienteId, 'valor_total' => $valorTotal, 'status' => 'aberto'],
            ['endpoint' => 'vendas.pedidos.create', 'user_id' => ($user['sub'] ?? null)]
        );
        return Response::json(['ok' => true, 'id' => $newId, 'numero' => $numero], 201);
    }

    public function update(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $data = $r->body;
        $clienteId = isset($data['cliente_id']) ? (int)$data['cliente_id'] : null;
        $valorTotal = isset($data['valor_total']) ? (float)$data['valor_total'] : null;
        $status = $data['status'] ?? null;
        $items = is_array($data['items'] ?? null) ? $data['items'] : null;
        $beforeStmt = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status FROM sales_orders WHERE id = ? LIMIT 1');
        $beforeStmt->execute([$id]);
        $before = $beforeStmt->fetch();
        if (!$before) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
        // Bloquear edição de pedidos faturados/cancelados
        $curStatus = (string)($before['status'] ?? '');
        if (in_array($curStatus, ['faturado','cancelado'], true)) {
            return new Response(409, ['error' => ['code' => 'cannot_edit', 'message' => 'Pedido não pode ser editado no status atual']]);
        }

        try {
            $pdo->beginTransaction();
            // Atualização básica de cabeçalho
            $stmt = $pdo->prepare('UPDATE sales_orders SET cliente_id = COALESCE(?, cliente_id), valor_total = COALESCE(?, valor_total), status = COALESCE(?, status) WHERE id = ?');
            $stmt->execute([$clienteId, $valorTotal, $status, $id]);

            $after = $before;
            if ($clienteId !== null) $after['cliente_id'] = $clienteId;
            if ($valorTotal !== null) $after['valor_total'] = $valorTotal;
            if ($status !== null) $after['status'] = $status;

            // Edição de itens: substitui itens inteiros se enviados
            if (is_array($items)) {
                $novoTotal = 0.0;
                // Limpa itens existentes
                try { $pdo->prepare('DELETE FROM sales_order_items WHERE order_id = ?')->execute([$id]); } catch (\Throwable $e) {}
                // Insere itens novos com preço atual do produto, se não informado
                $itIns = $pdo->prepare('INSERT INTO sales_order_items (order_id, product_id, quantidade, preco, total) VALUES (?,?,?,?,?)');
                foreach ($items as $it) {
                    $pid = (int)($it['product_id'] ?? 0);
                    $qtd = (float)($it['quantidade'] ?? 0);
                    if ($pid <= 0 || $qtd <= 0) {
                        $pdo->rollBack();
                        return new Response(422, ['error' => ['code' => 'invalid_item', 'message' => 'Item inválido: product_id e quantidade > 0 requeridos']]);
                    }
                    $st = $pdo->prepare('SELECT id, preco FROM products WHERE id = ? LIMIT 1');
                    $st->execute([$pid]);
                    $prod = $st->fetch();
                    if (!$prod) { $pdo->rollBack(); return new Response(404, ['error' => ['code' => 'product_not_found', 'message' => 'Produto não encontrado', 'product_id' => $pid]]); }
                    $preco = isset($it['preco']) ? (float)$it['preco'] : (float)($prod['preco'] ?? 0);
                    $linhaTotal = $qtd * $preco; $novoTotal += $linhaTotal;
                    $itIns->execute([$id, $pid, $qtd, $preco, $linhaTotal]);
                }
                // Recalcula o valor_total do pedido
                $pdo->prepare('UPDATE sales_orders SET valor_total = ? WHERE id = ?')->execute([$novoTotal, $id]);
                $after['valor_total'] = $novoTotal;
            }

            $pdo->commit();
            AuditLogger::logUpdate('sales_orders', $id, $before ?: [], $after ?: [], ['endpoint' => 'vendas.pedidos.update']);
            return Response::json(['ok' => true]);
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            return new Response(500, ['error' => ['code' => 'update_failed', 'message' => $e->getMessage()]]);
        }
    }

    public function delete(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $beforeStmt = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status FROM sales_orders WHERE id = ? LIMIT 1');
        $beforeStmt->execute([$id]);
        $before = $beforeStmt->fetch();
        if (!$before) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
        try {
            $pdo->beginTransaction();
            // Remover itens dependentes para evitar violação de FK
            try { $pdo->prepare('DELETE FROM sales_order_items WHERE order_id = ?')->execute([$id]); } catch (\Throwable $e) {}
            // Remover o cabeçalho do pedido
            $stmt = $pdo->prepare('DELETE FROM sales_orders WHERE id = ?');
            $stmt->execute([$id]);
            AuditLogger::logDelete('sales_orders', $id, $before ?: [], ['endpoint' => 'vendas.pedidos.delete']);
            $pdo->commit();
            return Response::json(['ok' => true]);
        } catch (\Throwable $e) {
            $pdo->rollBack();
            return new Response(500, ['error' => ['code' => 'delete_failed', 'message' => 'Falha ao excluir pedido', 'details' => $e->getMessage()]]);
        }
    }

    // Faturar pedido: valida itens, verifica estoque, baixa estoque e marca como faturado
    public function invoice(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);

        // Buscar pedido
        $stmt = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status FROM sales_orders WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        $order = $stmt->fetch();
        if (!$order) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
        if (in_array(($order['status'] ?? ''), ['faturado', 'cancelado'], true)) {
            return new Response(409, ['error' => ['code' => 'cannot_invoice', 'message' => 'Pedido não pode ser faturado no status atual']]);
        }

        // Buscar itens
        $items = [];
        try {
            $it = $pdo->prepare('SELECT product_id, quantidade, preco, total FROM sales_order_items WHERE order_id = ? ORDER BY id ASC');
            $it->execute([$id]);
            $items = $it->fetchAll();
        } catch (\Throwable $e) {
            $items = [];
        }
        if (!$items || count($items) === 0) {
            return new Response(422, ['error' => ['code' => 'no_items', 'message' => 'Pedido sem itens não pode ser faturado']]);
        }

        try {
            $pdo->beginTransaction();

            // Verificar e baixar estoque item a item
            // Política de estoque: usar atributo do Request (via middleware), com fallback ao DB
            $allowNeg = (bool)($r->get('stock.allow_negative', false));
            if ($r->get('stock.allow_negative', null) === null) {
                try {
                    $cfgRow = $pdo->query('SELECT permitir_estoque_negativo FROM inventory_settings LIMIT 1')->fetch();
                    $allowNeg = ((int)($cfgRow['permitir_estoque_negativo'] ?? 0)) === 1;
                } catch (\Throwable $e) { $allowNeg = false; }
            }
            foreach ($items as $row) {
                $pid = (int)$row['product_id'];
                $qtd = (float)$row['quantidade'];
                // Verificar produto e estoque atual
                $pstmt = $pdo->prepare('SELECT id, estoque FROM products WHERE id = ? LIMIT 1');
                $pstmt->execute([$pid]);
                $prod = $pstmt->fetch();
                if (!$prod) {
                    $pdo->rollBack();
                    return new Response(404, ['error' => ['code' => 'product_not_found', 'message' => 'Produto não encontrado', 'product_id' => $pid]]);
                }
                $estoqueAtual = (float)($prod['estoque'] ?? 0);
                $before = $estoqueAtual;
                $after = $before - $qtd;
                if (!$allowNeg && $after < 0) {
                    // Log de tentativa bloqueada
                    try { \Gestoreasy\Core\Audit\AuditLogger::log('stock_policy_violation', 'sales_orders', $id, null, null, ['product_id' => $pid, 'available' => $estoqueAtual, 'required' => $qtd, 'endpoint' => 'vendas.pedidos.invoice']); } catch (\Throwable $e) {}
                    $pdo->rollBack();
                    return new Response(422, ['error' => ['code' => 'insufficient_stock', 'message' => 'Estoque insuficiente para realizar a venda. Disponível: ' . $estoqueAtual . ' unidades', 'product_id' => $pid, 'available' => $estoqueAtual, 'required' => $qtd]]);
                }
                // Atualizar estoque: tentar usar SP; fallback em UPDATE + movimento
                $spOk = false;
                try {
                    $call = $pdo->prepare('CALL sp_update_product_stock(?,?,?,?,?,?)');
                    $call->execute([$pid, -$qtd, 'sales_order', $id, (string)($order['numero'] ?? ''), $allowNeg ? 1 : 0]);
                    $spOk = true;
                } catch (\Throwable $e) { $spOk = false; }
                if (!$spOk) {
                    $upd = $pdo->prepare('UPDATE products SET estoque = :estoque WHERE id = :id');
                    $upd->execute([':estoque' => $after, ':id' => $pid]);
                    try {
                        $mov = $pdo->prepare('INSERT INTO inventory_movements (product_id, tipo, quantidade, motivo, stock_before, stock_after, origin_type, origin_id, origin_number) VALUES (?,?,?,?,?,?,?,?,?)');
                        $mov->execute([$pid, 'saida_venda', -$qtd, 'Venda faturada', $before, $after, 'sales_order', $id, (string)($order['numero'] ?? '')]);
                    } catch (\Throwable $e) { /* ignorar */ }
                }
            }

            // Atualizar status do pedido para faturado
            $before = $order;
            $pdo->prepare('UPDATE sales_orders SET status = ? WHERE id = ?')->execute(['faturado', $id]);

            $pdo->commit();
            $after = $before; $after['status'] = 'faturado';
            AuditLogger::logUpdate('sales_orders', $id, $before ?: [], $after ?: [], ['endpoint' => 'vendas.pedidos.invoice']);
            return Response::json(['ok' => true, 'id' => $id, 'status' => 'faturado']);
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) { $pdo->rollBack(); }
            return new Response(500, ['error' => ['code' => 'invoice_failed', 'message' => $e->getMessage()]]);
        }
    }

    public function exportCsv(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        // Separador CSV
        $sep = (string)($r->query['sep'] ?? ';');
        if (!in_array($sep, [';', ',', '\t'], true)) { $sep = ';'; }
        if ($sep === '\t') { $sep = "\t"; }

        // Filtros
        $q = trim((string)($r->query['q'] ?? ''));
        $status = trim((string)($r->query['status'] ?? ''));
        $start = (string)($r->query['start'] ?? '');
        $end = (string)($r->query['end'] ?? '');

        $where = ['1=1'];
        $params = [];
        if ($q !== '') { $where[] = '(so.numero LIKE :q OR so.status LIKE :q)'; $params[':q'] = '%' . $q . '%'; }
        if ($status !== '') { $where[] = 'so.status = :status'; $params[':status'] = $status; }
        if ($start !== '') { $where[] = 'DATE(so.created_at) >= :start'; $params[':start'] = $start; }
        if ($end !== '') { $where[] = 'DATE(so.created_at) <= :end'; $params[':end'] = $end; }
        $whereSql = implode(' AND ', $where);

        $sql = "SELECT so.id, so.numero, so.cliente_id, so.valor_total, so.status, so.created_at,\n                       c.nome AS cliente_nome\n                FROM sales_orders so\n                LEFT JOIN clients c ON c.id = so.cliente_id\n                WHERE $whereSql\n                ORDER BY so.created_at DESC";
        $stmt = $pdo->prepare($sql);
        foreach ($params as $k => $v) { $stmt->bindValue($k, $v); }
        $stmt->execute();
        $rows = $stmt->fetchAll();

        // Preparar CSV
        $bom = chr(0xEF) . chr(0xBB) . chr(0xBF);
        $out = fopen('php://temp', 'r+');
        fputs($out, $bom);
        $headers = ['ID','Número','Cliente ID','Cliente','Status','Valor Bruto','Valor Total','Criado em'];
        fputcsv($out, $headers, $sep);

        foreach ($rows as $r) {
            $orderId = (int)($r['id'] ?? 0);
            $gross = null;
            try {
                $sum = $pdo->prepare('SELECT SUM(total) AS s FROM sales_order_items WHERE order_id = ?');
                $sum->execute([$orderId]);
                $gross = $sum->fetch()['s'] ?? null;
            } catch (\Throwable $e) { $gross = null; }

            fputcsv($out, [
                $orderId,
                $r['numero'] ?? null,
                $r['cliente_id'] ?? null,
                $r['cliente_nome'] ?? null,
                $r['status'] ?? null,
                $gross !== null ? (float)$gross : (isset($r['valor_total']) ? (float)$r['valor_total'] : null),
                isset($r['valor_total']) ? (float)$r['valor_total'] : null,
                $r['created_at'] ?? null,
            ], $sep);
        }
        rewind($out);
        $csv = stream_get_contents($out);
        fclose($out);

        AuditLogger::logRead('sales_orders', null, ['endpoint' => 'vendas.pedidos.export_csv']);
        return new Response(200, $csv, [
            'Content-Type' => 'text/csv; charset=utf-8',
            'Content-Disposition' => 'attachment; filename="vendas_pedidos.csv"'
        ]);
    }

    // Checkout de pedido: atribuir particularidades, confirmar itens e marcar status como 'confirmado'
    public function checkout(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $data = $r->body;
        $particularidades = is_array($data['particularidades'] ?? null) ? $data['particularidades'] : [];
        $confirmados = is_array($data['items_confirmed'] ?? null) ? $data['items_confirmed'] : [];
        $respUser = UserContext::get($r) ?? [];

        // Buscar pedido
        $stmt = $pdo->prepare('SELECT id, status FROM sales_orders WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        $order = $stmt->fetch();
        if (!$order) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
        if (in_array((string)($order['status'] ?? ''), ['cancelado'], true)) {
            return new Response(409, ['error' => ['code' => 'cannot_checkout', 'message' => 'Pedido cancelado não pode ser conferido']]);
        }

        // Garantir coluna checkout_json (tolerância automigração)
        try { $pdo->query("ALTER TABLE sales_orders ADD COLUMN checkout_json JSON NULL"); } catch (\Throwable $e) { /* ignore */ }

        try {
            $pdo->beginTransaction();
            // Persistir dados de checkout
            $payload = json_encode([
                'particularidades' => $particularidades,
                'items_confirmed' => $confirmados,
                'user_id' => (int)($respUser['sub'] ?? 0),
                'ts' => date('c'),
            ], JSON_UNESCAPED_UNICODE);
            $pdo->prepare('UPDATE sales_orders SET checkout_json = ?, status = ? WHERE id = ?')->execute([$payload, 'confirmado', $id]);
            $pdo->commit();
            AuditLogger::logUpdate('sales_orders', $id, $order ?: [], ['status' => 'confirmado'], ['endpoint' => 'vendas.pedidos.checkout']);
            return Response::json(['ok' => true, 'id' => $id, 'status' => 'confirmado']);
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            return new Response(500, ['error' => ['code' => 'checkout_failed', 'message' => $e->getMessage()]]);
        }
    }

    // Registro de entrega: salvar assinatura, data/hora e observações
    public function registerDelivery(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $data = $r->body;
        $assinatura = (string)($data['assinatura_base64'] ?? '');
        $obs = (string)($data['observacoes'] ?? '');
        $ts = (string)($data['data_hora'] ?? date('c'));
        $respUser = UserContext::get($r) ?? [];

        // Garantir coluna delivery_json
        try { $pdo->query("ALTER TABLE sales_orders ADD COLUMN delivery_json JSON NULL"); } catch (\Throwable $e) { /* ignore */ }

        try {
            $pdo->beginTransaction();
            $payload = json_encode([
                'assinatura_base64' => $assinatura,
                'observacoes' => $obs,
                'data_hora' => $ts,
                'user_id' => (int)($respUser['sub'] ?? 0),
            ], JSON_UNESCAPED_UNICODE);
            $pdo->prepare('UPDATE sales_orders SET delivery_json = ? WHERE id = ?')->execute([$payload, $id]);
            $pdo->commit();
            AuditLogger::logUpdate('sales_orders', $id, [], ['delivery_json' => true], ['endpoint' => 'vendas.pedidos.delivery_register']);
            return Response::json(['ok' => true]);
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            return new Response(500, ['error' => ['code' => 'delivery_failed', 'message' => $e->getMessage()]]);
        }
    }

    // Enviar pedido por e-mail: compõe assunto/corpo padrão e utiliza Mailer::send
    public function sendEmail(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);

        // Buscar dados básicos do pedido
        $stmt = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status, created_at FROM sales_orders WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        $order = $stmt->fetch();
        if (!$order) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);

        // Coletar itens para resumo simples (tolerante)
        $items = [];
        try {
            $it = $pdo->prepare('SELECT product_id, quantidade, preco, total FROM sales_order_items WHERE order_id = ? ORDER BY id ASC');
            $it->execute([$id]);
            $items = $it->fetchAll();
        } catch (\Throwable $e) { /* ignore */ }

        $data = $r->body ?? [];
        $to = trim((string)($data['to'] ?? ''));
        $subject = trim((string)($data['subject'] ?? ''));
        $body = (string)($data['body'] ?? '');

        // Se não informado 'to', tenta e-mail do cliente
        if ($to === '') {
            try {
                $cstmt = $pdo->prepare('SELECT email, nome, nome_fantasia, razao_social FROM clients WHERE id = ? LIMIT 1');
                $cstmt->execute([(int)($order['cliente_id'] ?? 0)]);
                $client = $cstmt->fetch();
                if (!empty($client['email'])) { $to = (string)$client['email']; }
            } catch (\Throwable $e) { /* ignore */ }
        }
        if ($to === '') return new Response(422, ['error' => ['code' => 'email_required', 'message' => 'Destinatário (to) é obrigatório ou deve existir no cadastro do cliente']]);

        if ($subject === '') { $subject = 'Pedido ' . (string)($order['numero'] ?? $order['id']); }

        if ($body === '') {
            // Corpo padrão com resumo do pedido
            $lines = [];
            $lines[] = 'Olá,';
            $lines[] = '';
            $lines[] = 'Segue o resumo do pedido:';
            $lines[] = 'Número: ' . (string)($order['numero'] ?? $order['id']);
            $lines[] = 'Status: ' . (string)($order['status'] ?? '');
            $lines[] = 'Criado em: ' . (string)($order['created_at'] ?? '');
            $lines[] = 'Valor total: R$ ' . number_format((float)($order['valor_total'] ?? 0), 2, ',', '.');
            if (is_array($items) && count($items) > 0) {
                $lines[] = '';
                $lines[] = 'Itens:';
                foreach ($items as $it) {
                    $lines[] = sprintf('- Produto #%s • Qtd: %s • Preço: R$ %s • Total: R$ %s',
                        (string)($it['product_id'] ?? ''),
                        (string)($it['quantidade'] ?? ''),
                        number_format((float)($it['preco'] ?? 0), 2, ',', '.'),
                        number_format((float)($it['total'] ?? 0), 2, ',', '.')
                    );
                }
            }
            $lines[] = '';
            $lines[] = 'Qualquer dúvida, estamos à disposição.';
            $body = implode("\n", $lines);
        }

        // Enviar
        $ok = Mailer::send($to, $subject, $body);
        AuditLogger::log('send_email', 'sales_orders', (int)$order['id'], null, ['to' => $to, 'subject' => $subject], ['endpoint' => 'vendas.pedidos.email_send']);
        return Response::json(['ok' => (bool)$ok]);
    }

    // Duplicar pedido com vínculo ao original
    public function duplicate(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $params = $r->get('params', []);
        $id = (int)($params[0] ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);

        // Buscar cabeçalho e itens
        $hdr = $pdo->prepare('SELECT id, numero, cliente_id, valor_total, status FROM sales_orders WHERE id = ? LIMIT 1');
        $hdr->execute([$id]);
        $order = $hdr->fetch();
        if (!$order) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Pedido não encontrado']]);
        $items = [];
        try { $it = $pdo->prepare('SELECT product_id, quantidade, preco, total FROM sales_order_items WHERE order_id = ? ORDER BY id ASC'); $it->execute([$id]); $items = $it->fetchAll(); } catch (\Throwable $e) { $items = []; }

        // Garantir coluna de origem
        try { $pdo->query("ALTER TABLE sales_orders ADD COLUMN pedido_origem_id INT NULL"); } catch (\Throwable $e) { /* ignore */ }

        try {
            $pdo->beginTransaction();
            // Criar novo pedido com numeração sequencial: PD-##########
            $numero = null;
            try {
                $stmtSeq = $pdo->query("SELECT MAX(CAST(SUBSTRING(numero, 4) AS UNSIGNED)) AS max_seq FROM sales_orders WHERE numero LIKE 'PD-%'");
                $rowSeq = $stmtSeq->fetch();
                $next = ((int)($rowSeq['max_seq'] ?? 0)) + 1;
                $numero = 'PD-' . str_pad((string)$next, 10, '0', STR_PAD_LEFT);
            } catch (\Throwable $e) { $numero = null; }
            if ($numero === null) {
                // Fallback: será atualizado após insert usando id
                $numero = '';
            }
            $ins = $pdo->prepare('INSERT INTO sales_orders (numero, cliente_id, valor_total, status, pedido_origem_id) VALUES (?,?,?,?,?)');
            $ins->execute([$numero, (int)($order['cliente_id'] ?? 0), (float)($order['valor_total'] ?? 0), 'aberto', $id]);
            $newId = (int)$pdo->lastInsertId();
            if ($numero === '') {
                $numero = 'PD-' . str_pad((string)$newId, 10, '0', STR_PAD_LEFT);
                $pdo->prepare('UPDATE sales_orders SET numero = ? WHERE id = ?')->execute([$numero, $newId]);
            }

            // Itens
            if (count($items) > 0) {
                try {
                    $itIns = $pdo->prepare('INSERT INTO sales_order_items (order_id, product_id, quantidade, preco, total) VALUES (?,?,?,?,?)');
                    foreach ($items as $s) {
                        $itIns->execute([$newId, (int)$s['product_id'], (float)$s['quantidade'], (float)$s['preco'], (float)$s['total']]);
                    }
                } catch (\Throwable $e) { /* ignore */ }
            }

            $pdo->commit();
            AuditLogger::logCreate('sales_orders', $newId, ['numero' => $numero, 'cliente_id' => (int)($order['cliente_id'] ?? 0), 'valor_total' => (float)($order['valor_total'] ?? 0), 'status' => 'aberto', 'pedido_origem_id' => $id], ['endpoint' => 'vendas.pedidos.duplicate']);
            return Response::json(['ok' => true, 'id' => $newId, 'numero' => $numero], 201);
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            return new Response(500, ['error' => ['code' => 'duplicate_failed', 'message' => $e->getMessage()]]);
        }
    }
}