<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Integrations\ShipayClient;
use App\Services\NFSe\NFSeNacionalService;
use Exception;

class VendasController extends BaseController
{
    protected array $moduleConfig = [
        'origin' => 'venda',
        'slug' => 'vendas',
        'routeBase' => '/vendas',
        'viewBase' => 'vendas',
        'icon' => 'bi bi-cart-check',
        'createIcon' => 'bi bi-cart-plus',
        'singular' => 'Venda',
        'plural' => 'Vendas',
        'subtitle' => 'Gerencie suas vendas e pedidos',
        'createSubtitle' => 'Preencha os dados da venda e adicione os itens',
        'numberPrefix' => 'VND-',
        'statusModule' => 'vendas',
        'generateReceivablesDefault' => true,
        'updateStockDefault' => true,
        'conversionEnabled' => false,
    ];

    protected function module(string $key, $default = null)
    {
        return $this->moduleConfig[$key] ?? $default;
    }

    protected function moduleOrigin(): string
    {
        return (string) $this->module('origin', 'venda');
    }

    protected function moduleSlug(): string
    {
        return (string) $this->module('slug', 'vendas');
    }

    protected function moduleRouteBase(): string
    {
        return (string) $this->module('routeBase', '/vendas');
    }

    protected function moduleViewBase(): string
    {
        return (string) $this->module('viewBase', 'vendas');
    }

    protected function moduleIcon(): string
    {
        return (string) $this->module('icon', 'bi bi-cart-check');
    }

    protected function moduleCreateIcon(): string
    {
        return (string) $this->module('createIcon', $this->moduleIcon());
    }

    protected function moduleSingular(): string
    {
        return (string) $this->module('singular', 'Venda');
    }

    protected function modulePlural(): string
    {
        return (string) $this->module('plural', 'Vendas');
    }

    protected function moduleSubtitle(): string
    {
        return (string) $this->module('subtitle', 'Gerencie suas vendas e pedidos');
    }

    protected function moduleCreateSubtitle(): string
    {
        return (string) $this->module('createSubtitle', $this->moduleSubtitle());
    }

    protected function moduleNumberPrefix(): string
    {
        return (string) $this->module('numberPrefix', 'VND-');
    }

    protected function moduleStatusKey(): string
    {
        return (string) $this->module('statusModule', 'vendas');
    }

    protected function moduleShouldGenerateReceivables(): bool
    {
        return (bool) $this->module('generateReceivablesDefault', true);
    }

    protected function moduleShouldUpdateStock(): bool
    {
        return (bool) $this->module('updateStockDefault', true);
    }

    protected function moduleConversionEnabled(): bool
    {
        return (bool) $this->module('conversionEnabled', false);
    }

    protected function moduleView(string $view): string
    {
        return $this->moduleViewBase() . '/' . ltrim($view, '/');
    }

    protected function withModuleViewData(array $data = []): array
    {
        $moduleData = [
            'module' => [
                'origin' => $this->moduleOrigin(),
                'slug' => $this->moduleSlug(),
                'routeBase' => $this->moduleRouteBase(),
                'viewBase' => $this->moduleViewBase(),
                'icon' => $this->moduleIcon(),
                'createIcon' => $this->moduleCreateIcon(),
                'singular' => $this->moduleSingular(),
                'plural' => $this->modulePlural(),
                'subtitle' => $this->moduleSubtitle(),
                'createSubtitle' => $this->moduleCreateSubtitle(),
                'numberPrefix' => $this->moduleNumberPrefix(),
                'conversionEnabled' => $this->moduleConversionEnabled(),
            ],
        ];

        return array_merge($moduleData, $data);
    }

    private function verificarComissaoFuncionarioItem(int $companyId): bool
    {
        try {
            $stmt = $this->db->prepare("
                SELECT valor
                FROM parametros
                WHERE empresa_id = :empresa_id
                  AND chave = 'comissao_funcionario_item'
                LIMIT 1
            ");
            $stmt->execute(['empresa_id' => $companyId]);
            $valor = $stmt->fetchColumn();
            return ($valor !== false && (int) $valor === 1);
        } catch (\Throwable $e) {
            error_log('[Vendas] Erro ao verificar parâmetro comissao_funcionario_item: ' . $e->getMessage());
            return false;
        }
    }

    private function configurarLotesVenda(int $companyId, array &$produtos): bool
    {
        $usarLote = false;

        try {
            $stmt = $this->db->prepare("
                SELECT valor
                FROM parametros
                WHERE empresa_id = :empresa_id
                  AND chave = 'usar_lote_venda'
                LIMIT 1
            ");
            $stmt->execute(['empresa_id' => $companyId]);
            $valor = $stmt->fetchColumn();
            $usarLote = ($valor !== false && (int) $valor === 1);
        } catch (\Throwable $e) {
            error_log('[Vendas] Erro ao verificar parâmetro usar_lote_venda: ' . $e->getMessage());
            $usarLote = false;
        }

        foreach ($produtos as &$produto) {
            $produto['lotes'] = [];
        }
        unset($produto);

        if (empty($produtos)) {
            return $usarLote;
        }

        if (!$usarLote) {
            return false;
        }

        try {
            $stmt = $this->db->prepare("
                SELECT
                    product_id,
                    lote,
                    fabricacao,
                    validade,
                    SUM(CASE WHEN type = 'entrada' THEN quantity ELSE -quantity END) as saldo
                FROM estoque_movimentos
                WHERE company_id = :company_id
                  AND lote IS NOT NULL
                  AND lote <> ''
                GROUP BY product_id, lote, fabricacao, validade
                HAVING saldo > 0
                ORDER BY validade IS NULL, validade ASC, lote ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $lotesAgrupados = [];

            while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
                $productId = (int) ($row['product_id'] ?? 0);
                if ($productId <= 0) {
                    continue;
                }

                if (!isset($lotesAgrupados[$productId])) {
                    $lotesAgrupados[$productId] = [];
                }

                $lotesAgrupados[$productId][] = [
                    'lote' => $row['lote'] ?? '',
                    'fabricacao' => $row['fabricacao'] ?? null,
                    'validade' => $row['validade'] ?? null,
                    'saldo' => (float) ($row['saldo'] ?? 0),
                ];
            }

            foreach ($produtos as &$produto) {
                $id = (int) ($produto['id'] ?? 0);
                $produto['lotes'] = $lotesAgrupados[$id] ?? [];
            }
            unset($produto);
        } catch (\Throwable $e) {
            error_log('[Vendas] Erro ao carregar lotes disponíveis: ' . $e->getMessage());
        }

        return true;
    }

    public function index(): void
    {
        // Verificar permissão de visualização
        if (!$this->canView('vendas')) {
            $this->response->forbidden('Você não tem permissão para visualizar vendas.');
            return;
        }

        try {
            $companyId = $this->getCompanyId();
            $search = $this->request->get('search', '');
            $status = $this->request->get('status', '');

            // Para o módulo de vendas, incluir também as vendas do PDV
            $moduleOrigin = $this->moduleOrigin();
            $incluirPdv = ($moduleOrigin === 'venda');

            $query = "
                SELECT v.*,
                    COALESCE(p.name, v.customer_name, 'Consumidor Final') as customer_name,
                    p.name as pessoa_name,
                    p.trade_name as pessoa_trade_name,
                    COALESCE(p.document, v.customer_document) as customer_document,
                    mp.name as payment_method_name,
                    u.name as vendor_name,
                    v.chave_nfe, v.numero_nfe, v.protocolo_nfe,
                    v.protocolo_cancelamento, v.data_cancelamento, v.pdf_cancelamento, v.xml_cancelamento,
                    v.observacoes_nfe
                FROM vendas v
                LEFT JOIN pessoas p ON v.customer_id = p.id
                LEFT JOIN metodos_pagamento mp ON v.payment_method_id = mp.id
                LEFT JOIN users u ON v.vendor_id = u.id
                WHERE v.company_id = :company_id
            ";

            $params = [
                'company_id' => $companyId
            ];

            // Se for módulo de vendas, incluir também PDV; caso contrário, filtrar pelo módulo específico
            if ($incluirPdv) {
                $query .= " AND v.modulo_origem IN ('venda', 'pdv')";
            } else {
                $query .= " AND v.modulo_origem = :modulo_origem";
                $params['modulo_origem'] = $moduleOrigin;
            }

            if (!empty($search)) {
                $query .= " AND (v.sale_number LIKE :search OR p.name LIKE :search)";
                $params['search'] = "%{$search}%";
            }

            if (!empty($status)) {
                $query .= " AND v.status = :status";
                $params['status'] = $status;
            }

            $query .= " ORDER BY v.id DESC";

            $stmt = $this->db->prepare($query);
            $stmt->execute($params);
            $vendas = $stmt->fetchAll();

            // Busca status do módulo para o filtro
            $statusModule = $this->moduleStatusKey();
            $stmt = $this->db->prepare("
                SELECT codigo, nome, cor, icone
                FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = :modulo
                  AND ativo = 1
                ORDER BY ordem ASC
            ");
            $stmt->execute([
                'company_id' => $companyId,
                'modulo' => $statusModule
            ]);
            $statusVendas = $stmt->fetchAll();

            $this->view($this->moduleView('index'), $this->withModuleViewData([
                'vendas' => $vendas,
                'search' => $search,
                'status' => $status,
                'statusVendas' => $statusVendas,
                'nfseConfig' => $this->getEmpresaNFSeConfig($companyId),
                'pageTitle' => $this->modulePlural(),
                'activeMenu' => $this->moduleSlug()
            ]));
        } catch (Exception $e) {
            error_log("Erro ao carregar {$this->modulePlural()}: " . $e->getMessage());
            $this->error('Erro ao carregar ' . mb_strtolower($this->modulePlural()));
        }
    }

    public function conferencia(): void
    {
        // Verificar permissão de visualização
        if (!$this->canView('vendas')) {
            $this->response->forbidden('Você não tem permissão para visualizar conferência de vendas.');
            return;
        }

        try {
            $companyId = $this->getCompanyId();
            $search = $this->request->get('search', '');
            $status = $this->request->get('status', '');

            // Buscar vendas para conferência (normalmente vendas pendentes ou em processo)
            $query = "
                SELECT v.*,
                    COALESCE(p.name, v.customer_name, 'Consumidor Final') as customer_name,
                    p.name as pessoa_name,
                    p.trade_name as pessoa_trade_name,
                    COALESCE(p.document, v.customer_document) as customer_document,
                    mp.name as payment_method_name,
                    u.name as vendor_name,
                    v.chave_nfe, v.numero_nfe, v.protocolo_nfe
                FROM vendas v
                LEFT JOIN pessoas p ON v.customer_id = p.id
                LEFT JOIN metodos_pagamento mp ON v.payment_method_id = mp.id
                LEFT JOIN users u ON v.vendor_id = u.id
                WHERE v.company_id = :company_id
            ";

            $params = [
                'company_id' => $companyId
            ];

            // Filtrar por vendas que precisam de conferência (status específicos ou todas)
            if (!empty($search)) {
                $query .= " AND (v.sale_number LIKE :search OR p.name LIKE :search)";
                $params['search'] = "%{$search}%";
            }

            if (!empty($status)) {
                $query .= " AND v.status = :status";
                $params['status'] = $status;
            }

            $query .= " ORDER BY v.id DESC";

            $stmt = $this->db->prepare($query);
            $stmt->execute($params);
            $vendas = $stmt->fetchAll();

            // Busca status do módulo para o filtro
            $statusModule = $this->moduleStatusKey();
            $stmt = $this->db->prepare("
                SELECT codigo, nome, cor, icone
                FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = :modulo
                  AND ativo = 1
                ORDER BY ordem ASC
            ");
            $stmt->execute([
                'company_id' => $companyId,
                'modulo' => $statusModule
            ]);
            $statusVendas = $stmt->fetchAll();

            $this->view($this->moduleView('conferencia'), $this->withModuleViewData([
                'vendas' => $vendas,
                'search' => $search,
                'status' => $status,
                'statusVendas' => $statusVendas,
                'nfseConfig' => $this->getEmpresaNFSeConfig($companyId),
                'pageTitle' => 'Conferência de Vendas',
                'activeMenu' => $this->moduleSlug()
            ]));
        } catch (Exception $e) {
            error_log("Erro ao carregar conferência de vendas: " . $e->getMessage());
            $this->error('Erro ao carregar conferência de vendas');
        }
    }

    public function create(): void
    {
        // Verificar permissão de criação
        if (!$this->canCreate('vendas')) {
            $this->response->forbidden('Você não tem permissão para criar vendas.');
            return;
        }

        try {
            $companyId = $this->getCompanyId();

            // Busca clientes com tabela de preço (inicialmente vazio, será carregado via AJAX baseado na empresa selecionada)
            $clientes = [];

            // Produtos serão carregados dinamicamente via AJAX baseado na empresa selecionada
            $produtos = [];

            $usarLoteVenda = $this->configurarLotesVenda($companyId, $produtos);

            // Busca métodos de pagamento
            $stmt = $this->db->prepare("
                SELECT id, name, type FROM metodos_pagamento
                WHERE company_id = :company_id AND is_active = 1
                ORDER BY name ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $metodosPagamento = $stmt->fetchAll();

            // Busca vendedores (usuários ativos)
            $stmt = $this->db->prepare("
                SELECT id, name FROM users
                WHERE company_id = :company_id AND is_active = 1
                ORDER BY name ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $vendedores = $stmt->fetchAll();

            // Verificar se deve exibir comissão por funcionário por item
            $comissaoFuncionarioItem = $this->verificarComissaoFuncionarioItem($companyId);
            $profissionaisItem = [];
            if ($comissaoFuncionarioItem) {
                // Verificar se o campo é 'type' ou 'role' e buscar profissionais (vendedor)
                try {
                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM users LIKE 'type'");
                    $temCampoType = $stmtCheck->fetch() !== false;

                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM users LIKE 'role'");
                    $temCampoRole = $stmtCheck->fetch() !== false;
                } catch (\Exception $e) {
                    $temCampoType = false;
                    $temCampoRole = false;
                }

                // Buscar profissionais (vendedor) - tentar type primeiro, depois role
                if ($temCampoType) {
                    $stmt = $this->db->prepare("
                        SELECT id, name FROM users
                        WHERE company_id = :company_id
                          AND type = 'vendedor'
                          AND is_active = 1
                        ORDER BY name ASC
                    ");
                } elseif ($temCampoRole) {
                    $stmt = $this->db->prepare("
                        SELECT id, name FROM users
                        WHERE company_id = :company_id
                          AND role = 'vendedor'
                          AND is_active = 1
                        ORDER BY name ASC
                    ");
                } else {
                    // Se não tiver nenhum dos campos, buscar todos os usuários ativos
                    $stmt = $this->db->prepare("
                        SELECT id, name FROM users
                        WHERE company_id = :company_id
                          AND is_active = 1
                        ORDER BY name ASC
                    ");
                }
                $stmt->execute(['company_id' => $companyId]);
                $profissionaisItem = $stmt->fetchAll() ?: [];

                // Debug: log dos profissionais encontrados
                error_log('[Vendas] Profissionais encontrados: ' . count($profissionaisItem));
                if (!empty($profissionaisItem)) {
                    error_log('[Vendas] Primeiro profissional: ' . json_encode($profissionaisItem[0]));
                }
            }

            // Busca status do módulo vendas
            $stmt = $this->db->prepare("
                SELECT codigo, nome, descricao, cor, icone, ordem, is_default
                FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = :modulo
                  AND ativo = 1
                ORDER BY ordem ASC
            ");
            $stmt->execute([
                'company_id' => $companyId,
                'modulo' => $this->moduleStatusKey()
            ]);
            $statusVendas = $stmt->fetchAll();

            // Gerar próximo número de venda
            $proximoNumero = $this->gerarProximoNumeroVenda($companyId);

            // Status padrão (primeiro status ou o marcado como is_default)
            $statusPadrao = !empty($statusVendas) ? $statusVendas[0]['codigo'] : 'orcamento';
            foreach ($statusVendas as $status) {
                if (!empty($status['is_default'])) {
                    $statusPadrao = $status['codigo'];
                    break;
                }
            }

            // Buscar empresas vinculadas ao usuário
            $empresasVinculadas = $this->getEmpresasVinculadasUsuario();

            $this->view($this->moduleView('create'), $this->withModuleViewData([
                'clientes' => $clientes,
                'produtos' => $produtos,
                'usarLoteVenda' => $usarLoteVenda,
                'metodosPagamento' => $metodosPagamento,
                'vendedores' => $vendedores,
                'statusVendas' => $statusVendas,
                'statusPadrao' => $statusPadrao,
                'proximoNumero' => $proximoNumero,
                'comissaoFuncionarioItem' => $comissaoFuncionarioItem,
                'profissionaisItem' => $profissionaisItem,
                'empresasVinculadas' => $empresasVinculadas,
                'pageTitle' => 'Nova ' . $this->moduleSingular(),
                'activeMenu' => $this->moduleSlug()
            ]));
        } catch (Exception $e) {
            error_log("Erro ao carregar formulário de {$this->moduleSingular()}: " . $e->getMessage());
            $this->error('Erro ao carregar formulário');
        }
    }

    public function store(): void
    {
        // Verificar permissão de criação
        if (!$this->canCreate('vendas')) {
            $this->response->forbidden('Você não tem permissão para criar vendas.');
            return;
        }

        try {
            $companyId = $this->getCompanyId();

            // Gerar próximo número de venda SEMPRE no backend (segurança)
            $proximoNumero = $this->gerarProximoNumeroVenda($companyId);

            error_log("Nova {$this->moduleSingular()}: Número gerado automaticamente: {$proximoNumero}");

            $generateReceivablesPost = $this->request->post('generate_receivables');
            $updateStockPost = $this->request->post('update_stock');

            // Buscar empresa_id selecionada
            $empresaId = $this->request->post('empresa_id');
            if (!empty($empresaId)) {
                // Validar se o usuário tem acesso a essa empresa
                $empresasVinculadas = $this->getEmpresasVinculadasUsuario();
                $empresasIds = array_column($empresasVinculadas, 'id');
                if (!in_array((int) $empresaId, $empresasIds)) {
                    $this->error('Você não tem permissão para realizar vendas para esta empresa');
                    return;
                }
            }

            $this->db->beginTransaction();

            // Verificar se a coluna empresa_id existe
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas LIKE 'empresa_id'");
            $temColunaEmpresaId = $stmtCheck->rowCount() > 0;

            $data = [
                'company_id' => $companyId,
                'modulo_origem' => $this->moduleOrigin(),
                'customer_type' => $this->request->post('customer_type') ?: 'pessoa',
                'customer_id' => $this->request->post('customer_id') ?: null,
                'customer_name' => $this->request->post('customer_name'),
                'customer_document' => $this->request->post('customer_document'),
                'sale_number' => $proximoNumero, // Usar número gerado automaticamente
                'sale_date' => $this->request->post('sale_date') ?: date('Y-m-d'),
                'payment_method_id' => $this->request->post('payment_method_id') ?: null,
                'payment_term_id' => $this->request->post('payment_term_id') ?: null,
                'installments' => $this->request->post('installments') ?: 1,
                'subtotal' => $this->request->post('subtotal') ?: 0.00,
                'discount' => $this->request->post('discount') ?: 0.00,
                'discount_type' => $this->request->post('discount_type') ?: 'fixed',
                'additions' => $this->request->post('additions') ?: 0.00,
                'shipping' => $this->request->post('shipping') ?: 0.00,
                'total' => $this->request->post('total') ?: 0.00,
                'status' => $this->request->post('status') ?: 'pendente',
                'notes' => $this->request->post('notes'),
                'observacoes_nfe' => $this->request->post('observacoes_nfe'),
                'vendor_id' => $this->request->post('vendor_id') ?: null,
                'auxiliary_vendor_id' => $this->request->post('auxiliary_vendor_id') ?: null,
                'generate_receivables' => $generateReceivablesPost !== null ? (int) (bool) $generateReceivablesPost : ($this->moduleShouldGenerateReceivables() ? 1 : 0),
                'update_stock' => $updateStockPost !== null ? (int) (bool) $updateStockPost : ($this->moduleShouldUpdateStock() ? 1 : 0),
            ];

            // Adicionar empresa_id ao array apenas se a coluna existir
            if ($temColunaEmpresaId) {
                $data['empresa_id'] = $empresaId ?: null;
            }

            // Validar que vendedor principal e auxiliar não sejam a mesma pessoa
            if (!empty($data['vendor_id']) && !empty($data['auxiliary_vendor_id'])) {
                if ((int) $data['vendor_id'] === (int) $data['auxiliary_vendor_id']) {
                    $this->error('O vendedor principal e o vendedor auxiliar não podem ser a mesma pessoa');
                    return;
                }
            }

            $campoEmpresaId = $temColunaEmpresaId ? ', empresa_id' : '';
            $valorEmpresaId = $temColunaEmpresaId ? ', :empresa_id' : '';

            $stmt = $this->db->prepare("
                INSERT INTO vendas (
                    company_id{$campoEmpresaId}, modulo_origem, customer_type, customer_id, customer_name, customer_document,
                    sale_number, sale_date, payment_method_id, payment_term_id, installments,
                    subtotal, discount, discount_type, additions, shipping, total,
                    status, notes, observacoes_nfe, vendor_id, auxiliary_vendor_id, generate_receivables, update_stock,
                    created_at, updated_at
                ) VALUES (
                    :company_id{$valorEmpresaId}, :modulo_origem, :customer_type, :customer_id, :customer_name, :customer_document,
                    :sale_number, :sale_date, :payment_method_id, :payment_term_id, :installments,
                    :subtotal, :discount, :discount_type, :additions, :shipping, :total,
                    :status, :notes, :observacoes_nfe, :vendor_id, :auxiliary_vendor_id, :generate_receivables, :update_stock,
                    NOW(), NOW()
                )
            ");
            $stmt->execute($data);
            $vendaId = (int) $this->db->lastInsertId();

            // Salvar itens da venda
            $itens = $this->request->post('itens') ?? [];
            $statusCodigo = $data['status'] ?? 'pendente';
            $updateStock = !empty($data['update_stock']);

            error_log('[Vendas] Total de itens recebidos: ' . count($itens));
            if (!empty($itens)) {
                error_log('[Vendas] Primeiro item: ' . json_encode($itens[0] ?? []));
                try {
                    $this->salvarItens($vendaId, $itens, $statusCodigo, $updateStock);
                } catch (\Exception $e) {
                    error_log('[Vendas] ERRO ao salvar itens: ' . $e->getMessage());
                    error_log('[Vendas] Stack trace: ' . $e->getTraceAsString());
                    throw $e;
                }
            } else {
                error_log('[Vendas] AVISO: Nenhum item recebido para salvar');
            }

            // Atualizar estoque se solicitado
            if ($data['update_stock']) {
                $this->atualizarEstoque($vendaId, $itens);
            }

            // Salvar informações de pagamento na tabela vendas_pagamentos
            $this->salvarPagamentos($vendaId, $data);

            // Verificar se o status da venda gera financeiro
            $statusCodigo = $data['status'] ?? 'pendente';
            error_log("Venda #{$vendaId}: Verificando status '{$statusCodigo}' para geração financeira. Company ID: {$companyId}");

            $stmtStatus = $this->db->prepare("
                SELECT codigo, nome, gera_financeiro, movimenta_estoque FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = 'vendas'
                  AND codigo = :codigo
                  AND ativo = 1
                LIMIT 1
            ");
            $stmtStatus->execute([
                'company_id' => $companyId,
                'codigo' => $statusCodigo
            ]);
            $statusInfo = $stmtStatus->fetch();

            // GERAR CONTAS A RECEBER se o status permitir
            $deveGerarFinanceiro = false;

            // Verificar se o status gera financeiro
            if ($statusInfo && isset($statusInfo['gera_financeiro'])) {
                $geraFin = (int) $statusInfo['gera_financeiro'];
                if ($geraFin == 1) {
                    $deveGerarFinanceiro = true;
                    error_log("Venda #{$vendaId}: Status '{$statusCodigo}' ({$statusInfo['nome']}) TEM gera_financeiro = 1");
                } else {
                    error_log("Venda #{$vendaId}: Status '{$statusCodigo}' ({$statusInfo['nome']}) TEM gera_financeiro = 0");
                }
            } else {
                error_log("Venda #{$vendaId}: ⚠️ Status '{$statusCodigo}' NÃO encontrado na tabela modulo_status");
            }

            // Se checkbox marcado, também gera
            if (!empty($data['generate_receivables'])) {
                $deveGerarFinanceiro = true;
                error_log("Venda #{$vendaId}: Checkbox 'Gerar Contas a Receber' está marcado");
            }

            // GERAR AS CONTAS A RECEBER
            if ($deveGerarFinanceiro && !empty($data['payment_method_id']) && !empty($data['total']) && $data['total'] > 0) {
                error_log("Venda #{$vendaId}: ✅ INICIANDO geração de contas a receber - Status: {$statusCodigo}, Total: {$data['total']}, Parcelas: " . ($data['installments'] ?? 1));
                try {
                    $this->gerarContasReceber($vendaId, $data);
                    error_log("Venda #{$vendaId}: ✅ Função gerarContasReceber() concluída sem exceções");
                } catch (\Exception $e) {
                    error_log("Venda #{$vendaId}: ❌ ERRO na função gerarContasReceber(): " . $e->getMessage());
                    error_log("Venda #{$vendaId}: Stack trace: " . $e->getTraceAsString());
                    // NÃO relançar exceção para não interromper a criação da venda
                }
            } else {
                error_log("Venda #{$vendaId}: ❌ NÃO gerou contas - gera_fin: " . ($deveGerarFinanceiro ? 'SIM' : 'NÃO') . ", payment_method: " . ($data['payment_method_id'] ?? 'NULL') . ", total: " . ($data['total'] ?? 0));
            }

            // Movimentação de estoque já é feita dentro de salvarItens()

            $this->logActivity('create', 'vendas', $vendaId, $data);
            $this->db->commit();

            $this->success($this->moduleSingular() . ' criada com sucesso', [
                'id' => $vendaId,
                'redirect' => $this->moduleRouteBase()
            ]);
        } catch (Exception $e) {
            if ($this->db && $this->db->inTransaction()) {
                $this->db->rollBack();
            }
            error_log("=== ERRO AO CRIAR VENDA ===");
            error_log("Mensagem: " . $e->getMessage());
            error_log("Arquivo: " . $e->getFile());
            error_log("Linha: " . $e->getLine());
            error_log("Stack trace: " . $e->getTraceAsString());
            error_log("POST data: " . json_encode($_POST));
            $this->error('Erro ao criar ' . mb_strtolower($this->moduleSingular()) . ': ' . $e->getMessage());
        }
    }

    private function salvarItens(int $vendaId, array $itens, string $statusCodigo = '', bool $updateStock = false): void
    {
        $companyId = $this->getCompanyId();

        // Verificar se as colunas vendedor_id, comissao, comissao_percentual e profissional_id existem
        $colunaVendedorIdExiste = false;
        $colunaComissaoExiste = false;
        $colunaComissaoPercentualExiste = false;
        $colunaProfissionalIdExiste = false;
        $colunaCclassExiste = false;
        $colunaAliquotaIbsExiste = false;
        $colunaReducaoAliquotaIbsExiste = false;
        $colunaAliquotaCbsExiste = false;
        $colunaReducaoAliquotaCbsExiste = false;
        $colunaAliquotaIbsMunicipalExiste = false;
        $colunaReducaoAliquotaIbsMunicipalExiste = false;
        try {
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'vendedor_id'");
            $colunaVendedorIdExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'comissao'");
            $colunaComissaoExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'comissao_percentual'");
            $colunaComissaoPercentualExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'profissional_id'");
            $colunaProfissionalIdExiste = $stmtCheck->fetch() !== false;

            // Verificar colunas de IBS/CBS
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'cclass'");
            $colunaCclassExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'aliquota_ibs'");
            $colunaAliquotaIbsExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'reducao_aliquota_ibs'");
            $colunaReducaoAliquotaIbsExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'aliquota_cbs'");
            $colunaAliquotaCbsExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'reducao_aliquota_cbs'");
            $colunaReducaoAliquotaCbsExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'aliquota_ibs_municipal'");
            $colunaAliquotaIbsMunicipalExiste = $stmtCheck->fetch() !== false;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'reducao_aliquota_ibs_municipal'");
            $colunaReducaoAliquotaIbsMunicipalExiste = $stmtCheck->fetch() !== false;
        } catch (\Exception $e) {
            // Ignorar erro de verificação
            $colunaVendedorIdExiste = false;
            $colunaComissaoExiste = false;
            $colunaComissaoPercentualExiste = false;
            $colunaProfissionalIdExiste = false;
            $colunaCclassExiste = false;
            $colunaAliquotaIbsExiste = false;
            $colunaReducaoAliquotaIbsExiste = false;
            $colunaAliquotaCbsExiste = false;
            $colunaReducaoAliquotaCbsExiste = false;
            $colunaAliquotaIbsMunicipalExiste = false;
            $colunaReducaoAliquotaIbsMunicipalExiste = false;
        }

        $vendedorIdField = $colunaVendedorIdExiste ? ', vendedor_id' : '';
        $vendedorIdValue = $colunaVendedorIdExiste ? ', :vendedor_id' : '';
        $comissaoField = $colunaComissaoExiste ? ', comissao' : '';
        $comissaoValue = $colunaComissaoExiste ? ', :comissao' : '';
        $comissaoPercentualField = $colunaComissaoPercentualExiste ? ', comissao_percentual' : '';
        $comissaoPercentualValue = $colunaComissaoPercentualExiste ? ', :comissao_percentual' : '';
        $profissionalIdField = $colunaProfissionalIdExiste ? ', profissional_id' : '';
        $profissionalIdValue = $colunaProfissionalIdExiste ? ', :profissional_id' : '';
        // Campos de IBS/CBS
        $cclassField = $colunaCclassExiste ? ', cclass' : '';
        $cclassValue = $colunaCclassExiste ? ', :cclass' : '';
        $aliquotaIbsField = $colunaAliquotaIbsExiste ? ', aliquota_ibs' : '';
        $aliquotaIbsValue = $colunaAliquotaIbsExiste ? ', :aliquota_ibs' : '';
        $reducaoAliquotaIbsField = $colunaReducaoAliquotaIbsExiste ? ', reducao_aliquota_ibs' : '';
        $reducaoAliquotaIbsValue = $colunaReducaoAliquotaIbsExiste ? ', :reducao_aliquota_ibs' : '';
        $aliquotaCbsField = $colunaAliquotaCbsExiste ? ', aliquota_cbs' : '';
        $aliquotaCbsValue = $colunaAliquotaCbsExiste ? ', :aliquota_cbs' : '';
        $reducaoAliquotaCbsField = $colunaReducaoAliquotaCbsExiste ? ', reducao_aliquota_cbs' : '';
        $reducaoAliquotaCbsValue = $colunaReducaoAliquotaCbsExiste ? ', :reducao_aliquota_cbs' : '';
        $aliquotaIbsMunicipalField = $colunaAliquotaIbsMunicipalExiste ? ', aliquota_ibs_municipal' : '';
        $aliquotaIbsMunicipalValue = $colunaAliquotaIbsMunicipalExiste ? ', :aliquota_ibs_municipal' : '';
        $reducaoAliquotaIbsMunicipalField = $colunaReducaoAliquotaIbsMunicipalExiste ? ', reducao_aliquota_ibs_municipal' : '';
        $reducaoAliquotaIbsMunicipalValue = $colunaReducaoAliquotaIbsMunicipalExiste ? ', :reducao_aliquota_ibs_municipal' : '';

        $sql = "
            INSERT INTO vendas_itens (
                company_id, venda_id, product_id, product_name, product_sku,
                quantity, unit_price, discount, discount_type, total_price, notes,
                ncm, cest, cfop, origem, cst_icms, aliquota_icms, valor_icms, base_calculo_icms,
                cst_pis, aliquota_pis, valor_pis, base_calculo_pis,
                cst_cofins, aliquota_cofins, valor_cofins, base_calculo_cofins,
                cst_ipi, aliquota_ipi, valor_ipi, origem_st,
                numero_serie, lote, fabricacao, validade{$vendedorIdField}{$comissaoField}{$comissaoPercentualField}{$profissionalIdField}{$cclassField}{$aliquotaIbsField}{$reducaoAliquotaIbsField}{$aliquotaCbsField}{$reducaoAliquotaCbsField}{$aliquotaIbsMunicipalField}{$reducaoAliquotaIbsMunicipalField}
            ) VALUES (
                :company_id, :venda_id, :product_id, :product_name, :product_sku,
                :quantity, :unit_price, :discount, :discount_type, :total_price, :notes,
                :ncm, :cest, :cfop, :origem, :cst_icms, :aliquota_icms, :valor_icms, :base_calculo_icms,
                :cst_pis, :aliquota_pis, :valor_pis, :base_calculo_pis,
                :cst_cofins, :aliquota_cofins, :valor_cofins, :base_calculo_cofins,
                :cst_ipi, :aliquota_ipi, :valor_ipi, :origem_st,
                :numero_serie, :lote, :fabricacao, :validade{$vendedorIdValue}{$comissaoValue}{$comissaoPercentualValue}{$profissionalIdValue}{$cclassValue}{$aliquotaIbsValue}{$reducaoAliquotaIbsValue}{$aliquotaCbsValue}{$reducaoAliquotaCbsValue}{$aliquotaIbsMunicipalValue}{$reducaoAliquotaIbsMunicipalValue}
            )
        ";

        error_log('[Vendas] SQL para inserir itens: ' . $sql);
        error_log('[Vendas] Colunas verificadas - vendedor_id: ' . ($colunaVendedorIdExiste ? 'SIM' : 'NÃO') . ', comissao: ' . ($colunaComissaoExiste ? 'SIM' : 'NÃO') . ', comissao_percentual: ' . ($colunaComissaoPercentualExiste ? 'SIM' : 'NÃO') . ', profissional_id: ' . ($colunaProfissionalIdExiste ? 'SIM' : 'NÃO'));

        $stmt = $this->db->prepare($sql);

        // Buscar profissional_id geral se existir
        $profissionalIdGeral = $this->request->post('profissional_id') ?: null;

        foreach ($itens as $item) {
            if (empty($item['product_id']) && empty($item['product_name'])) {
                continue;
            }

            // Calcular valores dos impostos
            $totalItem = floatval($item['total_price'] ?? 0);
            $aliquotaIcms = floatval($item['aliquota_icms'] ?? 0);
            $aliquotaPis = floatval($item['aliquota_pis'] ?? 0);
            $aliquotaCofins = floatval($item['aliquota_cofins'] ?? 0);
            $aliquotaIpi = floatval($item['aliquota_ipi'] ?? 0);

            $baseCalculoIcms = floatval($item['base_calculo_icms'] ?? $totalItem);
            $baseCalculoPis = floatval($item['base_calculo_pis'] ?? $totalItem);
            $baseCalculoCofins = floatval($item['base_calculo_cofins'] ?? $totalItem);

            $valorIcms = ($baseCalculoIcms * $aliquotaIcms) / 100;
            $valorPis = ($baseCalculoPis * $aliquotaPis) / 100;
            $valorCofins = ($baseCalculoCofins * $aliquotaCofins) / 100;
            $valorIpi = ($totalItem * $aliquotaIpi) / 100;

            $params = [
                'company_id' => $companyId,
                'venda_id' => $vendaId,
                'product_id' => $item['product_id'] ?: null,
                'product_name' => $item['product_name'] ?? '',
                'product_sku' => $item['product_sku'] ?? '',
                'quantity' => $item['quantity'] ?? 1,
                'unit_price' => $item['unit_price'] ?? 0,
                'discount' => $item['discount'] ?? 0,
                'discount_type' => $item['discount_type'] ?? 'fixed',
                'total_price' => $item['total_price'] ?? 0,
                'notes' => $item['notes'] ?? null,
                // Campos fiscais
                'ncm' => $item['ncm'] ?? null,
                'cest' => $item['cest'] ?? null,
                'cfop' => $item['cfop'] ?? null,
                'origem' => $item['origem'] ?? null,
                'cst_icms' => $item['cst_icms'] ?? null,
                'aliquota_icms' => $aliquotaIcms,
                'valor_icms' => $valorIcms,
                'base_calculo_icms' => $baseCalculoIcms,
                'cst_pis' => $item['cst_pis'] ?? null,
                'aliquota_pis' => $aliquotaPis,
                'valor_pis' => $valorPis,
                'base_calculo_pis' => $baseCalculoPis,
                'cst_cofins' => $item['cst_cofins'] ?? null,
                'aliquota_cofins' => $aliquotaCofins,
                'valor_cofins' => $valorCofins,
                'base_calculo_cofins' => $baseCalculoCofins,
                'cst_ipi' => $item['cst_ipi'] ?? null,
                'aliquota_ipi' => $aliquotaIpi,
                'valor_ipi' => $valorIpi,
                'origem_st' => $item['origem_st'] ?? null,
                'numero_serie' => $item['numero_serie'] ?? null,
                'lote' => $item['lote'] ?? null,
                'fabricacao' => !empty($item['fabricacao']) ? $item['fabricacao'] : null,
                'validade' => !empty($item['validade']) ? $item['validade'] : null,
                // Campos de IBS/CBS
                'cclass' => $item['cclass'] ?? '000001',
                'aliquota_ibs' => floatval($item['aliquota_ibs'] ?? 0),
                'reducao_aliquota_ibs' => floatval($item['reducao_aliquota_ibs'] ?? 0),
                'aliquota_cbs' => floatval($item['aliquota_cbs'] ?? 0),
                'reducao_aliquota_cbs' => floatval($item['reducao_aliquota_cbs'] ?? 0),
                'aliquota_ibs_municipal' => floatval($item['aliquota_ibs_municipal'] ?? 0),
                'reducao_aliquota_ibs_municipal' => floatval($item['reducao_aliquota_ibs_municipal'] ?? 0),
            ];

            // Adicionar parâmetros de IBS/CBS se as colunas existirem
            if (!$colunaCclassExiste) {
                unset($params['cclass']);
            }
            if (!$colunaAliquotaIbsExiste) {
                unset($params['aliquota_ibs']);
            }
            if (!$colunaReducaoAliquotaIbsExiste) {
                unset($params['reducao_aliquota_ibs']);
            }
            if (!$colunaAliquotaCbsExiste) {
                unset($params['aliquota_cbs']);
            }
            if (!$colunaReducaoAliquotaCbsExiste) {
                unset($params['reducao_aliquota_cbs']);
            }
            if (!$colunaAliquotaIbsMunicipalExiste) {
                unset($params['aliquota_ibs_municipal']);
            }
            if (!$colunaReducaoAliquotaIbsMunicipalExiste) {
                unset($params['reducao_aliquota_ibs_municipal']);
            }

            // Processar vendedor_id, comissao e profissional_id
            $vendedorId = !empty($item['vendedor_id']) ? (int) $item['vendedor_id'] : null;
            // Buscar profissional_id do item ou usar o geral
            $profissionalId = !empty($item['profissional_id']) ? (int) $item['profissional_id'] : ($profissionalIdGeral ? (int) $profissionalIdGeral : null);

            if ($colunaVendedorIdExiste && $vendedorId) {
                $params['vendedor_id'] = $vendedorId;
            }

            // Se a coluna profissional_id existe, sempre adicionar aos params (pode ser null)
            if ($colunaProfissionalIdExiste) {
                $params['profissional_id'] = $profissionalId;
            }

            // Processar comissão e percentual de comissão
            // Priorizar profissional_id, depois vendedor_id
            $usuarioComissaoId = $profissionalId ?: $vendedorId;
            $percentualComissao = 0;
            if ($usuarioComissaoId) {
                // Buscar comissão do usuário (profissional ou vendedor)
                try {
                    $stmtComissao = $this->db->prepare("
                        SELECT comissao FROM users
                        WHERE id = :user_id AND company_id = :company_id
                        LIMIT 1
                    ");
                    $stmtComissao->execute([
                        'user_id' => $usuarioComissaoId,
                        'company_id' => $companyId
                    ]);
                    $userData = $stmtComissao->fetch();

                    if ($userData && isset($userData['comissao'])) {
                        $percentualComissao = floatval($userData['comissao'] ?? 0);
                    }
                } catch (\Exception $e) {
                    error_log('[Vendas] Erro ao buscar comissão do usuário: ' . $e->getMessage());
                    $percentualComissao = 0;
                }
            }

            // Se a coluna comissao existe, sempre adicionar aos params (mesmo que seja 0)
            if ($colunaComissaoExiste) {
                if ($percentualComissao > 0) {
                    // Calcular comissão: (total_price * percentual_comissao) / 100
                    $comissaoCalculada = ($totalItem * $percentualComissao) / 100;
                    $params['comissao'] = $comissaoCalculada;
                } else {
                    $params['comissao'] = 0;
                }
            }

            // Se a coluna comissao_percentual existe, sempre adicionar aos params (pode ser 0)
            if ($colunaComissaoPercentualExiste) {
                $params['comissao_percentual'] = $percentualComissao;
            }

            try {
                error_log('[Vendas] Executando inserção de item. Params: ' . json_encode(array_keys($params)));
                $stmt->execute($params);
            } catch (\Exception $e) {
                error_log('[Vendas] ERRO ao executar INSERT de item: ' . $e->getMessage());
                error_log('[Vendas] SQL: ' . $sql);
                error_log('[Vendas] Params: ' . json_encode($params));
                $errorInfo = $stmt->errorInfo();
                error_log('[Vendas] ErrorInfo: ' . json_encode($errorInfo));
                throw $e;
            }
        }

        // APÓS salvar os itens, verificar se deve movimentar estoque
        $deveMovimentar = false;

        if ($updateStock) {
            $deveMovimentar = true;
            error_log("Venda #{$vendaId}: Checkbox 'Atualizar Estoque' está marcado");
        } elseif (!empty($statusCodigo)) {
            // Verificar se o status permite movimentar estoque
            $stmtStatus = $this->db->prepare("
                SELECT movimenta_estoque FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = 'vendas'
                  AND codigo = :codigo
                  AND ativo = 1
                LIMIT 1
            ");
            $stmtStatus->execute([
                'company_id' => $companyId,
                'codigo' => $statusCodigo
            ]);
            $statusInfo = $stmtStatus->fetch();

            if ($statusInfo && isset($statusInfo['movimenta_estoque']) && (int) $statusInfo['movimenta_estoque'] == 1) {
                $deveMovimentar = true;
                error_log("Venda #{$vendaId}: Status '{$statusCodigo}' permite movimentar estoque");
            }
        }

        if ($deveMovimentar) {
            // Usar os itens recém-salvos que foram passados como parâmetro
            // Filtrar apenas os que têm product_id
            $itensComProductId = array_filter($itens, function ($item) {
                return !empty($item['product_id']) && !empty($item['quantity']) && (float) $item['quantity'] > 0;
            });

            if (!empty($itensComProductId)) {
                error_log("Venda #{$vendaId}: 🚀 Movimentando estoque com " . count($itensComProductId) . " itens");
                error_log("Venda #{$vendaId}: Itens para movimentar: " . json_encode($itensComProductId));
                try {
                    $this->movimentarEstoque($vendaId, array_values($itensComProductId));
                } catch (\Exception $e) {
                    error_log("Venda #{$vendaId}: ❌ ERRO ao movimentar estoque: " . $e->getMessage());
                    error_log("Venda #{$vendaId}: Stack trace: " . $e->getTraceAsString());
                    // Não interromper o processo se der erro na movimentação
                }
            } else {
                error_log("Venda #{$vendaId}: ⚠️ Nenhum item válido encontrado para movimentar estoque");
            }
        } else {
            error_log("Venda #{$vendaId}: ⚠️ Movimentação de estoque não habilitada - updateStock: " . ($updateStock ? 'SIM' : 'NÃO') . ", status: {$statusCodigo}");
        }
    }

    private function atualizarEstoque(int $vendaId, array $itens): void
    {
        $stmt = $this->db->prepare("
            UPDATE produtos
            SET stock_quantity = stock_quantity - :quantity
            WHERE id = :product_id AND company_id = :company_id
        ");

        foreach ($itens as $item) {
            if (!empty($item['product_id'])) {
                $stmt->execute([
                    'quantity' => $item['quantity'] ?? 0,
                    'product_id' => $item['product_id'],
                    'company_id' => $this->getCompanyId()
                ]);
            }
        }
    }

    private function salvarPagamentos(int $vendaId, array $vendaData): void
    {
        try {
            $companyId = $this->getCompanyId();

            // Só salvar se houver método de pagamento e parcelas
            if (empty($vendaData['payment_method_id'])) {
                return;
            }

            $totalVenda = (float) ($vendaData['total'] ?? 0);
            if ($totalVenda <= 0) {
                return;
            }

            $numParcelas = (int) ($vendaData['installments'] ?? 1);
            $numParcelas = max(1, $numParcelas);

            $dataVenda = $vendaData['sale_date'] ?? date('Y-m-d');
            $dataBase = new \DateTime($dataVenda);

            // Buscar informações do prazo de pagamento se houver
            $diasPrazoTotal = 0;
            if (!empty($vendaData['payment_term_id'])) {
                $stmtPrazo = $this->db->prepare("
                    SELECT dias FROM metodos_pagamento_prazos
                    WHERE id = :term_id AND company_id = :company_id AND ativo = 1
                    LIMIT 1
                ");
                $stmtPrazo->execute([
                    'term_id' => $vendaData['payment_term_id'],
                    'company_id' => $companyId
                ]);
                $prazo = $stmtPrazo->fetch();
                if ($prazo && !empty($prazo['dias'])) {
                    $diasPrazoTotal = (int) $prazo['dias'];
                }
            }

            // Se não há prazo configurado, usar 30 dias por parcela como padrão
            if ($diasPrazoTotal <= 0) {
                $diasPrazoTotal = 30 * $numParcelas;
            }

            // Calcular dias por parcela
            $diasPorParcela = (int) ceil($diasPrazoTotal / $numParcelas);

            // Calcular valor das parcelas
            $valorParcela = $totalVenda / $numParcelas;
            $valorParcelaArredondado = floor($valorParcela * 100) / 100;
            $resto = $totalVenda - ($valorParcelaArredondado * $numParcelas);

            // Preparar query de inserção
            $stmtInsert = $this->db->prepare("
                INSERT INTO vendas_pagamentos (
                    company_id, venda_id, numero_parcela, total_parcelas, valor_parcela,
                    data_vencimento, metodo_pagamento_id, payment_term_id, dias_prazo,
                    status, created_at, updated_at
                ) VALUES (
                    :company_id, :venda_id, :numero_parcela, :total_parcelas, :valor_parcela,
                    :data_vencimento, :metodo_pagamento_id, :payment_term_id, :dias_prazo,
                    'pendente', NOW(), NOW()
                )
            ");

            // Criar um registro para cada parcela
            for ($i = 1; $i <= $numParcelas; $i++) {
                // Calcular valor da parcela
                $valorParcelaAtual = $valorParcelaArredondado;
                if ($i == $numParcelas) {
                    // Última parcela recebe o resto
                    $valorParcelaAtual += $resto;
                }

                // Calcular data de vencimento
                $dataVencimento = clone $dataBase;
                $diasAdicionar = ($i - 1) * $diasPorParcela;
                $dataVencimento->modify("+{$diasAdicionar} days");

                $stmtInsert->execute([
                    'company_id' => $companyId,
                    'venda_id' => $vendaId,
                    'numero_parcela' => $i,
                    'total_parcelas' => $numParcelas,
                    'valor_parcela' => round($valorParcelaAtual, 2),
                    'data_vencimento' => $dataVencimento->format('Y-m-d'),
                    'metodo_pagamento_id' => $vendaData['payment_method_id'],
                    'payment_term_id' => $vendaData['payment_term_id'] ?? null,
                    'dias_prazo' => $diasAdicionar + $diasPorParcela
                ]);
            }

            error_log("Venda #{$vendaId}: Salvos {$numParcelas} registro(s) de pagamento com sucesso");
        } catch (\Exception $e) {
            error_log("Erro ao salvar pagamentos para venda #{$vendaId}: " . $e->getMessage());
            // Não lançar exceção para não interromper o processo
        }
    }

    private function gerarContasReceber(int $vendaId, array $vendaData): void
    {
        error_log("========================================");
        error_log("========== GERAR CONTAS RECEBER INICIADA ==========");
        error_log("========================================");
        error_log("Venda ID: {$vendaId}");
        error_log("Dados recebidos: " . json_encode($vendaData, JSON_UNESCAPED_UNICODE));
        error_log("========================================");

        // Verificar dados mínimos ANTES de tudo
        if (empty($vendaData['payment_method_id'])) {
            error_log("❌❌❌ ERRO CRÍTICO: Não há método de pagamento! payment_method_id: " . ($vendaData['payment_method_id'] ?? 'NULL'));
            throw new \Exception("Método de pagamento é obrigatório para gerar contas a receber");
        }

        if (empty($vendaData['total']) || (float) $vendaData['total'] <= 0) {
            error_log("❌❌❌ ERRO CRÍTICO: Total inválido! total: " . ($vendaData['total'] ?? 'NULL'));
            throw new \Exception("Total da venda deve ser maior que zero");
        }

        try {
            $companyId = $this->getCompanyId();
            error_log("Company ID: {$companyId}");

            if (!$companyId) {
                error_log("❌❌❌ ERRO CRÍTICO: Company ID é NULL!");
                throw new \Exception("Company ID não pode ser nulo");
            }

            error_log("Venda #{$vendaId}: === INICIANDO geração de contas a receber ===");

            // Verificar estrutura da tabela ANTES de usar
            $stmtCheck = $this->db->prepare("SHOW COLUMNS FROM contas_receber");
            $stmtCheck->execute();
            $colunas = $stmtCheck->fetchAll(\PDO::FETCH_COLUMN);
            $colunasArray = array_flip($colunas);

            $temCustomerId = isset($colunasArray['customer_id']);
            $temPessoaId = isset($colunasArray['pessoa_id']);
            $temVendaId = isset($colunasArray['venda_id']);
            $temAmountPaid = isset($colunasArray['amount_paid']);
            $temAmountReceived = isset($colunasArray['amount_received']);
            $temMetodoPagamentoId = isset($colunasArray['metodo_pagamento_id']);
            $temPaymentMethod = isset($colunasArray['payment_method']);
            $temNumero = isset($colunasArray['numero']);

            error_log("Venda #{$vendaId}: Campos detectados - pessoa_id: " . ($temPessoaId ? 'SIM' : 'NÃO') . ", customer_id: " . ($temCustomerId ? 'SIM' : 'NÃO') . ", amount_paid: " . ($temAmountPaid ? 'SIM' : 'NÃO') . ", amount_received: " . ($temAmountReceived ? 'SIM' : 'NÃO') . ", numero: " . ($temNumero ? 'SIM' : 'NÃO'));

            // Verificar se já existem contas a receber para esta venda
            $camposSelect = ['id', 'description', 'amount', 'amount_remaining', 'due_date'];
            if ($temAmountPaid) {
                $camposSelect[] = 'amount_paid';
            } elseif ($temAmountReceived) {
                $camposSelect[] = 'amount_received';
            }

            // Usar venda_id APENAS se o campo existir, caso contrário usar description LIKE
            if ($temVendaId) {
                $whereVenda = "venda_id = :venda_id AND company_id = :company_id";
                $paramsVenda = ['venda_id' => $vendaId, 'company_id' => $companyId];
            } else {
                // Se não tem venda_id, usar description LIKE como fallback
                $whereVenda = "company_id = :company_id AND description LIKE :desc_pattern";
                $descPattern = "#{$vendaData['sale_number']}%";
                $paramsVenda = ['company_id' => $companyId, 'desc_pattern' => $descPattern];
            }

            $stmtExistentes = $this->db->prepare("
                SELECT " . implode(', ', $camposSelect) . "
                FROM contas_receber
                WHERE {$whereVenda}
                ORDER BY due_date ASC
            ");

            $stmtExistentes->execute($paramsVenda);

            $contasExistentes = $stmtExistentes->fetchAll();

            $totalVenda = (float) ($vendaData['total'] ?? 0);
            error_log("Venda #{$vendaId}: Total da venda = " . number_format($totalVenda, 2, ',', '.'));

            if ($totalVenda <= 0) {
                error_log("Venda #{$vendaId}: ❌ ERRO CRÍTICO - Total é zero ou negativo. Valor: {$totalVenda}");
                throw new \Exception("Total da venda deve ser maior que zero. Valor atual: {$totalVenda}");
            }

            $numParcelas = (int) ($vendaData['installments'] ?? 1);
            $numParcelas = max(1, $numParcelas); // Garantir pelo menos 1 parcela
            error_log("Venda #{$vendaId}: Número de parcelas = {$numParcelas}");

            $dataVenda = $vendaData['sale_date'] ?? date('Y-m-d');
            $dataBase = new \DateTime($dataVenda);
            error_log("Venda #{$vendaId}: Data base da venda = {$dataVenda}");

            // Buscar informações do prazo de pagamento se houver
            $diasPrazoTotal = 0;
            if (!empty($vendaData['payment_term_id'])) {
                $stmtPrazo = $this->db->prepare("
                    SELECT dias FROM metodos_pagamento_prazos
                    WHERE id = :term_id AND company_id = :company_id AND ativo = 1
                    LIMIT 1
                ");
                $stmtPrazo->execute([
                    'term_id' => $vendaData['payment_term_id'],
                    'company_id' => $companyId
                ]);
                $prazo = $stmtPrazo->fetch();
                if ($prazo && !empty($prazo['dias'])) {
                    $diasPrazoTotal = (int) $prazo['dias'];
                }
            }

            // Se não há prazo configurado, usar 30 dias por parcela como padrão
            if ($diasPrazoTotal <= 0) {
                $diasPrazoTotal = 30 * $numParcelas;
            }

            // Calcular dias por parcela (distribuindo o prazo total entre as parcelas)
            $diasPorParcela = (int) ceil($diasPrazoTotal / $numParcelas);

            // Calcular valor das parcelas
            $valorParcela = $totalVenda / $numParcelas;
            $valorParcelaArredondado = floor($valorParcela * 100) / 100; // Arredondar para baixo
            $resto = $totalVenda - ($valorParcelaArredondado * $numParcelas);

            error_log("Venda #{$vendaId}: Cálculo das parcelas:");
            error_log("  - Valor por parcela (sem arredondar): " . number_format($valorParcela, 2, ',', '.'));
            error_log("  - Valor por parcela (arredondado): " . number_format($valorParcelaArredondado, 2, ',', '.'));
            error_log("  - Resto a distribuir: " . number_format($resto, 2, ',', '.'));

            // Buscar próximo número de conta APENAS se o campo existir
            // IMPORTANTE: Esta busca deve ser feita DEPOIS de detectar se o campo existe
            $proximoNumero = 1;
            // O $temNumero já foi detectado acima, então só busca se existir

            error_log("Venda #{$vendaId}: Estrutura da tabela - customer_id: " . ($temCustomerId ? 'SIM' : 'NÃO') .
                ", pessoa_id: " . ($temPessoaId ? 'SIM' : 'NÃO') .
                ", venda_id: " . ($temVendaId ? 'SIM' : 'NÃO') .
                ", amount_paid: " . ($temAmountPaid ? 'SIM' : 'NÃO') .
                ", amount_received: " . ($temAmountReceived ? 'SIM' : 'NÃO'));

            // Se sale_number não estiver nos dados, buscar da venda
            if (empty($vendaData['sale_number'])) {
                $stmtVenda = $this->db->prepare("SELECT sale_number FROM vendas WHERE id = :id AND company_id = :company_id");
                $stmtVenda->execute(['id' => $vendaId, 'company_id' => $companyId]);
                $vendaInfo = $stmtVenda->fetch();
                if ($vendaInfo) {
                    $vendaData['sale_number'] = $vendaInfo['sale_number'];
                } else {
                    $vendaData['sale_number'] = "VND-{$vendaId}"; // Fallback
                    error_log("Venda #{$vendaId}: ⚠️ sale_number não encontrado, usando fallback: {$vendaData['sale_number']}");
                }
            }

            // Preparar dados do cliente
            $customerType = $vendaData['customer_type'] ?? 'pessoa';
            $clienteId = $vendaData['customer_id'] ?? null;
            $clienteNome = $vendaData['customer_name'] ?? 'Consumidor Final';

            // Buscar nome do cliente se necessário
            if ($clienteId && (empty($clienteNome) || $clienteNome === 'Consumidor Final')) {
                if ($temPessoaId) {
                    $stmtCliente = $this->db->prepare("SELECT name FROM pessoas WHERE id = :id AND company_id = :company_id");
                    $stmtCliente->execute(['id' => $clienteId, 'company_id' => $companyId]);
                    $cliente = $stmtCliente->fetch();
                    if ($cliente) {
                        $clienteNome = $cliente['name'];
                    }
                }
            }

            // Descrição da conta
            $descricao = "#{$vendaData['sale_number']}";
            if ($numParcelas > 1) {
                $descricao .= " - Parcela {PARCELA} de {$numParcelas}";
            }

            // Mapear contas existentes por número de parcela (extraído da descrição)
            $contasPorParcela = [];
            foreach ($contasExistentes as $conta) {
                // Tentar extrair o número da parcela da descrição
                // Exemplo: "Venda #VND-000001 - Parcela 1 de 3" ou apenas "Venda #VND-000001"
                preg_match('/Parcela\s+(\d+)/i', $conta['description'] ?? '', $matches);
                $parcela = !empty($matches[1]) ? (int) $matches[1] : 1;
                $contasPorParcela[$parcela] = $conta;
            }

            // Se o número de parcelas mudou e há contas existentes, deletar as antigas
            if (count($contasExistentes) > 0 && count($contasExistentes) != $numParcelas) {
                error_log("Venda #{$vendaId}: Número de parcelas mudou ({$numParcelas} vs " . count($contasExistentes) . "). Deletando contas antigas.");

                if ($temVendaId) {
                    $stmtDelete = $this->db->prepare("
                        DELETE FROM contas_receber
                        WHERE venda_id = :venda_id AND company_id = :company_id
                    ");
                    $stmtDelete->execute(['venda_id' => $vendaId, 'company_id' => $companyId]);
                } else {
                    // Se não tem venda_id, deletar pela description
                    $descPattern = "#{$vendaData['sale_number']}%";
                    $stmtDelete = $this->db->prepare("
                        DELETE FROM contas_receber
                        WHERE company_id = :company_id AND description LIKE :desc_pattern
                    ");
                    $stmtDelete->execute(['company_id' => $companyId, 'desc_pattern' => $descPattern]);
                }

                $contasPorParcela = []; // Limpar o mapeamento
            }

            // Não precisamos buscar o nome do método de pagamento
            // Vamos usar apenas o ID se o campo payment_method existir e aceitar ID

            // Buscar próximo número APENAS se o campo numero existir na tabela
            if ($temNumero) {
                try {
                    $stmtNum = $this->db->prepare("
                        SELECT numero FROM contas_receber
                        WHERE company_id = :company_id AND numero IS NOT NULL AND numero != ''
                        ORDER BY CAST(numero AS UNSIGNED) DESC LIMIT 1
                    ");
                    $stmtNum->execute(['company_id' => $companyId]);
                    $ultimaConta = $stmtNum->fetch();

                    if ($ultimaConta && !empty($ultimaConta['numero'])) {
                        $ultimoNumero = (int) filter_var($ultimaConta['numero'], FILTER_SANITIZE_NUMBER_INT);
                        if ($ultimoNumero > 0) {
                            $proximoNumero = $ultimoNumero + 1;
                        }
                    }
                    error_log("Venda #{$vendaId}: Próximo número calculado: {$proximoNumero}");
                } catch (\Exception $e) {
                    error_log("Venda #{$vendaId}: ⚠️ Erro ao buscar próximo número: " . $e->getMessage());
                    $proximoNumero = 1; // Fallback
                    // Se der erro ao buscar, o campo pode não existir - não incluir no INSERT
                    $temNumero = false;
                }
            }

            // Preparar queries de inserção baseado na estrutura REAL da tabela
            // A tabela pode ter amount_paid OU amount_received
            $camposInsert = ['company_id', 'description', 'amount', 'amount_remaining', 'due_date', 'status', 'notes'];
            $valoresInsert = [':company_id', ':description', ':amount', ':amount_remaining', ':due_date', ':status', ':notes'];

            if ($temAmountPaid) {
                $camposInsert[] = 'amount_paid';
                $valoresInsert[] = ':amount_paid';
            } elseif ($temAmountReceived) {
                $camposInsert[] = 'amount_received';
                $valoresInsert[] = ':amount_received';
            }

            if ($temNumero) {
                $camposInsert[] = 'numero';
                $valoresInsert[] = ':numero';
            }

            // Usar pessoa_id se existir, caso contrário customer_id
            if ($temPessoaId) {
                $camposInsert[] = 'pessoa_id';
                $valoresInsert[] = ':pessoa_id';
            } elseif ($temCustomerId) {
                $camposInsert[] = 'customer_id';
                $valoresInsert[] = ':customer_id';
            }

            if ($temVendaId) {
                $camposInsert[] = 'venda_id';
                $valoresInsert[] = ':venda_id';
            }

            // Priorizar metodo_pagamento_id se existir, caso contrário usar payment_method com ID
            if ($temMetodoPagamentoId && !empty($vendaData['payment_method_id'])) {
                $camposInsert[] = 'metodo_pagamento_id';
                $valoresInsert[] = ':metodo_pagamento_id';
            } elseif ($temPaymentMethod && !empty($vendaData['payment_method_id'])) {
                // Se payment_method existe mas não metodo_pagamento_id, usar payment_method com o ID
                $camposInsert[] = 'payment_method';
                $valoresInsert[] = ':payment_method';
            }

            $sqlInsert = "INSERT INTO contas_receber (" . implode(', ', $camposInsert) . ", created_at, updated_at) VALUES (" . implode(', ', $valoresInsert) . ", NOW(), NOW())";

            error_log("========== SQL INSERT ==========");
            error_log("SQL: {$sqlInsert}");
            error_log("Campos detectados: " . json_encode($camposInsert));

            $stmtInsert = $this->db->prepare($sqlInsert);

            if (!$stmtInsert) {
                $errorInfo = $this->db->errorInfo();
                error_log("❌❌❌ ERRO ao preparar INSERT: " . json_encode($errorInfo));
                throw new \Exception("Erro ao preparar SQL INSERT: " . json_encode($errorInfo));
            }

            error_log("✅ SQL INSERT preparado com sucesso");

            // Query de UPDATE
            $camposUpdate = ['description', 'amount', 'amount_remaining', 'due_date', 'notes'];
            $sets = [];
            foreach ($camposUpdate as $campo) {
                $sets[] = "{$campo} = :{$campo}";
            }

            if ($temCustomerId) {
                $sets[] = 'customer_id = :customer_id';
            }

            if ($temMetodoPagamentoId) {
                $sets[] = 'metodo_pagamento_id = :metodo_pagamento_id';
            } elseif ($temPaymentMethod) {
                $sets[] = 'payment_method = :payment_method';
            }

            $sqlUpdate = "UPDATE contas_receber SET " . implode(', ', $sets) . ", updated_at = NOW() WHERE id = :id AND company_id = :company_id";
            $stmtUpdate = $this->db->prepare($sqlUpdate);

            error_log("Venda #{$vendaId}: SQL UPDATE preparado: {$sqlUpdate}");

            // Criar ou atualizar uma conta para cada parcela
            for ($i = 1; $i <= $numParcelas; $i++) {
                // Calcular valor da parcela
                $valorParcelaAtual = $valorParcelaArredondado;
                if ($i == $numParcelas) {
                    // Última parcela recebe o resto
                    $valorParcelaAtual += $resto;
                }

                // Garantir que o valor não seja zero
                if ($valorParcelaAtual <= 0) {
                    error_log("Venda #{$vendaId}: ⚠️ AVISO - Parcela {$i} tem valor zero! Usando valor total dividido igualmente.");
                    $valorParcelaAtual = $totalVenda / $numParcelas;
                }

                $valorFinal = round($valorParcelaAtual, 2);
                error_log("Venda #{$vendaId}: Parcela {$i} - Valor final calculado = R$ " . number_format($valorFinal, 2, ',', '.') . " ({$valorFinal})");

                // Calcular data de vencimento
                $dataVencimento = clone $dataBase;
                $diasAdicionar = ($i - 1) * $diasPorParcela;
                $dataVencimento->modify("+{$diasAdicionar} days");

                // Descrição com número da parcela
                $descricaoParcela = str_replace('{PARCELA}', strval($i), $descricao);

                // Verificar se já existe conta para esta parcela
                $contaExistente = $contasPorParcela[$i] ?? null;

                if ($contaExistente) {
                    // Atualizar conta existente
                    $valorRecebido = (float) ($contaExistente['amount_paid'] ?? $contaExistente['amount_received'] ?? 0);
                    $novoValorTotal = round($valorParcelaAtual, 2);
                    $novoValorRestante = max(0, $novoValorTotal - $valorRecebido);

                    $dadosUpdate = [
                        'id' => $contaExistente['id'],
                        'company_id' => $companyId,
                        'description' => $descricaoParcela,
                        'amount' => (float) $novoValorTotal,
                        'amount_remaining' => (float) $novoValorRestante,
                        'due_date' => $dataVencimento->format('Y-m-d'),
                        'notes' => !empty($vendaData['notes']) ? "Venda #{$vendaData['sale_number']}: {$vendaData['notes']}" : "Gerada automaticamente da venda #{$vendaData['sale_number']}"
                    ];

                    if ($temPessoaId && $clienteId) {
                        $dadosUpdate['pessoa_id'] = (int) $clienteId;
                    } elseif ($temCustomerId && $clienteId) {
                        $dadosUpdate['customer_id'] = (int) $clienteId;
                    }

                    if ($temMetodoPagamentoId && !empty($vendaData['payment_method_id'])) {
                        $dadosUpdate['metodo_pagamento_id'] = (int) $vendaData['payment_method_id'];
                    } elseif ($temPaymentMethod && !empty($vendaData['payment_method_id'])) {
                        // Se payment_method existe mas não metodo_pagamento_id, usar payment_method com o ID
                        $dadosUpdate['payment_method'] = (int) $vendaData['payment_method_id'];
                    }

                    $stmtUpdate->execute($dadosUpdate);
                    error_log("Venda #{$vendaId}: ✅ Conta #{$contaExistente['id']} (parcela {$i}) ATUALIZADA - Amount: {$novoValorTotal}, Remaining: {$novoValorRestante}");
                } else {
                    // Criar nova conta
                    // Usar $valorFinal já calculado acima
                    if (!isset($valorFinal)) {
                        $valorFinal = round($valorParcelaAtual, 2);
                    }

                    error_log("Venda #{$vendaId}: Preenchendo dados da parcela {$i}");
                    error_log("  - Valor final: {$valorFinal}");
                    error_log("  - Descrição: {$descricaoParcela}");

                    $dadosConta = [
                        'company_id' => (int) $companyId,
                        'description' => $descricaoParcela,
                        'amount' => (float) $valorFinal,
                        'amount_remaining' => (float) $valorFinal,
                        'due_date' => $dataVencimento->format('Y-m-d'),
                        'status' => 'pendente',
                        'notes' => !empty($vendaData['notes']) ? "Venda #{$vendaData['sale_number']}: {$vendaData['notes']}" : "Gerada automaticamente da venda #{$vendaData['sale_number']}"
                    ];

                    // Adicionar campo correto de valor pago/recebido
                    if ($temAmountPaid) {
                        $dadosConta['amount_paid'] = 0.00;
                    } elseif ($temAmountReceived) {
                        $dadosConta['amount_received'] = 0.00;
                    }

                    // Garantir que TODOS os parâmetros do SQL estão no array
                    // Isso evita erro "Invalid parameter number"

                    if ($temNumero) {
                        $dadosConta['numero'] = (string) $proximoNumero;
                        error_log("Venda #{$vendaId}: Parcela {$i} - Número da conta: {$proximoNumero}");
                    }

                    if ($temPessoaId) {
                        if ($clienteId) {
                            $dadosConta['pessoa_id'] = (int) $clienteId;
                        }
                    } elseif ($temCustomerId) {
                        if ($clienteId) {
                            $dadosConta['customer_id'] = (int) $clienteId;
                        }
                    }

                    if ($temVendaId) {
                        $dadosConta['venda_id'] = (int) $vendaId;
                    }

                    if ($temMetodoPagamentoId && !empty($vendaData['payment_method_id'])) {
                        $dadosConta['metodo_pagamento_id'] = (int) $vendaData['payment_method_id'];
                    } elseif ($temPaymentMethod && !empty($vendaData['payment_method_id'])) {
                        // Se payment_method existe mas não metodo_pagamento_id, usar payment_method com o ID
                        $dadosConta['payment_method'] = (int) $vendaData['payment_method_id'];
                    }

                    // Verificar se todos os parâmetros necessários estão presentes
                    $parametrosNecessarios = [];
                    preg_match_all('/:(\w+)/', $sqlInsert, $matches);
                    if (!empty($matches[1])) {
                        $parametrosNecessarios = array_unique($matches[1]);
                    }

                    $parametrosFaltando = array_diff($parametrosNecessarios, array_keys($dadosConta));
                    if (!empty($parametrosFaltando)) {
                        error_log("Venda #{$vendaId}: ❌❌❌ ERRO - Parâmetros faltando no array \$dadosConta: " . json_encode($parametrosFaltando));
                        error_log("Parâmetros necessários: " . json_encode($parametrosNecessarios));
                        error_log("Parâmetros presentes: " . json_encode(array_keys($dadosConta)));
                        throw new \Exception("Parâmetros faltando no INSERT: " . implode(', ', $parametrosFaltando));
                    }

                    $clienteIdLog = $dadosConta['pessoa_id'] ?? $dadosConta['customer_id'] ?? 'NULL';
                    error_log("Venda #{$vendaId}: Parcela {$i} - PREPARANDO INSERT - Amount: {$dadosConta['amount']}, Remaining: {$dadosConta['amount_remaining']}, Cliente ID: {$clienteIdLog}");
                    error_log("Venda #{$vendaId}: Parcela {$i} - Todos os parâmetros verificados: " . json_encode(array_keys($dadosConta)));

                    try {
                        error_log("Venda #{$vendaId}: EXECUTANDO INSERT - Dados: " . json_encode($dadosConta, JSON_UNESCAPED_UNICODE));
                        error_log("Venda #{$vendaId}: SQL: {$sqlInsert}");

                        $resultado = $stmtInsert->execute($dadosConta);
                        $idInserido = $this->db->lastInsertId();

                        error_log("Venda #{$vendaId}: Resultado execute(): " . ($resultado ? 'SUCESSO' : 'FALHOU') . " | LastInsertId: " . ($idInserido ?? 'NULL'));

                        // Verificar se foi inserido corretamente
                        if ($resultado && $idInserido) {
                            $camposCheck = ['amount', 'amount_remaining'];
                            if ($temAmountPaid) {
                                $camposCheck[] = 'amount_paid';
                            } elseif ($temAmountReceived) {
                                $camposCheck[] = 'amount_received';
                            }
                            $stmtCheck = $this->db->prepare("SELECT " . implode(', ', $camposCheck) . " FROM contas_receber WHERE id = :id");
                            $stmtCheck->execute(['id' => $idInserido]);
                            $contaVerificada = $stmtCheck->fetch();

                            if ($contaVerificada) {
                                error_log("Venda #{$vendaId}: ✅✅✅ CONTA CRIADA E VERIFICADA - ID: {$idInserido} | Parcela: {$i} | Amount: {$contaVerificada['amount']} | Remaining: {$contaVerificada['amount_remaining']}");

                                // Incrementar número apenas se inserção foi bem-sucedida
                                if ($temNumero) {
                                    $proximoNumero++;
                                }

                                // INTEGRAÇÃO SHIPAY: Gerar boleto híbrido automaticamente se método de pagamento for Shipay
                                try {
                                    $this->gerarBoletoShipayParaConta((int) $idInserido, [
                                        'metodo_pagamento_id' => $vendaData['payment_method_id'] ?? null,
                                        'pessoa_id' => $clienteId,
                                        'amount' => $valorFinal,
                                        'due_date' => $dataVencimento->format('Y-m-d'),
                                        'description' => $descricaoParcela,
                                    ]);
                                } catch (\Exception $eShipay) {
                                    error_log("Venda #{$vendaId}: Shipay - Erro ao gerar boleto: " . $eShipay->getMessage());
                                    // Não interrompe o fluxo
                                }
                            } else {
                                error_log("Venda #{$vendaId}: ⚠️ Conta criada (ID: {$idInserido}) mas não encontrada ao verificar");
                            }
                        } else {
                            // Verificar erro do PDO
                            $errorInfo = $stmtInsert->errorInfo();
                            error_log("Venda #{$vendaId}: ❌❌❌ FALHA AO INSERIR - Parcela {$i}");
                            error_log("  - Resultado execute(): " . ($resultado ? 'true' : 'false'));
                            error_log("  - LastInsertId: " . ($idInserido ?? 'NULL'));
                            error_log("  - PDO Error Info: " . json_encode($errorInfo));
                            error_log("  - SQL preparado: {$sqlInsert}");
                            // NÃO incrementar se falhou
                        }
                    } catch (\Exception $e) {
                        error_log("Venda #{$vendaId}: ❌❌❌ EXCEÇÃO ao inserir parcela {$i}: " . $e->getMessage());
                        error_log("  - Arquivo: {$e->getFile()}:{$e->getLine()}");
                        error_log("  - Dados que tentaram inserir: " . json_encode($dadosConta, JSON_UNESCAPED_UNICODE));
                        error_log("  - SQL: {$sqlInsert}");
                        // Relançar para ver o erro completo
                        throw $e;
                    }
                }
            }

            error_log("========================================");
            error_log("Venda #{$vendaId}: ✅✅✅ FINALIZADO COM SUCESSO");
            error_log("  - {$numParcelas} conta(s) a receber processadas");
            error_log("========================================");
        } catch (\Exception $e) {
            error_log("========================================");
            error_log("Venda #{$vendaId}: ❌❌❌ ERRO FATAL ao gerar contas a receber");
            error_log("  - Mensagem: " . $e->getMessage());
            error_log("  - Arquivo: {$e->getFile()}:{$e->getLine()}");
            error_log("  - Stack trace: " . $e->getTraceAsString());
            error_log("========================================");
            // RELANÇAR exceção para que o erro seja visível
            throw $e;
        }
    }

    private function movimentarEstoque(int $vendaId, array $itens): void
    {
        error_log("========== MOVIMENTAR ESTOQUE INICIADO ==========");
        error_log("Venda ID: {$vendaId}");
        error_log("Total de itens: " . count($itens));
        error_log("Itens recebidos: " . json_encode($itens));

        if (empty($itens)) {
            error_log("⚠️ Nenhum item recebido para movimentar estoque");
            return;
        }

        $companyId = $this->getCompanyId();
        if (!$companyId) {
            error_log("⚠️ Company ID não encontrado");
            return;
        }

        error_log("Company ID: {$companyId}");

        // Verificar se a tabela existe (silencioso - não interrompe se não existir)
        try {
            $stmtCheck = $this->db->query("SHOW TABLES LIKE 'estoque_movimentos'");
            if ($stmtCheck->rowCount() == 0) {
                error_log("⚠️ Tabela 'estoque_movimentos' não existe no banco de dados. Pulando movimentação.");
                return;
            }
            error_log("✅ Tabela 'estoque_movimentos' existe");
        } catch (\Exception $e) {
            error_log("⚠️ Erro ao verificar existência da tabela estoque_movimentos: " . $e->getMessage());
            return;
        }

        // Usar a estrutura real da tabela estoque_movimentos
        $sql = "INSERT INTO estoque_movimentos (
            company_id, product_id, origem_id, origem, type, quantity, reason, reference_type, reference_id,
            numero_serie, lote, fabricacao, validade, created_at
        ) VALUES (
            :company_id, :product_id, :origem_id, 'venda', 'saida', :quantity, :reason, 'venda', :reference_id,
            :numero_serie, :lote, :fabricacao, :validade, NOW()
        )";

        error_log("SQL preparado: {$sql}");

        $stmt = $this->db->prepare($sql);
        if (!$stmt) {
            $error = $this->db->errorInfo();
            error_log("❌ ERRO ao preparar SQL: " . json_encode($error));
            throw new \Exception("Erro ao preparar SQL para movimentar estoque: " . json_encode($error));
        }

        $inseridos = 0;
        foreach ($itens as $index => $item) {
            error_log("--- Processando item {$index} ---");
            error_log("Item data: " . json_encode($item));

            $productId = $item['product_id'] ?? $item['item_id'] ?? null;

            if (empty($productId)) {
                error_log("⚠️ Item {$index} sem product_id/item_id - pulando");
                continue;
            }

            if (empty($item['quantity']) || (float) $item['quantity'] <= 0) {
                error_log("⚠️ Item {$index} sem quantity válida - pulando");
                continue;
            }

            $productName = $item['product_name'] ?? 'Produto';

            // Garantir que quantity seja um número
            $quantidade = (float) ($item['quantity'] ?? 0);
            if ($quantidade <= 0) {
                error_log("⚠️ Item {$index} tem quantidade inválida: {$quantidade}");
                continue;
            }

            $params = [
                'company_id' => (int) $companyId,
                'product_id' => (int) $productId,
                'origem_id' => $vendaId,
                'quantity' => -abs($quantidade),
                'reason' => "Saída por venda #{$vendaId} - {$productName}",
                'reference_id' => $vendaId,
                'numero_serie' => $item['numero_serie'] ?? null,
                'lote' => $item['lote'] ?? null,
                'fabricacao' => !empty($item['fabricacao']) ? $item['fabricacao'] : null,
                'validade' => !empty($item['validade']) ? $item['validade'] : null,
            ];

            error_log("Parâmetros: " . json_encode($params));

            $result = $stmt->execute($params);
            if ($result) {
                $inseridos++;
                error_log("✅ Item {$index} inserido com sucesso (ID: " . $this->db->lastInsertId() . ")");
            } else {
                $error = $stmt->errorInfo();
                error_log("❌ ERRO ao inserir item {$index}: " . json_encode($error));
                throw new \Exception("Erro ao inserir movimento de estoque: " . json_encode($error));
            }
        }

        error_log("========== MOVIMENTAR ESTOQUE FINALIZADO ==========");
        error_log("Total de registros inseridos: {$inseridos}");
    }

    public function edit(): void
    {
        // Verificar permissão de edição
        if (!$this->canEdit('vendas')) {
            $this->response->forbidden('Você não tem permissão para editar vendas.');
            return;
        }

        try {
            $id = (int) $this->request->get('id');
            error_log("Iniciando edição de {$this->moduleSingular()}. ID: {$id}");

            $venda = $this->getVenda($id, $this->moduleOrigin());

            if (!$venda) {
                error_log("{$this->moduleSingular()} não encontrada. ID: {$id}");
                $this->response->notFound($this->moduleSingular() . ' não encontrada');
                return;
            }

            $companyId = $this->getCompanyId();
            error_log("Company ID: {$companyId}");

            // Busca clientes com tabela de preço (inicialmente vazio, será carregado via AJAX baseado na empresa selecionada)
            $clientes = [];

            // Produtos serão carregados dinamicamente via AJAX baseado na empresa selecionada
            $produtos = [];

            $usarLoteVenda = $this->configurarLotesVenda($companyId, $produtos);

            // Busca métodos de pagamento
            $stmt = $this->db->prepare("
                SELECT id, name, type FROM metodos_pagamento
                WHERE company_id = :company_id AND is_active = 1
                ORDER BY name ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $metodosPagamento = $stmt->fetchAll();

            // Busca vendedores (usuários ativos)
            $stmt = $this->db->prepare("
                SELECT id, name FROM users
                WHERE company_id = :company_id AND is_active = 1
                ORDER BY name ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $vendedores = $stmt->fetchAll() ?: [];

            // Verificar se deve exibir comissão por funcionário por item
            $comissaoFuncionarioItem = $this->verificarComissaoFuncionarioItem($companyId);
            $profissionaisItem = [];
            if ($comissaoFuncionarioItem) {
                // Verificar se o campo é 'type' ou 'role' e buscar profissionais (vendedor)
                try {
                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM users LIKE 'type'");
                    $temCampoType = $stmtCheck->fetch() !== false;

                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM users LIKE 'role'");
                    $temCampoRole = $stmtCheck->fetch() !== false;
                } catch (\Exception $e) {
                    $temCampoType = false;
                    $temCampoRole = false;
                }

                // Buscar profissionais (vendedor) - tentar type primeiro, depois role
                if ($temCampoType) {
                    $stmt = $this->db->prepare("
                        SELECT id, name FROM users
                        WHERE company_id = :company_id
                          AND type = 'vendedor'
                          AND is_active = 1
                        ORDER BY name ASC
                    ");
                } elseif ($temCampoRole) {
                    $stmt = $this->db->prepare("
                        SELECT id, name FROM users
                        WHERE company_id = :company_id
                          AND role = 'vendedor'
                          AND is_active = 1
                        ORDER BY name ASC
                    ");
                } else {
                    // Se não tiver nenhum dos campos, buscar todos os usuários ativos
                    $stmt = $this->db->prepare("
                        SELECT id, name FROM users
                        WHERE company_id = :company_id
                          AND is_active = 1
                        ORDER BY name ASC
                    ");
                }
                $stmt->execute(['company_id' => $companyId]);
                $profissionaisItem = $stmt->fetchAll() ?: [];

                // Debug: log dos profissionais encontrados
                error_log('[Vendas] Profissionais encontrados: ' . count($profissionaisItem));
                if (!empty($profissionaisItem)) {
                    error_log('[Vendas] Primeiro profissional: ' . json_encode($profissionaisItem[0]));
                }
            }

            // Busca status do módulo vendas
            $stmt = $this->db->prepare("
                SELECT codigo, nome, descricao, cor, icone, ordem, is_default
                FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = :modulo
                  AND ativo = 1
                ORDER BY ordem ASC
            ");
            $stmt->execute([
                'company_id' => $companyId,
                'modulo' => $this->moduleStatusKey()
            ]);
            $statusVendas = $stmt->fetchAll() ?: [];

            // Buscar empresas vinculadas ao usuário
            $empresasVinculadas = $this->getEmpresasVinculadasUsuario();

            // Busca itens da venda (vendas.id = vendas_itens.venda_id)
            // Verificar se a coluna vendedor_id existe na tabela
            $colunaVendedorIdExiste = false;
            if ($comissaoFuncionarioItem) {
                try {
                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'vendedor_id'");
                    $colunaVendedorIdExiste = $stmtCheck->fetch() !== false;
                } catch (\Exception $e) {
                    // Ignorar erro de verificação
                    $colunaVendedorIdExiste = false;
                }
            }

            // Verificar se a coluna comissao existe
            $colunaComissaoExiste = false;
            if ($comissaoFuncionarioItem) {
                try {
                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'comissao'");
                    $colunaComissaoExiste = $stmtCheck->fetch() !== false;
                } catch (\Exception $e) {
                    // Ignorar erro de verificação
                    $colunaComissaoExiste = false;
                }
            }

            // Verificar se a coluna profissional_id existe
            $colunaProfissionalIdExiste = false;
            try {
                $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'profissional_id'");
                $colunaProfissionalIdExiste = $stmtCheck->fetch() !== false;
            } catch (\Exception $e) {
                // Ignorar erro de verificação
                $colunaProfissionalIdExiste = false;
            }

            $vendedorIdField = ($comissaoFuncionarioItem && $colunaVendedorIdExiste) ? ', COALESCE(vendedor_id, "") as vendedor_id' : '';
            $comissaoField = ($comissaoFuncionarioItem && $colunaComissaoExiste) ? ', COALESCE(comissao, 0) as comissao' : '';
            $profissionalIdField = $colunaProfissionalIdExiste ? ', COALESCE(profissional_id, "") as profissional_id' : '';
            $stmt = $this->db->prepare("
                SELECT
                    id, company_id, venda_id, product_id, product_name, product_sku,
                    quantity, unit_price, discount, discount_type, total_price, notes,
                    COALESCE(ncm, '0000.00.00') as ncm,
                    COALESCE(cfop, '5102') as cfop,
                    COALESCE(origem, '0') as origem,
                    COALESCE(cst_icms, '00') as cst_icms,
                    COALESCE(aliquota_icms, 0) as aliquota_icms,
                    COALESCE(valor_icms, 0) as valor_icms,
                    COALESCE(base_calculo_icms, 0) as base_calculo_icms,
                    COALESCE(cst_pis, '01') as cst_pis,
                    COALESCE(aliquota_pis, 0) as aliquota_pis,
                    COALESCE(valor_pis, 0) as valor_pis,
                    COALESCE(base_calculo_pis, 0) as base_calculo_pis,
                    COALESCE(cst_cofins, '01') as cst_cofins,
                    COALESCE(aliquota_cofins, 0) as aliquota_cofins,
                    COALESCE(valor_cofins, 0) as valor_cofins,
                    COALESCE(base_calculo_cofins, 0) as base_calculo_cofins,
                    COALESCE(cst_ipi, '99') as cst_ipi,
                    COALESCE(aliquota_ipi, 0) as aliquota_ipi,
                    COALESCE(valor_ipi, 0) as valor_ipi,
                    COALESCE(origem_st, '0') as origem_st,
                    numero_serie,
                    lote,
                    fabricacao,
                    validade{$vendedorIdField}{$comissaoField}{$profissionalIdField},
                    created_at
                FROM vendas_itens
                WHERE venda_id = :venda_id AND company_id = :company_id
                ORDER BY id ASC
            ");
            $stmt->execute(['venda_id' => $id, 'company_id' => $companyId]);
            $itens = $stmt->fetchAll();

            foreach ($itens as &$item) {
                $item['lote'] = $item['lote'] ?? '';
                $item['fabricacao'] = $item['fabricacao'] ?? null;
                $item['validade'] = $item['validade'] ?? null;
                $item['numero_serie'] = $item['numero_serie'] ?? null;
                if ($comissaoFuncionarioItem) {
                    $item['vendedor_id'] = $item['vendedor_id'] ?? '';
                    if ($colunaComissaoExiste) {
                        $item['comissao'] = $item['comissao'] ?? 0;
                    }
                }
                if ($colunaProfissionalIdExiste) {
                    $item['profissional_id'] = $item['profissional_id'] ?? '';
                }
            }
            unset($item);

            // Log para debug
            error_log("Editando {$this->moduleSingular()} ID: {$id}, Company ID: {$companyId}, Itens encontrados: " . count($itens));
            if (!empty($itens)) {
                error_log("Primeiro item: " . json_encode($itens[0]));
            }

            // Buscar empresa_id selecionada da venda (usa company_id)
            $empresaSelecionadaId = $venda['company_id'] ?? null;

            error_log("Preparando dados para a view...");
            $viewData = [
                'venda' => $venda,
                'itens' => $itens,
                'clientes' => $clientes,
                'produtos' => $produtos,
                'usarLoteVenda' => $usarLoteVenda,
                'metodosPagamento' => $metodosPagamento,
                'vendedores' => $vendedores,
                'statusVendas' => $statusVendas,
                'comissaoFuncionarioItem' => $comissaoFuncionarioItem,
                'profissionaisItem' => $profissionaisItem,
                'empresasVinculadas' => $empresasVinculadas,
                'empresaSelecionadaId' => $empresaSelecionadaId,
                'pageTitle' => 'Editar ' . $this->moduleSingular() . ' #' . $venda['sale_number'],
                'activeMenu' => $this->moduleSlug()
            ];

            error_log("Chamando view com " . count($viewData) . " parâmetros");
            $this->view($this->moduleView('edit'), $this->withModuleViewData($viewData));
        } catch (Exception $e) {
            error_log("Erro ao editar {$this->moduleSingular()}: " . $e->getMessage());
            error_log("Stack trace: " . $e->getTraceAsString());
            $this->error('Erro ao carregar formulário: ' . $e->getMessage());
        }
    }

    public function show(): void
    {
        try {
            $id = (int) $this->request->get('id');
            $venda = $this->getVenda($id, $this->moduleOrigin());

            if (!$venda) {
                $this->response->notFound($this->moduleSingular() . ' não encontrada');
                return;
            }

            // Buscar itens da venda
            $stmt = $this->db->prepare("
                SELECT * FROM vendas_itens
                WHERE venda_id = :venda_id
                ORDER BY id ASC
            ");
            $stmt->execute(['venda_id' => $id]);
            $itens = $stmt->fetchAll();

            $this->view($this->moduleView('show'), $this->withModuleViewData([
                'venda' => $venda,
                'itens' => $itens,
                'pageTitle' => $this->moduleSingular() . ' #' . $venda['sale_number'],
                'activeMenu' => $this->moduleSlug()
            ]));
        } catch (Exception $e) {
            error_log("Erro ao visualizar {$this->moduleSingular()}: " . $e->getMessage());
            $this->error('Erro ao carregar ' . mb_strtolower($this->moduleSingular()) . ': ' . $e->getMessage());
        }
    }

    public function print(): void
    {
        try {
            $id = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            $venda = $this->getVenda($id, $this->moduleOrigin());

            if (!$venda) {
                echo $this->moduleSingular() . " não encontrada";
                return;
            }

            // Buscar dados da empresa usando JOIN com a venda
            error_log("Print: Buscando empresa da venda ID = {$id}");

            $stmt = $this->db->prepare("
                SELECT empresas.*
                FROM empresas
                LEFT JOIN vendas ON vendas.company_id = empresas.id
                WHERE vendas.id = :venda_id
                LIMIT 1
            ");
            $stmt->execute(['venda_id' => $id]);
            $empresa = $stmt->fetch();

            error_log("Print: Empresa encontrada = " . ($empresa ? ($empresa['razao_social'] ?? $empresa['nome'] ?? 'SIM') : 'NÃO'));

            if ($empresa) {
                error_log("Print: Nome da empresa = " . ($empresa['razao_social'] ?? $empresa['nome'] ?? 'não identificado'));
                error_log("Print: CNPJ da empresa = " . ($empresa['cnpj'] ?? 'não informado'));
            }

            // Buscar itens da venda
            $stmt = $this->db->prepare("
                SELECT * FROM vendas_itens
                WHERE venda_id = :venda_id
                ORDER BY id ASC
            ");
            $stmt->execute(['venda_id' => $id]);
            $itens = $stmt->fetchAll();

            // Buscar dados do cliente
            $cliente = null;
            if (!empty($venda['customer_id'])) {
                $stmt = $this->db->prepare("SELECT * FROM pessoas WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['customer_id']]);
                $cliente = $stmt->fetch();
            }

            // Buscar método de pagamento
            $metodoPagamento = null;
            if (!empty($venda['payment_method_id'])) {
                $stmt = $this->db->prepare("SELECT name FROM metodos_pagamento WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['payment_method_id']]);
                $metodoPagamento = $stmt->fetch();
            }

            // Buscar vendedor
            $vendedor = null;
            if (!empty($venda['vendor_id'])) {
                $stmt = $this->db->prepare("SELECT name FROM users WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['vendor_id']]);
                $vendedor = $stmt->fetch();
            }

            // Renderizar view de impressão
            $this->view($this->moduleView('print'), $this->withModuleViewData([
                'venda' => $venda,
                'itens' => $itens,
                'empresa' => $empresa,
                'cliente' => $cliente,
                'metodoPagamento' => $metodoPagamento,
                'vendedor' => $vendedor
            ]));
        } catch (Exception $e) {
            error_log("Erro ao imprimir {$this->moduleSingular()}: " . $e->getMessage());
            echo "Erro ao gerar impressão: " . $e->getMessage();
        }
    }

    public function romaneioSeparacao(): void
    {
        try {
            $id = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            $venda = $this->getVenda($id, $this->moduleOrigin());

            if (!$venda) {
                echo $this->moduleSingular() . " não encontrada";
                return;
            }

            // Buscar dados da empresa usando JOIN com a venda
            $stmt = $this->db->prepare("
                SELECT empresas.*
                FROM empresas
                LEFT JOIN vendas ON vendas.company_id = empresas.id
                WHERE vendas.id = :venda_id
                LIMIT 1
            ");
            $stmt->execute(['venda_id' => $id]);
            $empresa = $stmt->fetch();

            // Verificar se coluna item_conferido existe
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'item_conferido'");
            $colunaConferidoExiste = $stmtCheck->fetch() !== false;

            // Buscar itens da venda com endereço de estoque, lote e validade
            if ($colunaConferidoExiste) {
                $query = "
                    SELECT
                        vi.*,
                        COALESCE(vi.item_conferido, '') as item_conferido,
                        pr.stock_address as produto_stock_address,
                        pr.stock_exit_location as produto_stock_exit_location,
                        le.name as local_estoque_nome
                    FROM vendas_itens vi
                    LEFT JOIN produtos pr ON vi.product_id = pr.id
                    LEFT JOIN locais_estoque le ON pr.stock_exit_location = le.id
                    WHERE vi.venda_id = :venda_id
                    ORDER BY vi.id ASC
                ";
            } else {
                $query = "
                    SELECT
                        vi.*,
                        'Não' as item_conferido,
                        pr.stock_address as produto_stock_address,
                        pr.stock_exit_location as produto_stock_exit_location,
                        le.name as local_estoque_nome
                    FROM vendas_itens vi
                    LEFT JOIN produtos pr ON vi.product_id = pr.id
                    LEFT JOIN locais_estoque le ON pr.stock_exit_location = le.id
                    WHERE vi.venda_id = :venda_id
                    ORDER BY vi.id ASC
                ";
            }

            $stmt = $this->db->prepare($query);
            $stmt->execute(['venda_id' => $id]);
            $itens = $stmt->fetchAll(\PDO::FETCH_ASSOC);

            // Mapear dados do estoque para formato "Local - Armazenagem"
            foreach ($itens as &$item) {
                // Garantir que usamos o stock_address do produto
                $stockAddress = '';
                if (isset($item['produto_stock_address'])) {
                    $stockAddress = trim($item['produto_stock_address']);
                }

                // Buscar nome do local de estoque
                $localEstoqueNome = $item['local_estoque_nome'] ?? '';

                // Montar endereço completo no formato "Local - Armazenagem"
                if (!empty($localEstoqueNome) && !empty($stockAddress)) {
                    $item['stock_address'] = $localEstoqueNome . ' - ' . $stockAddress;
                } elseif (!empty($localEstoqueNome)) {
                    $item['stock_address'] = $localEstoqueNome;
                } elseif (!empty($stockAddress)) {
                    $item['stock_address'] = $stockAddress;
                } else {
                    $item['stock_address'] = '';
                }

                // Remover aliases temporários
                unset($item['produto_stock_address'], $item['produto_stock_exit_location'], $item['local_estoque_nome']);
            }
            unset($item);

            // Buscar dados do cliente
            $cliente = null;
            if (!empty($venda['customer_id'])) {
                $stmt = $this->db->prepare("SELECT * FROM pessoas WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['customer_id']]);
                $cliente = $stmt->fetch();
            }

            // Buscar vendedor
            $vendedor = null;
            if (!empty($venda['vendor_id'])) {
                $stmt = $this->db->prepare("SELECT name FROM users WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['vendor_id']]);
                $vendedor = $stmt->fetch();
            }

            // Renderizar view de romaneio
            $this->view($this->moduleView('romaneio-separacao'), $this->withModuleViewData([
                'venda' => $venda,
                'itens' => $itens,
                'empresa' => $empresa,
                'cliente' => $cliente,
                'vendedor' => $vendedor
            ]));
        } catch (Exception $e) {
            error_log("Erro ao imprimir romaneio de separação: " . $e->getMessage());
            echo "Erro ao gerar romaneio: " . $e->getMessage();
        }
    }

    public function buscarItemConferencia(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $vendaId = (int) $this->request->post('venda_id');
            $codigoBarras = trim($this->request->post('codigo_barras', ''));

            if (!$vendaId || !$codigoBarras) {
                $this->response->json(['success' => false, 'message' => 'Venda ID e código de barras são obrigatórios']);
                return;
            }

            // Buscar item da venda pelo código de barras/SKU do produto
            $stmt = $this->db->prepare("
                SELECT vi.*, pr.barcode, pr.sku, pr.name as product_name
                FROM vendas_itens vi
                LEFT JOIN produtos pr ON vi.product_id = pr.id
                WHERE vi.venda_id = :venda_id
                  AND (pr.barcode = :codigo_barras1 OR pr.sku = :codigo_barras2)
                  AND vi.company_id = :company_id
                LIMIT 1
            ");
            $stmt->execute([
                'venda_id' => $vendaId,
                'codigo_barras1' => $codigoBarras,
                'codigo_barras2' => $codigoBarras,
                'company_id' => $companyId
            ]);
            $item = $stmt->fetch(\PDO::FETCH_ASSOC);

            if (!$item) {
                $this->response->json(['success' => false, 'message' => 'Produto não encontrado nesta venda']);
                return;
            }

            // Verificar se já foi conferido
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'item_conferido'");
            $colunaExiste = $stmtCheck->fetch() !== false;

            if ($colunaExiste && !empty($item['item_conferido']) && $item['item_conferido'] === 'Sim') {
                $this->response->json(['success' => false, 'message' => 'Item já foi conferido anteriormente']);
                return;
            }

            $this->response->json([
                'success' => true,
                'item' => $item
            ]);
        } catch (Exception $e) {
            error_log("Erro ao buscar item para conferência: " . $e->getMessage());
            $this->response->json(['success' => false, 'message' => 'Erro ao buscar item: ' . $e->getMessage()]);
        }
    }

    public function conferirItemRomaneio(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $vendaId = (int) $this->request->post('venda_id');
            $itemId = (int) $this->request->post('item_id');
            $quantidadeInformada = (float) $this->request->post('quantidade', 0);
            $loteInformado = trim($this->request->post('lote', ''));

            if (!$vendaId || !$itemId) {
                $this->response->json(['success' => false, 'message' => 'Venda ID e Item ID são obrigatórios']);
                return;
            }

            // Buscar item da venda
            $stmt = $this->db->prepare("
                SELECT vi.*, pr.stock_address as produto_stock_address,
                    pr.name as product_name, pr.sku as product_sku
                FROM vendas_itens vi
                LEFT JOIN produtos pr ON vi.product_id = pr.id
                WHERE vi.id = :item_id
                  AND vi.venda_id = :venda_id
                  AND vi.company_id = :company_id
                LIMIT 1
            ");
            $stmt->execute([
                'item_id' => $itemId,
                'venda_id' => $vendaId,
                'company_id' => $companyId
            ]);
            $item = $stmt->fetch(\PDO::FETCH_ASSOC);

            if (!$item) {
                $this->response->json(['success' => false, 'message' => 'Item não encontrado']);
                return;
            }

            // Verificar se já foi conferido
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'item_conferido'");
            $colunaExiste = $stmtCheck->fetch() !== false;

            if ($colunaExiste && !empty($item['item_conferido']) && $item['item_conferido'] === 'Sim') {
                $this->response->json(['success' => false, 'message' => 'Item já foi conferido anteriormente']);
                return;
            }

            // Validar quantidade
            $quantidadePedido = (float) ($item['quantity'] ?? 0);
            if ($quantidadeInformada != $quantidadePedido) {
                $this->response->json([
                    'success' => false,
                    'message' => "Quantidade informada ({$quantidadeInformada}) não confere com a do pedido ({$quantidadePedido})"
                ]);
                return;
            }

            // Validar lote (se o pedido tiver lote)
            $lotePedido = trim($item['lote'] ?? '');
            if (!empty($lotePedido)) {
                if ($loteInformado !== $lotePedido) {
                    $this->response->json([
                        'success' => false,
                        'message' => "Lote informado ({$loteInformado}) não confere com o do pedido ({$lotePedido})"
                    ]);
                    return;
                }
            }

            // Se chegou aqui, quantidade e lote estão corretos - conferir item
            if ($colunaExiste) {
                $stmt = $this->db->prepare("
                    UPDATE vendas_itens
                    SET item_conferido = 'Sim'
                    WHERE id = :item_id AND company_id = :company_id
                ");
                $stmt->execute([
                    'item_id' => $itemId,
                    'company_id' => $companyId
                ]);
            }

            // Buscar dados atualizados do item
            $stmt = $this->db->prepare("
                SELECT vi.*,
                    pr.stock_address as produto_stock_address,
                    pr.name as product_name,
                    pr.sku as product_sku
                FROM vendas_itens vi
                LEFT JOIN produtos pr ON vi.product_id = pr.id
                WHERE vi.id = :item_id
            ");
            $stmt->execute(['item_id' => $itemId]);
            $itemAtualizado = $stmt->fetch(\PDO::FETCH_ASSOC);

            if (isset($itemAtualizado['produto_stock_address'])) {
                $itemAtualizado['stock_address'] = trim($itemAtualizado['produto_stock_address']);
            }

            $this->response->json([
                'success' => true,
                'message' => 'Item conferido com sucesso',
                'item' => $itemAtualizado
            ]);
        } catch (Exception $e) {
            error_log("Erro ao conferir item do romaneio: " . $e->getMessage());
            $this->response->json(['success' => false, 'message' => 'Erro ao conferir item: ' . $e->getMessage()]);
        }
    }

    public function enviarEmail(): void
    {
        try {
            $vendaId = (int) $this->request->post('venda_id');
            $email = $this->request->post('email');
            $assunto = $this->request->post('assunto');
            $mensagem = $this->request->post('mensagem', '');
            $anexarPdf = (bool) $this->request->post('anexar_pdf', true);

            if (!$vendaId || !$email) {
                $this->json([
                    'success' => false,
                    'message' => 'Venda ID e email são obrigatórios'
                ]);
                return;
            }

            $companyId = $this->getCompanyId();
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());

            if (!$venda) {
                $this->json([
                    'success' => false,
                    'message' => $this->moduleSingular() . ' não encontrada'
                ]);
                return;
            }

            // Buscar dados da empresa
            $stmt = $this->db->prepare("
                SELECT empresas.*
                FROM empresas
                LEFT JOIN vendas ON vendas.company_id = empresas.id
                WHERE vendas.id = :venda_id
                LIMIT 1
            ");
            $stmt->execute(['venda_id' => $vendaId]);
            $empresa = $stmt->fetch();

            // Buscar itens da venda
            $stmt = $this->db->prepare("
                SELECT * FROM vendas_itens
                WHERE venda_id = :venda_id
                ORDER BY id ASC
            ");
            $stmt->execute(['venda_id' => $vendaId]);
            $itens = $stmt->fetchAll();

            // Buscar dados do cliente
            $cliente = null;
            if (!empty($venda['customer_id'])) {
                $stmt = $this->db->prepare("SELECT * FROM pessoas WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['customer_id']]);
                $cliente = $stmt->fetch();
            }

            // Buscar método de pagamento
            $metodoPagamento = null;
            if (!empty($venda['payment_method_id'])) {
                $stmt = $this->db->prepare("SELECT name FROM metodos_pagamento WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['payment_method_id']]);
                $metodoPagamento = $stmt->fetch();
            }

            // Buscar vendedor
            $vendedor = null;
            if (!empty($venda['vendor_id'])) {
                $stmt = $this->db->prepare("SELECT name FROM users WHERE id = :id LIMIT 1");
                $stmt->execute(['id' => $venda['vendor_id']]);
                $vendedor = $stmt->fetch();
            }

            // Gerar HTML do pedido
            ob_start();
            include \ROOT_PATH . '/views/vendas/print.php';
            $html = ob_get_clean();

            $pdfPath = null;

            // Gerar PDF se solicitado
            if ($anexarPdf) {
                require_once \ROOT_PATH . '/vendor/autoload.php';

                $mpdf = new \Mpdf\Mpdf([
                    'mode' => 'utf-8',
                    'format' => 'A4',
                    'margin_left' => 10,
                    'margin_right' => 10,
                    'margin_top' => 10,
                    'margin_bottom' => 10
                ]);

                $mpdf->WriteHTML($html);

                // Salvar PDF temporário
                $pdfDir = \ROOT_PATH . '/storage/temp';
                if (!is_dir($pdfDir)) {
                    mkdir($pdfDir, 0755, true);
                }

                $pdfFilename = 'pedido_' . $venda['sale_number'] . '_' . time() . '.pdf';
                $pdfPath = $pdfDir . '/' . $pdfFilename;
                $mpdf->Output($pdfPath, \Mpdf\Output\Destination::FILE);
            }

            // Configurar e enviar email
            $mail = new \PHPMailer\PHPMailer\PHPMailer(true);

            // Configuração SMTP (buscar do banco de dados ou arquivo de config)
            $mail->isSMTP();
            $mail->Host = getenv('MAIL_HOST') ?: 'smtp.gmail.com';
            $mail->SMTPAuth = true;
            $mail->Username = getenv('MAIL_USERNAME') ?: '';
            $mail->Password = getenv('MAIL_PASSWORD') ?: '';
            $mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
            $mail->Port = getenv('MAIL_PORT') ?: 587;
            $mail->CharSet = 'UTF-8';

            // Remetente
            $nomeEmpresa = $empresa['razao_social'] ?? $empresa['nome'] ?? $empresa['name'] ?? 'Systhema';
            $emailEmpresa = $empresa['email'] ?? getenv('MAIL_FROM') ?: 'noreply@Systhema.com.br';
            $mail->setFrom($emailEmpresa, $nomeEmpresa);

            // Destinatário
            $mail->addAddress($email);

            // Conteúdo
            $mail->isHTML(true);
            $mail->Subject = $assunto ?: "{$this->moduleSingular()} {$venda['sale_number']}";

            $htmlBody = "<html><body>";
            $htmlBody .= "<p>Olá,</p>";
            if ($mensagem) {
                $htmlBody .= "<p>" . nl2br(htmlspecialchars($mensagem)) . "</p>";
            }
            $htmlBody .= "<p>Segue em anexo o pedido <strong>{$venda['sale_number']}</strong>.</p>";
            $htmlBody .= "<p>Atenciosamente,<br>{$nomeEmpresa}</p>";
            $htmlBody .= "</body></html>";

            $mail->Body = $htmlBody;
            $mail->AltBody = strip_tags(str_replace('<br>', "\n", $htmlBody));

            // Anexar PDF
            if ($anexarPdf && $pdfPath && file_exists($pdfPath)) {
                $mail->addAttachment($pdfPath, $pdfFilename);
            }

            // Enviar
            $mail->send();

            // Remover PDF temporário
            if ($pdfPath && file_exists($pdfPath)) {
                unlink($pdfPath);
            }

            $this->json([
                'success' => true,
                'message' => 'Email enviado com sucesso!'
            ]);
        } catch (\PHPMailer\PHPMailer\Exception $e) {
            error_log("Erro PHPMailer: " . $e->getMessage());
            $this->json([
                'success' => false,
                'message' => 'Erro ao enviar email: ' . $e->getMessage()
            ]);
        } catch (Exception $e) {
            error_log("Erro ao enviar email: " . $e->getMessage());
            $this->json([
                'success' => false,
                'message' => 'Erro ao enviar email: ' . $e->getMessage()
            ]);
        }
    }

    public function update(): void
    {
        // Verificar permissão de edição
        if (!$this->canEdit('vendas')) {
            $this->response->forbidden('Você não tem permissão para editar vendas.');
            return;
        }

        try {
            error_log("=== INÍCIO UPDATE {$this->moduleSingular()} ===");
            error_log("POST recebido: " . json_encode($_POST));

            $id = (int) $this->request->post('id');
            error_log("ID da {$this->moduleSingular()}: {$id}");

            if (!$id) {
                error_log("ERRO: ID não informado");
                $this->error('ID da ' . mb_strtolower($this->moduleSingular()) . ' não informado');
                return;
            }

            $venda = $this->getVenda($id, $this->moduleOrigin());
            error_log($this->moduleSingular() . " encontrada: " . ($venda ? 'SIM' : 'NÃO'));

            if (!$venda) {
                error_log("ERRO: {$this->moduleSingular()} não encontrada para ID: {$id}");
                $this->error($this->moduleSingular() . ' não encontrada');
                return;
            }

            // Proteção: Não permitir editar venda com NF-e emitida
            if (!empty($venda['chave_nfe']) && !empty($venda['protocolo_nfe']) && !empty($venda['numero_nfe'])) {
                error_log("ERRO: Tentativa de editar {$this->moduleSingular()} #{$id} com NF-e emitida");
                $this->error('Não é possível editar uma venda com NF-e emitida. Use a Carta de Correção se necessário fazer alterações.');
                return;
            }

            $companyId = $this->getCompanyId();
            error_log("Company ID: {$companyId}");

            if (!$companyId) {
                error_log("ERRO: Company ID não encontrado");
                $this->error('Company ID não encontrado');
                return;
            }

            $generateReceivablesPost = $this->request->post('generate_receivables');
            $data = [
                'customer_type' => $this->request->post('customer_type') ?: 'pessoa',
                'customer_id' => $this->request->post('customer_id') ?: null,
                'customer_name' => $this->request->post('customer_name'),
                'customer_document' => $this->request->post('customer_document'),
                'sale_number' => $this->request->post('sale_number') ?: $venda['sale_number'], // Fallback para venda existente
                'sale_date' => $this->request->post('sale_date') ?: date('Y-m-d'),
                'payment_method_id' => $this->request->post('payment_method_id') ?: null,
                'payment_term_id' => $this->request->post('payment_term_id') ?: null,
                'installments' => $this->request->post('installments') ?: 1,
                'subtotal' => $this->request->post('subtotal') ?: 0.00,
                'discount' => $this->request->post('discount') ?: 0.00,
                'discount_type' => $this->request->post('discount_type') ?: 'fixed',
                'additions' => $this->request->post('additions') ?: 0.00,
                'shipping' => $this->request->post('shipping') ?: 0.00,
                'total' => $this->request->post('total') ?: 0.00,
                'status' => $this->request->post('status') ?: ($venda['status'] ?? 'pendente'), // Fallback para status atual
                'notes' => $this->request->post('notes'),
                'observacoes_nfe' => $this->request->post('observacoes_nfe'),
                'vendor_id' => !empty($this->request->post('vendor_id')) && $this->request->post('vendor_id') !== '' ? (int) $this->request->post('vendor_id') : null,
                'auxiliary_vendor_id' => !empty($this->request->post('auxiliary_vendor_id')) && $this->request->post('auxiliary_vendor_id') !== '' ? (int) $this->request->post('auxiliary_vendor_id') : null,
                'id' => $id,
                'company_id' => $companyId
            ];

            error_log("Dados processados: " . json_encode([
                'sale_number' => $data['sale_number'] ?? 'NULL',
                'status' => $data['status'] ?? 'NULL',
                'total' => $data['total'] ?? 0,
                'payment_method_id' => $data['payment_method_id'] ?? 'NULL',
                'installments' => $data['installments'] ?? 1,
                'vendor_id' => $data['vendor_id'] ?? 'NULL',
                'auxiliary_vendor_id' => $data['auxiliary_vendor_id'] ?? 'NULL'
            ]));

            error_log("POST vendor_id: " . ($this->request->post('vendor_id') ?? 'NULL'));
            error_log("POST auxiliary_vendor_id: " . ($this->request->post('auxiliary_vendor_id') ?? 'NULL'));

            // Validar que vendedor principal e auxiliar não sejam a mesma pessoa
            if (!empty($data['vendor_id']) && !empty($data['auxiliary_vendor_id'])) {
                if ((int) $data['vendor_id'] === (int) $data['auxiliary_vendor_id']) {
                    error_log("ERRO: Vendedor principal e auxiliar são iguais");
                    $this->error('O vendedor principal e o vendedor auxiliar não podem ser a mesma pessoa');
                    return;
                }
            }

            $this->db->beginTransaction();
            error_log("Transação iniciada. Executando UPDATE...");

            // Buscar empresa_id selecionada
            $empresaId = $this->request->post('empresa_id');
            error_log("POST empresa_id: " . ($empresaId ?? 'NULL'));
            if (!empty($empresaId)) {
                // Validar se o usuário tem acesso a essa empresa
                $empresasVinculadas = $this->getEmpresasVinculadasUsuario();
                $empresasIds = array_column($empresasVinculadas, 'id');
                if (!in_array((int) $empresaId, $empresasIds)) {
                    $this->error('Você não tem permissão para realizar vendas para esta empresa');
                    return;
                }
                $data['empresa_id'] = (int) $empresaId;
                error_log("empresa_id adicionado ao data: " . $data['empresa_id']);
            }

            // Preparar array apenas com os campos que serão atualizados no SQL
            $updateData = [
                'id' => $data['id'],
                'company_id' => $data['company_id'],
                'customer_type' => $data['customer_type'],
                'customer_id' => $data['customer_id'],
                'customer_name' => $data['customer_name'],
                'customer_document' => $data['customer_document'],
                'sale_number' => $data['sale_number'],
                'sale_date' => $data['sale_date'],
                'payment_method_id' => $data['payment_method_id'],
                'payment_term_id' => $data['payment_term_id'],
                'installments' => $data['installments'],
                'subtotal' => $data['subtotal'],
                'discount' => $data['discount'],
                'discount_type' => $data['discount_type'],
                'additions' => $data['additions'],
                'shipping' => $data['shipping'],
                'total' => $data['total'],
                'status' => $data['status'],
                'notes' => $data['notes'],
                'observacoes_nfe' => $data['observacoes_nfe'],
                'vendor_id' => $data['vendor_id'],
                'auxiliary_vendor_id' => $data['auxiliary_vendor_id'],
            ];

            // Verificar se a coluna empresa_id existe ANTES de adicionar ao updateData
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas LIKE 'empresa_id'");
            $temColunaEmpresaId = $stmtCheck->rowCount() > 0;
            error_log("Tem coluna empresa_id: " . ($temColunaEmpresaId ? 'sim' : 'não'));

            // Adicionar empresa_id ao updateData se foi enviado e a coluna existe
            if (isset($data['empresa_id']) && $temColunaEmpresaId) {
                $updateData['empresa_id'] = $data['empresa_id'];
                error_log("empresa_id adicionado ao updateData: " . $updateData['empresa_id']);
            }

            $campoEmpresaId = $temColunaEmpresaId && isset($updateData['empresa_id']) ? ', empresa_id = :empresa_id' : '';

            // Log para debug
            error_log("UPDATE Data: " . json_encode($updateData));
            error_log("Campo empresa_id na query: " . ($campoEmpresaId ?: 'não incluído'));

            $sqlQuery = "
                UPDATE vendas SET
                    customer_type = :customer_type,
                    customer_id = :customer_id,
                    customer_name = :customer_name,
                    customer_document = :customer_document,
                    sale_number = :sale_number,
                    sale_date = :sale_date,
                    payment_method_id = :payment_method_id,
                    payment_term_id = :payment_term_id,
                    installments = :installments,
                    subtotal = :subtotal,
                    discount = :discount,
                    discount_type = :discount_type,
                    additions = :additions,
                    shipping = :shipping,
                    total = :total,
                    status = :status,
                    notes = :notes,
                    observacoes_nfe = :observacoes_nfe,
                    vendor_id = :vendor_id,
                    auxiliary_vendor_id = :auxiliary_vendor_id{$campoEmpresaId},
                    updated_at = NOW()
                WHERE id = :id AND company_id = :company_id
            ";

            error_log("SQL Query: " . $sqlQuery);

            $stmt = $this->db->prepare($sqlQuery);
            $stmt->execute($updateData);

            // Remover itens antigos
            $stmt = $this->db->prepare("DELETE FROM vendas_itens WHERE venda_id = :venda_id AND company_id = :company_id");
            $stmt->execute(['venda_id' => $id, 'company_id' => $companyId]);

            // Salvar novos itens
            $itens = $this->request->post('itens') ?? [];
            $statusCodigoUpdate = $data['status'] ?? 'pendente';
            $updateStockFlag = !empty($this->request->post('update_stock'));
            if (!empty($itens)) {
                $this->salvarItens($id, $itens, $statusCodigoUpdate, $updateStockFlag);
            }

            // Atualizar estoque se solicitado
            $updateStock = $this->request->post('update_stock') ? 1 : 0;
            if ($updateStock) {
                error_log("Venda #{$id}: Atualizando estoque conforme solicitado");
                $this->atualizarEstoque($id, $itens);
            }

            // Remover pagamentos antigos e criar novos
            $stmt = $this->db->prepare("DELETE FROM vendas_pagamentos WHERE venda_id = :venda_id AND company_id = :company_id");
            $stmt->execute(['venda_id' => $id, 'company_id' => $companyId]);

            // Salvar novos pagamentos
            $this->salvarPagamentos($id, $data);

            // Verificar se o status da venda gera financeiro
            $statusCodigo = $data['status'] ?? 'pendente';
            error_log("Venda #{$id}: Verificando status '{$statusCodigo}' para geração financeira. Company ID: {$companyId}");

            $stmtStatus = $this->db->prepare("
                SELECT codigo, nome, gera_financeiro, movimenta_estoque FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = 'vendas'
                  AND codigo = :codigo
                  AND ativo = 1
                LIMIT 1
            ");
            $stmtStatus->execute([
                'company_id' => $companyId,
                'codigo' => $statusCodigo
            ]);
            $statusInfo = $stmtStatus->fetch();

            if (!$statusInfo) {
                error_log("Venda #{$id}: AVISO - Status '{$statusCodigo}' não encontrado na tabela modulo_status!");
                // Tentar buscar todos os status disponíveis para debug
                $stmtAll = $this->db->prepare("
                    SELECT codigo, nome, gera_financeiro, movimenta_estoque FROM modulo_status
                    WHERE company_id = :company_id AND modulo = 'vendas' AND ativo = 1
                ");
                $stmtAll->execute(['company_id' => $companyId]);
                $todosStatus = $stmtAll->fetchAll();
                error_log("Venda #{$id}: Status disponíveis: " . json_encode($todosStatus));
            } else {
                error_log("Venda #{$id}: Status encontrado: " . json_encode($statusInfo));
            }

            // GERAR CONTAS A RECEBER se o status permitir
            $deveGerarFinanceiro = false;

            // Verificar se o status gera financeiro (verificação mais robusta)
            if ($statusInfo && isset($statusInfo['gera_financeiro'])) {
                $geraFin = (int) $statusInfo['gera_financeiro'];
                if ($geraFin == 1) {
                    $deveGerarFinanceiro = true;
                    error_log("Venda #{$id}: Status '{$statusCodigo}' ({$statusInfo['nome']}) TEM gera_financeiro = 1");
                } else {
                    error_log("Venda #{$id}: Status '{$statusCodigo}' ({$statusInfo['nome']}) TEM gera_financeiro = 0");
                }
            } else {
                error_log("Venda #{$id}: ⚠️ Status '{$statusCodigo}' NÃO encontrado na tabela modulo_status");
            }

            // Se checkbox marcado, também gera
            if (!empty($data['generate_receivables'])) {
                $deveGerarFinanceiro = true;
            }

            // GERAR AS CONTAS A RECEBER
            if ($deveGerarFinanceiro && !empty($data['payment_method_id']) && !empty($data['total']) && $data['total'] > 0) {
                error_log("Venda #{$id}: ✅ INICIANDO geração de contas a receber - Status: {$statusCodigo}, Total: {$data['total']}, Parcelas: " . ($data['installments'] ?? 1));

                // Garantir que sale_number está nos dados
                if (empty($data['sale_number'])) {
                    $stmtVenda = $this->db->prepare("SELECT sale_number FROM vendas WHERE id = :id AND company_id = :company_id");
                    $stmtVenda->execute(['id' => $id, 'company_id' => $companyId]);
                    $vendaInfo = $stmtVenda->fetch();
                    if ($vendaInfo) {
                        $data['sale_number'] = $vendaInfo['sale_number'];
                        error_log("Venda #{$id}: sale_number recuperado: {$data['sale_number']}");
                    }
                }

                try {
                    $this->gerarContasReceber($id, $data);
                    error_log("Venda #{$id}: ✅ Função gerarContasReceber() concluída sem exceções");

                    // Verificar se realmente foi inserido
                    // Precisar verificar se venda_id existe na tabela contas_receber
                    $stmtCheckCols = $this->db->query("SHOW COLUMNS FROM contas_receber LIKE 'venda_id'");
                    $temVendaIdCheck = $stmtCheckCols->rowCount() > 0;

                    if ($temVendaIdCheck) {
                        $stmtCheck = $this->db->prepare("SELECT COUNT(*) as total FROM contas_receber WHERE venda_id = :venda_id AND company_id = :company_id");
                        $stmtCheck->execute(['venda_id' => $id, 'company_id' => $companyId]);
                    } else {
                        // Usar description LIKE como fallback
                        $saleNumberCheck = $data['sale_number'] ?? 'VND-' . str_pad((string) $id, 6, '0', STR_PAD_LEFT);
                        $descPatternCheck = "#{$saleNumberCheck}%";
                        $stmtCheck = $this->db->prepare("SELECT COUNT(*) as total FROM contas_receber WHERE company_id = :company_id AND description LIKE :desc_pattern");
                        $stmtCheck->execute(['company_id' => $companyId, 'desc_pattern' => $descPatternCheck]);
                    }

                    $verificacao = $stmtCheck->fetch();
                    error_log("Venda #{$id}: 🔍 VERIFICAÇÃO - Total de contas inseridas para esta venda: " . ($verificacao['total'] ?? 0));

                    if (empty($verificacao['total'])) {
                        error_log("Venda #{$id}: ⚠️⚠️⚠️ AVISO - Nenhuma conta foi inserida mesmo após função executar sem erros!");
                    }
                } catch (\Exception $e) {
                    error_log("Venda #{$id}: ❌❌❌ ERRO FATAL na função gerarContasReceber(): " . $e->getMessage());
                    error_log("Venda #{$id}: Stack trace: " . $e->getTraceAsString());
                    // RELANÇAR exceção para que o usuário veja o erro
                    throw new \Exception("Erro ao gerar contas a receber: " . $e->getMessage(), 0, $e);
                }
            } else {
                error_log("Venda #{$id}: ❌ NÃO gerou contas - gera_fin: " . ($deveGerarFinanceiro ? 'SIM' : 'NÃO') . ", payment_method: " . ($data['payment_method_id'] ?? 'NULL') . ", total: " . ($data['total'] ?? 0));
            }

            // Movimentação de estoque já é feita dentro de salvarItens()

            $this->logActivity('update', 'vendas', $id, $data);
            $this->db->commit();

            $this->success($this->moduleSingular() . ' atualizada com sucesso', [
                'redirect' => $this->moduleRouteBase()
            ]);
        } catch (\Exception $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            error_log("=== ERRO AO ATUALIZAR VENDA ===");
            error_log("Mensagem: " . $e->getMessage());
            error_log("Arquivo: " . $e->getFile());
            error_log("Linha: " . $e->getLine());
            error_log("Stack trace: " . $e->getTraceAsString());
            error_log("POST data: " . json_encode($_POST));

            // Garantir que a resposta seja enviada mesmo se houver erro
            http_response_code(400);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'message' => 'Erro ao atualizar venda: ' . $e->getMessage(),
                'errors' => []
            ]);
            exit;
        } catch (\Throwable $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            error_log("=== ERRO FATAL AO ATUALIZAR VENDA ===");
            error_log("Mensagem: " . $e->getMessage());
            error_log("Arquivo: " . $e->getFile());
            error_log("Linha: " . $e->getLine());
            error_log("Stack trace: " . $e->getTraceAsString());

            http_response_code(500);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false,
                'message' => 'Erro fatal ao atualizar venda: ' . $e->getMessage(),
                'errors' => []
            ]);
            exit;
        }
    }

    public function delete(): void
    {
        // Verificar permissão de exclusão
        if (!$this->canDelete('vendas')) {
            $this->response->forbidden('Você não tem permissão para excluir vendas.');
            return;
        }

        try {
            $id = (int) $this->request->post('id');
            $venda = $this->getVenda($id, $this->moduleOrigin());

            if (!$venda) {
                $this->error($this->moduleSingular() . ' não encontrada');
                return;
            }

            // Proteção: Não permitir excluir venda com NF-e emitida
            if (!empty($venda['chave_nfe']) && !empty($venda['protocolo_nfe']) && !empty($venda['numero_nfe'])) {
                $this->error('Não é possível excluir uma venda com NF-e emitida. Cancele a NF-e primeiro, se necessário.');
                return;
            }

            $this->db->beginTransaction();

            $stmt = $this->db->prepare("DELETE FROM vendas WHERE id = :id AND company_id = :company_id AND modulo_origem = :modulo_origem");
            $stmt->execute([
                'id' => $id,
                'company_id' => $this->getCompanyId(),
                'modulo_origem' => $this->moduleOrigin()
            ]);

            $this->logActivity('delete', 'vendas', $id, $venda);
            $this->db->commit();

            $this->success($this->moduleSingular() . ' excluída com sucesso');
        } catch (Exception $e) {
            $this->db->rollBack();
            error_log("Erro ao excluir {$this->moduleSingular()}: " . $e->getMessage());
            $this->error('Erro ao excluir ' . mb_strtolower($this->moduleSingular()));
        }
    }

    private function getVenda(int $id, ?string $origin = null): ?array
    {
        $sql = "SELECT * FROM vendas WHERE id = :id AND company_id = :company_id";
        $params = ['id' => $id, 'company_id' => $this->getCompanyId()];

        if ($origin !== null) {
            // Para o módulo de vendas, aceitar também vendas do PDV
            if ($origin === 'venda') {
                $sql .= " AND modulo_origem IN ('venda', 'pdv')";
            } else {
                $sql .= " AND modulo_origem = :modulo_origem";
                $params['modulo_origem'] = $origin;
            }
        }

        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetch() ?: null;
    }

    public function buscarVendaConferencia(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $numeroPedido = trim($this->request->get('numero', ''));

            if (empty($numeroPedido)) {
                $this->response->json(['success' => false, 'message' => 'Número do pedido é obrigatório']);
                return;
            }

            // Buscar venda por número
            $stmt = $this->db->prepare("
                SELECT v.*,
                    COALESCE(p.name, v.customer_name, 'Consumidor Final') as customer_name,
                    p.name as pessoa_name,
                    p.trade_name as pessoa_trade_name,
                    COALESCE(p.document, v.customer_document) as customer_document,
                    mp.name as payment_method_name,
                    u.name as vendor_name
                FROM vendas v
                LEFT JOIN pessoas p ON v.customer_id = p.id
                LEFT JOIN metodos_pagamento mp ON v.payment_method_id = mp.id
                LEFT JOIN users u ON v.vendor_id = u.id
                WHERE v.company_id = :company_id
                  AND v.sale_number = :numero
                LIMIT 1
            ");
            $stmt->execute([
                'company_id' => $companyId,
                'numero' => $numeroPedido
            ]);
            $venda = $stmt->fetch();

            if (!$venda) {
                $this->response->json(['success' => false, 'message' => 'Venda não encontrada']);
                return;
            }

            // Verificar se coluna item_conferido existe
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'item_conferido'");
            $colunaConferidoExiste = $stmtCheck->fetch() !== false;

            // Buscar itens da venda
            if ($colunaConferidoExiste) {
                $query = "
                    SELECT vi.*,
                        COALESCE(vi.item_conferido, '') as item_conferido,
                        pr.name as product_name,
                        pr.sku as product_sku,
                        pr.unit as product_unit
                    FROM vendas_itens vi
                    LEFT JOIN produtos pr ON vi.product_id = pr.id
                    WHERE vi.venda_id = :venda_id
                      AND vi.company_id = :company_id
                    ORDER BY vi.id ASC
                ";
            } else {
                $query = "
                    SELECT vi.*,
                        'Não' as item_conferido,
                        pr.name as product_name,
                        pr.sku as product_sku,
                        pr.unit as product_unit
                    FROM vendas_itens vi
                    LEFT JOIN produtos pr ON vi.product_id = pr.id
                    WHERE vi.venda_id = :venda_id
                      AND vi.company_id = :company_id
                    ORDER BY vi.id ASC
                ";
            }

            $stmt = $this->db->prepare($query);
            $stmt->execute([
                'venda_id' => $venda['id'],
                'company_id' => $companyId
            ]);
            $itens = $stmt->fetchAll();

            // Verificar se usa lote
            $usarLote = false;
            try {
                $stmtLote = $this->db->prepare("
                    SELECT valor
                    FROM parametros
                    WHERE empresa_id = :empresa_id
                      AND chave = 'usar_lote_venda'
                    LIMIT 1
                ");
                $stmtLote->execute(['empresa_id' => $companyId]);
                $valor = $stmtLote->fetchColumn();
                $usarLote = ($valor !== false && (int) $valor === 1);
            } catch (\Throwable $e) {
                error_log('[Vendas] Erro ao verificar parâmetro usar_lote_venda: ' . $e->getMessage());
                $usarLote = false;
            }

            $this->response->json([
                'success' => true,
                'venda' => $venda,
                'itens' => $itens,
                'usarLote' => $usarLote
            ]);
        } catch (Exception $e) {
            error_log("Erro ao buscar venda para conferência: " . $e->getMessage());
            $this->response->json(['success' => false, 'message' => 'Erro ao buscar venda: ' . $e->getMessage()]);
        }
    }

    public function salvarConferencia(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $vendaId = (int) $this->request->post('venda_id');
            $itensConferidos = json_decode($this->request->post('itens', '[]'), true);

            if (!$vendaId || empty($itensConferidos)) {
                $this->response->json(['success' => false, 'message' => 'Dados inválidos']);
                return;
            }

            // Verificar se venda existe e pertence à empresa
            $stmt = $this->db->prepare("
                SELECT id FROM vendas
                WHERE id = :id AND company_id = :company_id
            ");
            $stmt->execute(['id' => $vendaId, 'company_id' => $companyId]);
            if (!$stmt->fetch()) {
                $this->response->json(['success' => false, 'message' => 'Venda não encontrada']);
                return;
            }

            $this->db->beginTransaction();

            // Deletar conferência anterior se existir
            $stmt = $this->db->prepare("DELETE FROM vendas_conferencia WHERE venda_id = :venda_id");
            $stmt->execute(['venda_id' => $vendaId]);

            // Inserir nova conferência
            $stmt = $this->db->prepare("
                INSERT INTO vendas_conferencia (venda_id, company_id, user_id, itens, created_at)
                VALUES (:venda_id, :company_id, :user_id, :itens, NOW())
            ");
            $stmt->execute([
                'venda_id' => $vendaId,
                'company_id' => $companyId,
                'user_id' => $this->getUserId(),
                'itens' => json_encode($itensConferidos)
            ]);

            $this->db->commit();

            $this->response->json(['success' => true, 'message' => 'Conferência salva com sucesso']);
        } catch (Exception $e) {
            $this->db->rollBack();
            error_log("Erro ao salvar conferência: " . $e->getMessage());
            $this->response->json(['success' => false, 'message' => 'Erro ao salvar conferência: ' . $e->getMessage()]);
        }
    }

    public function buscarComissaoUsuario(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $userId = (int) $this->request->get('user_id');

            if (!$companyId || $userId <= 0) {
                $this->error('Parâmetros inválidos');
                return;
            }

            $stmt = $this->db->prepare("
                SELECT id, name, COALESCE(comissao, 0) as comissao
                FROM users
                WHERE id = :user_id
                  AND company_id = :company_id
                  AND is_active = 1
                LIMIT 1
            ");
            $stmt->execute([
                'user_id' => $userId,
                'company_id' => $companyId
            ]);
            $user = $stmt->fetch();

            if (!$user) {
                $this->error('Usuário não encontrado');
                return;
            }

            $this->success('Comissão encontrada', [
                'comissao' => floatval($user['comissao'] ?? 0),
                'user_name' => $user['name'] ?? ''
            ]);
        } catch (Exception $e) {
            error_log("Erro ao buscar comissão do usuário: " . $e->getMessage());
            $this->error('Erro ao buscar comissão do usuário');
        }
    }

    public function buscarItens(): void
    {
        try {
            $vendaId = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            error_log("=== BUSCAR ITENS INICIADO ===");
            error_log("venda_id: {$vendaId}");
            error_log("company_id: {$companyId}");

            if (!$vendaId) {
                error_log("ERRO: ID da venda não informado");
                $this->error('ID da venda não informado');
                return;
            }

            // Buscar itens da venda
            $stmt = $this->db->prepare("
                SELECT
                    product_name,
                    quantity,
                    unit_price,
                    total_price,
                    COALESCE(ncm, '0000.00.00') as ncm,
                    COALESCE(cfop, '5102') as cfop,
                    COALESCE(valor_icms, 0) as valor_icms,
                    COALESCE(valor_pis, 0) as valor_pis,
                    COALESCE(valor_cofins, 0) as valor_cofins,
                    COALESCE(valor_ipi, 0) as valor_ipi
                FROM vendas_itens
                WHERE venda_id = :venda_id AND company_id = :company_id
                ORDER BY id ASC
            ");
            $stmt->execute([
                'venda_id' => $vendaId,
                'company_id' => $companyId
            ]);
            $itens = $stmt->fetchAll();

            error_log("Total de itens encontrados: " . count($itens));
            if (count($itens) > 0) {
                error_log("Primeiro item: " . json_encode($itens[0]));
            }
            error_log("=== BUSCAR ITENS FINALIZADO ===");

            $this->success('Itens carregados com sucesso', ['itens' => $itens]);
        } catch (Exception $e) {
            error_log("Erro ao buscar itens da venda: " . $e->getMessage());
            $this->error('Erro ao buscar itens da venda');
        }
    }

    public function emitirNfe(): void
    {
        try {
            error_log("=== EMITIR NFE CHAMADO ===");
            $vendaId = (int) $this->request->post('venda_id');
            error_log("Venda ID: " . $vendaId);
            $this->logEmitirNFe($vendaId, 'Iniciando emissão de NF-e');

            $companyId = $this->getCompanyId();
            $this->logEmitirNFe($vendaId, 'Company ID identificado', ['company_id' => $companyId]);

            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $this->logEmitirNFe($vendaId, 'Venda não encontrada', ['venda_id' => $vendaId]);
                $this->error('Venda não encontrada');
                return;
            }

            // Verificar se a NF-e já foi emitida para esta venda
            if (!empty($venda['chave_nfe']) && !empty($venda['protocolo_nfe'])) {
                $this->logEmitirNFe($vendaId, 'NF-e já existente para a venda', [
                    'chave_nfe' => $venda['chave_nfe'],
                    'protocolo_nfe' => $venda['protocolo_nfe']
                ]);
                $this->error('Esta venda já possui NF-e emitida. Use a função "Reimprimir" ou "Carta de Correção" se necessário.', [
                    'cStat' => '204',
                    'chave_nfe' => $venda['chave_nfe'],
                    'numero_nfe' => $venda['numero_nfe']
                ]);
                return;
            }

            // Buscar itens da venda com dados tributários completos
            // CORREÇÃO: Adicionar DISTINCT para evitar duplicatas de JOINs múltiplos
            $stmt = $this->db->prepare("
                SELECT DISTINCT
                    vi.id,
                    vi.venda_id,
                    vi.product_id,
                    vi.product_name,
                    vi.product_sku,
                    vi.quantity,
                    vi.unit_price,
                    vi.total_price,
                    COALESCE(vi.ncm, '0000.00.00') as ncm,
                    COALESCE(vi.cfop, '5102') as cfop,
                    COALESCE(vi.cst_icms, '00') as cst_icms,
                    COALESCE(vi.aliquota_icms, 0) as aliquota_icms,
                    COALESCE(vi.valor_icms, 0) as valor_icms,
                    COALESCE(vi.base_calculo_icms, 0) as base_calculo_icms,
                    COALESCE(vi.cst_pis, '01') as cst_pis,
                    COALESCE(vi.aliquota_pis, 0) as aliquota_pis,
                    COALESCE(vi.valor_pis, 0) as valor_pis,
                    COALESCE(vi.base_calculo_pis, 0) as base_calculo_pis,
                    COALESCE(vi.cst_cofins, '01') as cst_cofins,
                    COALESCE(vi.aliquota_cofins, 0) as aliquota_cofins,
                    COALESCE(vi.valor_cofins, 0) as valor_cofins,
                    COALESCE(vi.base_calculo_cofins, 0) as base_calculo_cofins,
                    COALESCE(vi.cst_ipi, '99') as cst_ipi,
                    COALESCE(vi.aliquota_ipi, 0) as aliquota_ipi,
                    COALESCE(vi.valor_ipi, 0) as valor_ipi,
                    COALESCE(ic.cclass, '000001') as cclass,
                    COALESCE(ic.aliquota_ibs, 0) as aliquota_ibs,
                    COALESCE(ic.reducao_aliquota_ibs, 0) as reducao_aliquota_ibs,
                    COALESCE(ic.aliquota_cbs, 0) as aliquota_cbs,
                    COALESCE(ic.reducao_aliquota_cbs, 0) as reducao_aliquota_cbs,
                    COALESCE(ic.aliquota_ibs_municipal, 0) as aliquota_ibs_municipal,
                    COALESCE(ic.reducao_aliquota_ibs_municipal, 0) as reducao_aliquota_ibs_municipal,
                    COALESCE(vi.origem, '0') as origem,
                    COALESCE(vi.origem_st, '0') as origem_st,
                    vi.company_id,
                    vi.created_at,
                    COALESCE(vi.origem_st, ic.origem_mercadoria, i.origem_mercadoria, vi.origem, '0') as origem_mercadoria
                FROM vendas_itens vi
                LEFT JOIN produtos p ON vi.product_id = p.id
                LEFT JOIN impostos i ON p.grupo_tributacao_id = i.id AND i.ativo = 'Sim'
                LEFT JOIN impostos_configuracoes ic ON i.hash_imposto = ic.hash_imposto AND ic.company_id = vi.company_id
                WHERE vi.venda_id = :venda_id AND vi.company_id = :company_id
                ORDER BY vi.id ASC
            ");
            $stmt->execute(['venda_id' => $vendaId, 'company_id' => $companyId]);
            $itens = $stmt->fetchAll();

            // Log detalhado para debug
            error_log("NFe #{$vendaId}: Query retornou " . count($itens) . " itens únicos");
            $this->logEmitirNFe($vendaId, 'Itens carregados', ['quantidade_itens' => count($itens)]);
            foreach ($itens as $idx => $item) {
                error_log("NFe #{$vendaId}: Item " . ($idx + 1) . " - ID: {$item['id']}, Produto: {$item['product_name']}, Qtd: {$item['quantity']}");
            }

            // Capturar configurações do formulário
            $configuracoes = [
                'presenca_comprador' => $this->request->post('presenca_comprador') ?: '1',
                'intermediador' => $this->request->post('intermediador') ?: '0',
                'base_pis_cofins_icms' => $this->request->post('base_pis_cofins_icms') === 'true',
                'exibir_pis_cofins_danfe' => $this->request->post('exibir_pis_cofins_danfe') === 'true',
                'subtrair_icms_desonerado' => $this->request->post('subtrair_icms_desonerado') === 'true',
                'exibir_financeiro' => $this->request->post('exibir_financeiro') === 'true',
                'enviar_por_email' => $this->request->post('enviar_por_email') === 'true'
            ];

            // Log das configurações recebidas
            error_log("NFe #{$vendaId}: Configurações recebidas: " . json_encode($configuracoes));
            $this->logEmitirNFe($vendaId, 'Configurações recebidas', $configuracoes);

            // Buscar dados da empresa
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $this->logEmitirNFe($vendaId, 'Dados da empresa não encontrados', ['company_id' => $companyId]);
                $this->error('Dados da empresa não encontrados');
                return;
            }

            // Log dos dados da empresa
            error_log("NFe #{$vendaId}: Empresa encontrada - ID: " . ($empresa['id'] ?? 'N/A') . ", CNPJ: " . ($empresa['cnpj'] ?? 'N/A'));
            error_log("NFe #{$vendaId}: Certificado - Path: " . ($empresa['certificado_path'] ?? 'N/A') . ", Senha: " . (empty($empresa['senha_certificado']) ? 'VAZIA' : 'PREenchida'));
            $this->logEmitirNFe($vendaId, 'Empresa carregada', [
                'empresa_id' => $empresa['id'] ?? null,
                'cnpj' => $empresa['cnpj'] ?? null,
                'certificado_path' => $empresa['certificado_path'] ?? null,
                'certificado_informado' => !empty($empresa['certificado_path'])
            ]);

            // Buscar dados completos do cliente
            $clienteId = isset($venda['customer_id']) && $venda['customer_id'] !== ''
                ? (int) $venda['customer_id']
                : null;
            $cliente = $this->buscarDadosCliente($clienteId);
            $this->logEmitirNFe($vendaId, 'Cliente carregado', [
                'cliente_id' => $clienteId,
                'cliente_nome' => $cliente['name'] ?? null
            ]);

            // Buscar próximo número de NF-e
            $proximoNumeroNfe = $this->buscarProximoNumeroNFe($companyId);

            // Log dos itens encontrados
            error_log("NFe #{$vendaId}: Total de itens encontrados no banco: " . count($itens));
            $this->logEmitirNFe($vendaId, 'Dados consolidados para montagem da NF-e', [
                'total_itens' => count($itens),
                'numero_nfe_sugerido' => $proximoNumeroNfe
            ]);

            // Montar dados no formato da API de NF-e
            $dadosNFe = $this->montarDadosNFeAPI($venda, $itens, $empresa, $cliente, $proximoNumeroNfe, $configuracoes);

            // Log dos itens montados
            error_log("NFe #{$vendaId}: Total de itens montados para API: " . count($dadosNFe['itens'] ?? []));
            $this->logEmitirNFe($vendaId, 'Payload pronto para envio', [
                'total_itens_api' => count($dadosNFe['itens'] ?? []),
                'valor_total' => $dadosNFe['total']['valor_total'] ?? null
            ]);

            // Usar serviço de integração NF-e (wrapper que gerencia dependências)
            $nfeIntegration = new \App\Services\NFeIntegrationService();
            $this->logEmitirNFe($vendaId, 'Chamando serviço de integração NF-e');
            $resultado = $nfeIntegration->emitirNFe($dadosNFe);
            $this->logEmitirNFe($vendaId, 'Resposta do serviço de integração', $resultado);

            if (!$resultado['success']) {
                // Extrair código de status e motivo da rejeição
                $cStat = $resultado['cStat'] ?? '';
                $errorMsg = $resultado['error'] ?? 'Erro ao emitir NF-e';

                $this->logEmitirNFe($vendaId, 'Falha na emissão de NF-e', [
                    'cStat' => $cStat,
                    'error' => $errorMsg,
                    'resultado' => $resultado
                ]);

                // Verificar se é erro de timeout
                if (stripos($errorMsg, 'timeout') !== false || stripos($errorMsg, 'Connection timed out') !== false) {
                    $errorMsg = 'Timeout na comunicação com a SEFAZ. A conexão demorou mais de 180 segundos (3 minutos) para responder. ' .
                        'Possíveis causas: SEFAZ sobrecarregada, problemas de rede ou firewall bloqueando a conexão. ' .
                        'Tente novamente em alguns instantes. Se o problema persistir, verifique a conectividade do servidor com a SEFAZ de PE. ' .
                        'Em caso de persistência, entre em contato com o suporte técnico.';
                }

                // Tentar extrair motivo mais detalhado
                if (preg_match('/\[(\d+)\]:\s*(.+)/', $errorMsg, $matches)) {
                    $cStat = $matches[1];
                    $motivo = $matches[2];
                }

                $this->error($errorMsg, [
                    'cStat' => $cStat,
                    'motivo' => $motivo ?? $errorMsg,
                    'chave_nfe' => $resultado['chave_acesso'] ?? null
                ]);
                return;
            }

            $numeroNfe = $resultado['numero'] ?? null;
            $chaveNfe = $resultado['chave_acesso'] ?? null;
            $protocolo = $resultado['protocolo'] ?? null;
            $pdfPathOriginal = $resultado['pdf_path'] ?? null;
            $xmlPath = $resultado['xml_path'] ?? null;

            // Converter caminho do PDF de /pontti_nfe/ para endpoint correto
            if (!empty($pdfPathOriginal) && !empty($chaveNfe)) {
                // Usar endpoint interno ao invés de caminho direto
                $pdfPath = \App\Helpers\UrlHelper::url('/vendas/visualizar-pdf?chave=' . urlencode($chaveNfe));
            } else {
                $pdfPath = null;
            }

            // Se não veio número no resultado, usar o que foi gerado
            if (empty($numeroNfe)) {
                $numeroNfe = $proximoNumeroNfe;
                error_log("NFe: Número não veio no resultado, usando número gerado: {$numeroNfe}");
            }

            // Salvar dados da NF-e na tabela vendas
            $stmt = $this->db->prepare("
                UPDATE vendas
                SET chave_nfe = :chave_nfe,
                    numero_nfe = :numero_nfe,
                    protocolo_nfe = :protocolo_nfe
                WHERE id = :id AND company_id = :company_id
            ");
            $stmt->execute([
                'chave_nfe' => $chaveNfe,
                'numero_nfe' => (string) $numeroNfe,
                'protocolo_nfe' => $protocolo,
                'id' => $vendaId,
                'company_id' => $companyId
            ]);

            // IMPORTANTE: Atualizar número da NF-e na tabela empresas SEMPRE que emitir com sucesso
            // Isso garante que o próximo número será sempre o correto
            $numeroParaAtualizar = !empty($numeroNfe) ? (int) $numeroNfe : (int) $proximoNumeroNfe;

            $stmt = $this->db->prepare("
                UPDATE empresas
                SET numero_nfe = :numero_nfe
                WHERE id = :company_id
            ");
            $resultadoUpdate = $stmt->execute([
                'numero_nfe' => $numeroParaAtualizar,
                'company_id' => $companyId
            ]);

            if ($resultadoUpdate) {
                error_log("NFe: ✅ Atualizado numero_nfe na tabela empresas para: {$numeroParaAtualizar} (Company ID: {$companyId})");
                $this->logEmitirNFe($vendaId, 'Número da NF-e atualizado na empresa', [
                    'numero_atualizado' => $numeroParaAtualizar,
                    'company_id' => $companyId
                ]);

                // Verificar se foi realmente atualizado
                $stmt = $this->db->prepare("SELECT numero_nfe FROM empresas WHERE id = :company_id");
                $stmt->execute(['company_id' => $companyId]);
                $verificacao = $stmt->fetch();
                $numeroAtualizado = $verificacao['numero_nfe'] ?? 'NULL';
                error_log("NFe: Verificação - numero_nfe na empresa agora é: {$numeroAtualizado}");
                $this->logEmitirNFe($vendaId, 'Verificação após update do numero_nfe', ['numero_verificado' => $numeroAtualizado]);

                if ($numeroAtualizado != $numeroParaAtualizar) {
                    error_log("⚠️ ATENÇÃO: numero_nfe não foi atualizado corretamente! Esperado: {$numeroParaAtualizar}, Atual: {$numeroAtualizado}");
                }
            } else {
                error_log("❌ ERRO: Falha ao atualizar numero_nfe na tabela empresas!");
                $this->logEmitirNFe($vendaId, 'Falha ao atualizar numero_nfe na empresa');
            }

            // Salvar log da emissão (opcional)
            $this->logActivity('emitir_nfe', 'vendas', $vendaId, [
                'configuracoes' => $configuracoes,
                'numero_nfe' => $numeroNfe,
                'chave_nfe' => $chaveNfe,
                'protocolo' => $protocolo
            ]);

            // Se marcou para enviar por email, enviar
            if ($configuracoes['enviar_por_email']) {
                // TODO: Implementar envio de email com XML e DANFE em anexo
                error_log("NFe #{$vendaId}: Enviando NF-e por email para cliente");

                /* Exemplo de implementação:
                $emailCliente = $venda['customer_email'] ?? '';
                if ($emailCliente) {
                    $emailService = new EmailService();
                    $emailService->enviarNFe([
                        'destinatario' => $emailCliente,
                        'numero_nfe' => $numeroNfe,
                        'chave_nfe' => $chaveNfe,
                        'xml' => $xmlAssinado,
                        'pdf' => $pdfDanfe
                    ]);
                }
                */
            }

            $this->logEmitirNFe($vendaId, 'NF-e emitida com sucesso', [
                'numero_nfe' => $numeroNfe,
                'chave_nfe' => $chaveNfe,
                'protocolo' => $protocolo
            ]);

            $this->success('NF-e emitida com sucesso', [
                'numero_nfe' => $numeroNfe,
                'chave_nfe' => $chaveNfe,
                'protocolo' => $protocolo,
                'protocolo_nfe' => $protocolo,
                'pdf_path' => $pdfPath,
                'xml_path' => $xmlPath
            ]);
        } catch (Exception $e) {
            $this->logEmitirNFe($vendaId ?? 0, 'Exceção capturada na emissão de NF-e', [
                'erro' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            error_log("Erro ao emitir NF-e: " . $e->getMessage());
            error_log("Stack trace: " . $e->getTraceAsString());
            $this->error('Erro ao emitir NF-e: ' . $e->getMessage());
        }
    }

    private function logEmitirNFe(int $vendaId, string $mensagem, array $contexto = []): void
    {
        $dataHora = date('Y-m-d H:i:s');
        $arquivo = rtrim(ROOT_PATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'nfe-emissao-' . date('Y-m-d') . '.log';

        $linha = "[{$dataHora}] Venda {$vendaId} | {$mensagem}";
        if (!empty($contexto)) {
            $linha .= ' | ' . json_encode($contexto, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        }
        $linha .= PHP_EOL;

        try {
            $diretorio = dirname($arquivo);
            if (!is_dir($diretorio)) {
                mkdir($diretorio, 0775, true);
            }

            file_put_contents($arquivo, $linha, FILE_APPEND | LOCK_EX);
        } catch (Exception $e) {
            error_log('Falha ao gravar log de emissão de NF-e: ' . $e->getMessage());
        }
    }

    private function buscarDadosEmpresa(int $companyId): ?array
    {
        // Prioriza a empresa vinculada ao usuário atual
        $stmt = $this->db->prepare("\n            SELECT * FROM empresas\n            WHERE id = :id OR company_id = :company_id\n            LIMIT 1\n        ");
        $stmt->execute(['id' => $companyId, 'company_id' => $companyId]);
        $empresa = $stmt->fetch();

        if (!$empresa) {
            // Segunda tentativa: qualquer empresa com certificado configurado
            $stmt = $this->db->prepare("\n                SELECT * FROM empresas\n                WHERE certificado_path IS NOT NULL AND certificado_path != '' AND certificado_path != 'NULL'\n                LIMIT 1\n            ");
            $stmt->execute();
            $empresa = $stmt->fetch();
        }

        if (!$empresa) {
            // Último fallback: tabela companies
            $stmt = $this->db->prepare("\n                SELECT * FROM companies\n                WHERE certificado_path IS NOT NULL AND certificado_path != '' AND certificado_path != 'NULL'\n                LIMIT 1\n            ");
            $stmt->execute();
            $empresa = $stmt->fetch();
        }

        return $empresa ?: null;
    }

    private function buscarDadosCliente(?int $clienteId): ?array
    {
        if (!$clienteId) {
            return null;
        }

        $stmt = $this->db->prepare("
            SELECT * FROM pessoas WHERE id = :id LIMIT 1
        ");
        $stmt->execute(['id' => $clienteId]);
        return $stmt->fetch() ?: null;
    }

    private function buscarProximoNumeroNFe(int $companyId): int
    {
        try {
            // Buscar último número de NF-e da tabela empresas
            $stmt = $this->db->prepare("
                SELECT numero_nfe
                FROM empresas
                WHERE id = :company_id
            ");
            $stmt->execute(['company_id' => $companyId]);
            $result = $stmt->fetch();

            $ultimoNumero = !empty($result['numero_nfe']) ? (int) $result['numero_nfe'] : 0;

            // Se não tiver número ou for 0, começar do 1
            if ($ultimoNumero <= 0) {
                $ultimoNumero = 0;
            }

            $proximoNumero = $ultimoNumero + 1;

            error_log("NFe: Último número da empresa: {$ultimoNumero}, Próximo: {$proximoNumero}");

            return $proximoNumero;
        } catch (Exception $e) {
            // Em caso de erro, iniciar do número 1
            error_log("Erro ao buscar próximo número NF-e: " . $e->getMessage());
            return 1;
        }
    }

    /**
     * Obtém a série da NF-e da empresa
     */
    private function obterSerieNFe(array $empresa): string
    {
        try {
            // Buscar série do array $empresa (que vem do SELECT * de buscarDadosEmpresa)
            // Pode estar em série_nfe ou como número
            $serie = $empresa['serie_nfe'] ?? null;

            if (empty($serie)) {
                // Se não encontrou no array, tentar buscar do banco usando CNPJ ou ID
                $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');
                $empresaId = $empresa['id'] ?? null;

                if ($cnpj) {
                    // Buscar por CNPJ na tabela empresas
                    $stmt = $this->db->prepare("
                        SELECT serie_nfe
                        FROM empresas
                        WHERE cnpj = :cnpj OR REPLACE(REPLACE(REPLACE(cnpj, '.', ''), '/', ''), '-', '') = :cnpj_limpo
                        LIMIT 1
                    ");
                    $stmt->execute(['cnpj' => $cnpj, 'cnpj_limpo' => $cnpj]);
                    $result = $stmt->fetch();
                    $serie = $result['serie_nfe'] ?? null;
                } elseif ($empresaId) {
                    // Buscar por ID
                    $stmt = $this->db->prepare("
                        SELECT serie_nfe
                        FROM empresas
                        WHERE id = :id
                        LIMIT 1
                    ");
                    $stmt->execute(['id' => $empresaId]);
                    $result = $stmt->fetch();
                    $serie = $result['serie_nfe'] ?? null;
                }
            }

            // Manter série exatamente como está na tabela (sem formatação adicional)
            if (!empty($serie)) {
                // Apenas converter para string se necessário e retornar como está
                $serie = trim((string) $serie);
                error_log("NFe: Série obtida da empresa: {$serie}");
            } else {
                // Se não encontrar, usar série padrão '1'
                $serie = '1';
                error_log("NFe: ⚠️ Série não encontrada na tabela empresas, usando padrão '1'");
            }

            return $serie;
        } catch (Exception $e) {
            error_log("NFe: ❌ Erro ao obter série NF-e: " . $e->getMessage());
            // Em caso de erro, retornar série padrão
            return '001';
        }
    }

    private function montarDadosNFeAPI(array $venda, array $itens, array $empresa, ?array $cliente, int $numeroNfe, array $configuracoes): array
    {
        // Montar estrutura no formato que a API de NF-e espera
        $certificadoPath = $empresa['certificado_path'] ?? '';
        $senhaCertificado = $empresa['senha_certificado'] ?? '';

        // Log antes de processar
        error_log("montarDadosNFeAPI: certificado_path original = " . ($certificadoPath ?: 'VAZIO'));

        // Converter caminho relativo para absoluto
        if ($certificadoPath && !empty($certificadoPath)) {
            if (strpos($certificadoPath, '/') === 0) {
                // Já é relativo, converter para absoluto
                $certificadoPath = \ROOT_PATH . $certificadoPath;
            } else {
                // Adicionar \ROOT_PATH se necessário
                $certificadoPath = \ROOT_PATH . '/' . ltrim($certificadoPath, '/');
            }
        }

        // Log depois de processar
        error_log("montarDadosNFeAPI: certificado_path final = " . ($certificadoPath ?: 'VAZIO'));
        error_log("montarDadosNFeAPI: certificado existe? " . (file_exists($certificadoPath) ? 'SIM' : 'NÃO'));

        $cidadeCliente = $cliente['city']
            ?? $cliente['cidade']
            ?? $venda['customer_city']
            ?? $venda['customer_municipio']
            ?? '';
        $estadoCliente = strtoupper($cliente['state']
            ?? $cliente['uf']
            ?? $venda['customer_state']
            ?? $venda['customer_uf']
            ?? 'PE');

        $cepCliente = $cliente
            ? preg_replace('/\D/', '', $cliente['zip_code'] ?? $cliente['cep'] ?? '')
            : preg_replace('/\D/', '', $venda['customer_zip'] ?? $venda['customer_cep'] ?? '');

        // Determinar IE do emitente - buscar de todas as fontes possíveis
        $ieEmitente = '';
        if (!empty($empresa['state_registration'])) {
            $ieEmitente = trim($empresa['state_registration']);
        } elseif (!empty($empresa['inscricao_estadual'])) {
            $ieEmitente = trim($empresa['inscricao_estadual']);
        } elseif (!empty($empresa['rg_ie'])) {
            $ieEmitente = trim($empresa['rg_ie']);
        }

        // Log para debug
        error_log("NFe - IE do emitente encontrada: " . ($ieEmitente ?: 'VAZIA') . " | Empresa ID: " . ($empresa['id'] ?? 'N/A'));

        // Buscar regime tributário da empresa para ajudar na detecção do CRT
        // IMPORTANTE: Buscar de todas as fontes possíveis, incluindo campos NFSe
        $regimeTributario = $empresa['regime_tributario'] ?? $empresa['optante_simples'] ?? $empresa['nfse_regime_tributacao'] ?? null;
        $optanteSimples = $empresa['optante_simples'] ?? $empresa['nfse_optante_simples'] ?? null;

        // Log para debug
        error_log("NFe - Regime tributário encontrado: " . ($regimeTributario ?: 'NULL') . " | Optante Simples: " . ($optanteSimples ?: 'NULL'));
        error_log("NFe - Todos os campos disponíveis: " . json_encode([
            'regime_tributario' => $empresa['regime_tributario'] ?? 'N/A',
            'optante_simples' => $empresa['optante_simples'] ?? 'N/A',
            'nfse_regime_tributacao' => $empresa['nfse_regime_tributacao'] ?? 'N/A',
            'nfse_optante_simples' => $empresa['nfse_optante_simples'] ?? 'N/A'
        ]));

        $dados = [
            'empresa' => [
                'nome' => $empresa['name'] ?? $empresa['razao_social'] ?? '',
                'cnpj' => preg_replace('/\D/', '', $empresa['document'] ?? $empresa['cnpj'] ?? ''),
                'inscricao_estadual' => $ieEmitente, // Pode estar vazia - será tratada no NFeService
                'regime_tributario' => $regimeTributario, // Para ajudar na detecção do CRT
                'optante_simples' => $optanteSimples, // Para ajudar na detecção do CRT
                'estado' => $empresa['state'] ?? $empresa['uf'] ?? 'PE',
                'cidade' => $empresa['city'] ?? $empresa['cidade'] ?? '',
                'endereco' => $empresa['address'] ?? $empresa['endereco'] ?? '',
                'numero' => $empresa['number'] ?? $empresa['numero'] ?? '',
                'bairro' => $empresa['neighborhood'] ?? $empresa['bairro'] ?? '',
                'cep' => preg_replace('/\D/', '', $empresa['zip_code'] ?? $empresa['cep'] ?? ''),
                'email' => $empresa['email'] ?? '',
                'telefone' => $empresa['phone'] ?? $empresa['telefone'] ?? '',
                // Certificado digital
                'certificado_path' => $certificadoPath,
                'certificado_senha' => $senhaCertificado,
                // Logo da empresa
                'logo' => $empresa['logo'] ?? null
            ],
            'cliente' => [
                'nome' => $cliente ? ($cliente['name'] ?? $cliente['trade_name'] ?? '') : ($venda['customer_name'] ?? ''),
                'cnpj' => $cliente ? preg_replace('/\D/', '', $cliente['document'] ?? '') : preg_replace('/\D/', '', $venda['customer_document'] ?? ''),
                // IE do cliente: verificar rg_ie primeiro - se estiver vazio, cliente é não contribuinte e consumidor final
                'inscricao_estadual' => (!empty($cliente['rg_ie']) || !empty($cliente['state_registration']) || !empty($cliente['inscricao_estadual']))
                    ? ($cliente['state_registration'] ?? $cliente['inscricao_estadual'] ?? $cliente['rg_ie'] ?? ($venda['customer_ie'] ?? ''))
                    : '', // Se rg_ie, state_registration e inscricao_estadual estiverem vazios, deixar IE vazia (não contribuinte)
                'endereco' => $cliente['address'] ?? $cliente['endereco'] ?? $venda['customer_address'] ?? '',
                'cidade' => $cidadeCliente,
                'bairro' => $cliente['neighborhood'] ?? $cliente['bairro'] ?? $venda['customer_bairro'] ?? '',
                'estado' => $estadoCliente,
                'cep' => $cepCliente,
                'email' => $cliente['email'] ?? '',
                'telefone' => $cliente['phone'] ?? $cliente['mobile'] ?? $cliente['telefone'] ?? '',
                // Código do município se disponível
                'codigo_municipio' => $cliente['codigo_municipio'] ?? $cliente['city_code'] ?? null,
                'city_code' => $cliente['codigo_municipio'] ?? $cliente['city_code'] ?? null
            ],
            'nfe' => [
                'numero' => (string) $numeroNfe,
                'serie' => $this->obterSerieNFe($empresa),
                'data_emissao' => date('Y-m-d'),
                'data_saida' => date('Y-m-d'),
                'presenca_comprador' => $configuracoes['presenca_comprador'],
                'intermediador' => $configuracoes['intermediador'],
                'observacoes' => $venda['observacoes_nfe'] ?? ''
            ],
            'itens' => []
        ];

        // Montar itens
        foreach ($itens as $item) {
            $dados['itens'][] = [
                'codigo' => $item['product_id'] ?? $item['product_sku'] ?? 'ITEM',
                'descricao' => $item['product_name'] ?? '',
                'ncm' => $item['ncm'] ?? '00000000',
                'cfop' => $item['cfop'] ?? '5102',
                'unidade_comercial' => 'UN',
                'quantidade_comercial' => floatval($item['quantity'] ?? 1),
                'valor_unitario' => floatval($item['unit_price'] ?? 0),
                'valor_total' => floatval($item['total_price'] ?? 0),
                'cst_icms' => $item['cst_icms'] ?? '00',
                'base_calculo_icms' => floatval($item['total_price'] ?? 0),
                'aliquota_icms' => floatval($item['aliquota_icms'] ?? 0),
                'valor_icms' => floatval($item['valor_icms'] ?? 0),
                'origem' => $item['origem_mercadoria'] ?? $item['origem'] ?? '0',
                'cst_pis' => $item['cst_pis'] ?? '01',
                'base_calculo_pis' => floatval($item['total_price'] ?? 0),
                'aliquota_pis' => floatval($item['aliquota_pis'] ?? 0),
                'valor_pis' => floatval($item['valor_pis'] ?? 0),
                'cst_cofins' => $item['cst_cofins'] ?? '01',
                'base_calculo_cofins' => floatval($item['total_price'] ?? 0),
                'aliquota_cofins' => floatval($item['aliquota_cofins'] ?? 0),
                'valor_cofins' => floatval($item['valor_cofins'] ?? 0),
                'cst_ipi' => (!empty($item['cst_ipi']) && $item['cst_ipi'] != '' ? $item['cst_ipi'] : '99'),
                'aliquota_ipi' => floatval($item['aliquota_ipi'] ?? 0),
                'valor_ipi' => floatval($item['valor_ipi'] ?? 0),
                // IBS/CBS
                'cclass' => $item['cclass'] ?? '000001',
                'aliquota_ibs' => floatval($item['aliquota_ibs'] ?? 0),
                'reducao_aliquota_ibs' => floatval($item['reducao_aliquota_ibs'] ?? 0),
                'aliquota_cbs' => floatval($item['aliquota_cbs'] ?? 0),
                'reducao_aliquota_cbs' => floatval($item['reducao_aliquota_cbs'] ?? 0),
                'aliquota_ibs_municipal' => floatval($item['aliquota_ibs_municipal'] ?? 0),
                'reducao_aliquota_ibs_municipal' => floatval($item['reducao_aliquota_ibs_municipal'] ?? 0)
            ];
        }

        return $dados;
    }

    /**
     * Reimprime DANFE da NF-e
     */
    public function reimprimirNfe(): void
    {
        try {
            $vendaId = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            error_log("Reimprimir NF-e: Venda ID = {$vendaId}, Company ID = {$companyId}");

            if (!$vendaId) {
                error_log("Reimprimir NF-e: ERRO - ID da venda não informado");
                $this->error('ID da venda não informado');
                return;
            }

            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                error_log("Reimprimir NF-e: ERRO - Venda {$vendaId} não encontrada");
                $this->error('Venda não encontrada');
                return;
            }

            error_log("Reimprimir NF-e: Venda encontrada - chave_nfe = " . ($venda['chave_nfe'] ?? 'VAZIO'));

            if (empty($venda['chave_nfe'])) {
                error_log("Reimprimir NF-e: ERRO - Venda {$vendaId} não possui chave_nfe");
                $this->error('Esta venda não possui NF-e emitida');
                return;
            }

            // Buscar dados da empresa para pegar CNPJ
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                error_log("Reimprimir NF-e: ERRO - Empresa {$companyId} não encontrada");
                $this->error('Dados da empresa não encontrados');
                return;
            }

            error_log("Reimprimir NF-e: Empresa encontrada - CNPJ = " . ($empresa['cnpj'] ?? $empresa['document'] ?? 'VAZIO'));

            // Buscar caminho do PDF
            $chaveNfe = $venda['chave_nfe'];
            $chaveNfeLimpa = preg_replace('/\D/', '', $chaveNfe);
            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');

            // Extrair ano e mês da chave de acesso NF-e (44 dígitos)
            // Posições 2-3: AA (últimos 2 dígitos do ano)
            // Posições 4-5: MM (mês)
            // Formato: UU AAMM CNPJ...
            if (strlen($chaveNfe) >= 6) {
                $aa = substr($chaveNfe, 2, 2); // Ano (últimos 2 dígitos)
                $mm = substr($chaveNfe, 4, 2); // Mês
                // Converter AA para ano completo (assumindo 2000-2099)
                $ano = '20' . $aa;
                $anoMes = $ano . '_' . $mm;
                error_log("Reimprimir NF-e: Ano/Mês extraído da chave: {$anoMes}");
            } else {
                // Fallback: usar data da venda ou atual
                $anoMes = date('Y_m', strtotime($venda['created_at'] ?? 'now'));
                error_log("Reimprimir NF-e: Ano/Mês usando fallback (created_at): {$anoMes}");
            }

            // Tentar buscar o PDF do módulo NF-e (estrutura padrão do NFeService)
            // Caminho: C:\xampp\htdocs\Systhema\src\Integrations\NFe\arquivos\21497109000156\nfe\pdf\2025_10\emitidos
            $diretorioBase = \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/pdf/' . $anoMes . '/emitidos/';

            // Lista de possíveis nomes de arquivo (com e sem espaço)
            $possiveisNomes = [
                'NFe_' . $venda['chave_nfe'] . '.pdf',
                'NFe_ ' . $venda['chave_nfe'] . '.pdf',  // Com espaço extra
                'NFe_' . $chaveNfeLimpa . '.pdf',
                'NFe_ ' . $chaveNfeLimpa . '.pdf'  // Com espaço extra
            ];

            $pdfPath = null;
            foreach ($possiveisNomes as $nomeArquivo) {
                $caminhoTeste = $diretorioBase . $nomeArquivo;
                error_log("Reimprimir NF-e: Testando: {$caminhoTeste}");
                if (file_exists($caminhoTeste)) {
                    $pdfPath = $caminhoTeste;
                    error_log("Reimprimir NF-e: ✅ PDF encontrado: {$pdfPath}");
                    break;
                }
            }

            // Se não encontrou no diretório com ano/mês, tentar sem
            if (!$pdfPath) {
                $diretorioBase = \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/pdf/emitidos/';
                foreach ($possiveisNomes as $nomeArquivo) {
                    $caminhoTeste = $diretorioBase . $nomeArquivo;
                    error_log("Reimprimir NF-e: Testando (sem ano/mês): {$caminhoTeste}");
                    if (file_exists($caminhoTeste)) {
                        $pdfPath = $caminhoTeste;
                        error_log("Reimprimir NF-e: ✅ PDF encontrado (sem ano/mês): {$pdfPath}");
                        break;
                    }
                }
            }

            // Se ainda não encontrou, tentar no storage
            if (!$pdfPath) {
                $pdfPath = \ROOT_PATH . '/storage/nfe/NFe_' . $venda['chave_nfe'] . '.pdf';
                error_log("Reimprimir NF-e: Testando storage: {$pdfPath}");
                if (!file_exists($pdfPath)) {
                    $pdfPath = null;
                }
            }

            if (!$pdfPath) {
                error_log("Reimprimir NF-e: ERRO - PDF não encontrado em nenhum dos caminhos testados");
                error_log("Reimprimir NF-e: Chave NF-e: {$venda['chave_nfe']}");
                error_log("Reimprimir NF-e: CNPJ: {$cnpj}");
                error_log("Reimprimir NF-e: Ano/Mês: {$anoMes}");
                error_log("Reimprimir NF-e: Diretório testado: {$diretorioBase}");

                // Mesmo que o PDF não exista fisicamente, tentar usar o endpoint de visualização
                // que pode buscar de outros locais
                $pdfPathRel = \App\Helpers\UrlHelper::url('/vendas/visualizar-pdf?chave=' . urlencode($venda['chave_nfe']));

                error_log("Reimprimir NF-e: Usando endpoint de visualização: {$pdfPathRel}");

                $this->success('PDF será gerado ao visualizar', [
                    'pdf_path' => $pdfPathRel,
                    'chave_nfe' => $venda['chave_nfe'],
                    'info' => 'PDF pode ser gerado dinamicamente ao abrir'
                ]);
                return;
            }

            // Criar endpoint para servir o PDF (já que está em src/ que não é acessível diretamente)
            // Usar rota interna para servir o arquivo
            $pdfPathRel = \App\Helpers\UrlHelper::url('/vendas/visualizar-pdf?chave=' . urlencode($venda['chave_nfe']));

            // OU tentar converter para caminho web se houver acesso direto
            // Primeiro tenta pelo caminho direto do arquivo
            $pdfPathWeb = str_replace(\ROOT_PATH . '/src/Integrations/NFe', '/Systhema/src/Integrations/NFe', $pdfPath);
            $pdfPathWeb = str_replace('\\', '/', $pdfPathWeb);

            // Mas melhor usar endpoint que serve o arquivo
            error_log("Reimprimir NF-e: ✅ PDF encontrado fisicamente em: {$pdfPath}");
            error_log("Reimprimir NF-e: Caminho web direto: {$pdfPathWeb}");

            $this->success('PDF encontrado', [
                'pdf_path' => $pdfPathRel,
                'pdf_path_alternativo' => $pdfPathWeb ?? null,
                'chave_nfe' => $venda['chave_nfe']
            ]);
        } catch (Exception $e) {
            error_log("Erro ao buscar PDF: " . $e->getMessage());
            $this->error('Erro ao buscar PDF: ' . $e->getMessage());
        }
    }

    /**
     * Reimprime NFS-e (retorna PDF ou XML)
     */
    public function reimprimirNfse(): void
    {
        $logFile = \ROOT_PATH . '/storage/logs/php-errors.log';
        $logDir = dirname($logFile);

        // Criar diretório se não existir
        if (!is_dir($logDir)) {
            mkdir($logDir, 0775, true);
        }

        $writeLog = function ($message) use ($logFile) {
            $timestamp = date('Y-m-d H:i:s');
            $logMessage = "[{$timestamp}] [reimprimirNfse] {$message}" . PHP_EOL;
            file_put_contents($logFile, $logMessage, FILE_APPEND);
        };

        try {
            $writeLog("=== INÍCIO reimprimirNfse ===");
            $writeLog("REQUEST_METHOD: " . ($_SERVER['REQUEST_METHOD'] ?? 'N/A'));
            $writeLog("REQUEST_URI: " . ($_SERVER['REQUEST_URI'] ?? 'N/A'));
            $writeLog("QUERY_STRING: " . ($_SERVER['QUERY_STRING'] ?? 'N/A'));

            $vendaId = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            $writeLog("vendaId recebido: {$vendaId}");
            $writeLog("companyId: {$companyId}");

            if (!$vendaId) {
                $writeLog("ERRO: ID da venda não informado");
                $this->error('ID da venda não informado');
                return;
            }

            // Buscar dados da venda
            $writeLog("Buscando venda ID: {$vendaId}");
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $writeLog("ERRO: Venda não encontrada");
                $this->error('Venda não encontrada');
                return;
            }

            $writeLog("Venda encontrada. nfse_numero: " . ($venda['nfse_numero'] ?? 'NULL'));
            $writeLog("nfse_status: " . ($venda['nfse_status'] ?? 'NULL'));

            // Verificar se possui NFS-e
            if (empty($venda['nfse_numero']) || empty($venda['nfse_status']) || $venda['nfse_status'] === 'cancelada') {
                $writeLog("ERRO: Venda não possui NFS-e emitida ou está cancelada");
                $this->error('Esta venda não possui NFS-e emitida');
                return;
            }

            // Buscar dados da empresa
            $writeLog("Buscando dados da empresa ID: {$companyId}");
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $writeLog("ERRO: Dados da empresa não encontrados");
                $this->error('Dados da empresa não encontrados');
                return;
            }

            $writeLog("Empresa encontrada. CNPJ: " . ($empresa['cnpj'] ?? $empresa['document'] ?? 'NULL'));

            // SEMPRE usar PRODUÇÃO (emissão real) - homologação desabilitada
            $homologacao = false;
            $ambiente = 'PRODUÇÃO';
            $writeLog("✅ AMBIENTE: {$ambiente} (Sempre produção - emissão real)");

            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');
            $numeroNfse = $venda['nfse_numero'];
            $nfsePdfPath = $venda['nfse_pdf_path'] ?? null;
            $nfseXmlPath = $venda['nfse_xml_path'] ?? null;

            // Tentar buscar PDF do caminho salvo no banco
            $writeLog("Buscando PDF. nfse_pdf_path do banco: " . ($nfsePdfPath ?? 'NULL'));
            $pdfPath = null;
            if (!empty($nfsePdfPath)) {
                // Se o caminho começa com /, é relativo ao ROOT_PATH
                if (strpos($nfsePdfPath, '/') === 0 || strpos($nfsePdfPath, '\\') === 0) {
                    $pdfPath = \ROOT_PATH . $nfsePdfPath;
                } else {
                    $pdfPath = \ROOT_PATH . '/' . $nfsePdfPath;
                }

                // Normalizar separadores
                $pdfPath = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $pdfPath);
                $writeLog("Testando caminho PDF do banco: {$pdfPath}");

                if (!file_exists($pdfPath)) {
                    $writeLog("PDF não encontrado no caminho do banco");
                    $pdfPath = null;
                } else {
                    $writeLog("PDF encontrado no caminho do banco!");
                }
            }

            // Se não encontrou no caminho salvo, tentar estrutura padrão
            if (!$pdfPath) {
                $writeLog("Buscando PDF na estrutura padrão...");
                $cnpjLimpo = preg_replace('/\D/', '', $cnpj);
                $anoMes = date('Y_m', strtotime($venda['nfse_data_emissao'] ?? 'now'));
                $writeLog("CNPJ limpo: {$cnpjLimpo}, Ano/Mês: {$anoMes}, Número NFS-e: {$numeroNfse}");

                // Tentar múltiplos formatos de nome de arquivo
                $possiveisNomes = [
                    sprintf('NFS-e_%s.pdf', str_pad((string) $numeroNfse, 9, '0', STR_PAD_LEFT)),
                    sprintf('NFS-e_%s.pdf', $numeroNfse),
                    sprintf('NFSe_%s.pdf', str_pad((string) $numeroNfse, 9, '0', STR_PAD_LEFT)),
                    sprintf('NFSe_%s.pdf', $numeroNfse),
                ];

                $diretorios = [
                    \ROOT_PATH . '/src/Integrations/NFSe/arquivos/' . $cnpjLimpo . '/nfse/pdf/' . $anoMes . '/',
                    \ROOT_PATH . '/src/Integrations/NFSe/arquivos/' . $cnpjLimpo . '/nfse/pdf/',
                    \ROOT_PATH . '/storage/nfse/',
                ];

                foreach ($diretorios as $diretorio) {
                    $writeLog("Testando diretório: {$diretorio}");
                    foreach ($possiveisNomes as $nomeArquivo) {
                        $caminhoTeste = $diretorio . $nomeArquivo;
                        $writeLog("  - Testando: {$caminhoTeste}");
                        if (file_exists($caminhoTeste)) {
                            $pdfPath = $caminhoTeste;
                            $writeLog("PDF encontrado em: {$pdfPath}");
                            break 2;
                        }
                    }
                }

                if (!$pdfPath) {
                    $writeLog("PDF não encontrado em nenhum dos diretórios testados");
                }
            }

            // Preparar resposta
            $dadosRetorno = [
                'numero_nfse' => $numeroNfse,
                'codigo_verificacao' => $venda['nfse_codigo_verificacao'] ?? null,
                'data_emissao' => $venda['nfse_data_emissao'] ?? null,
                'ambiente' => $ambiente,
                'homologacao' => $homologacao,
            ];

            // Se encontrou PDF, criar URL para acesso
            if ($pdfPath) {
                // Criar endpoint para servir o PDF
                $pdfUrl = \App\Helpers\UrlHelper::url('/vendas/visualizar-pdf-nfse?id=' . $vendaId);
                $dadosRetorno['pdf_path'] = $pdfUrl;
                $dadosRetorno['pdf_path_alternativo'] = str_replace(\ROOT_PATH, '', $pdfPath);
            }

            // Se tem XML, adicionar também
            if (!empty($nfseXmlPath)) {
                $xmlUrl = \App\Helpers\UrlHelper::url('/vendas/visualizar-xml-nfse?id=' . $vendaId);
                $dadosRetorno['xml_path'] = $xmlUrl;
            }

            // Se PDF não foi encontrado, tentar regenerar
            if (!$pdfPath && !empty($venda['nfse_numero'])) {
                $writeLog("PDF não encontrado. Tentando regenerar...");
                $pdfRegenerado = $this->regenerarPdfNfse($venda, $empresa, $numeroNfse);
                if ($pdfRegenerado) {
                    $writeLog("PDF regenerado com sucesso: {$pdfRegenerado}");
                    $pdfPath = $pdfRegenerado;
                    $pdfUrl = \App\Helpers\UrlHelper::url('/vendas/visualizar-pdf-nfse?id=' . $vendaId);
                    $dadosRetorno['pdf_path'] = $pdfUrl;
                    $dadosRetorno['pdf_path_alternativo'] = str_replace(\ROOT_PATH, '', $pdfPath);
                } else {
                    $writeLog("Não foi possível regenerar o PDF");
                }
            }

            $writeLog("Sucesso! Preparando resposta. PDF encontrado: " . ($pdfPath ? 'SIM' : 'NÃO'));
            $writeLog("XML encontrado: " . ($nfseXmlPath ? 'SIM' : 'NÃO'));
            $writeLog("=== FIM reimprimirNfse (SUCESSO) ===");

            $this->success('NFS-e encontrada', $dadosRetorno);
        } catch (Exception $e) {
            $writeLog("EXCEÇÃO: " . $e->getMessage());
            $writeLog("Stack trace: " . $e->getTraceAsString());
            $writeLog("=== FIM reimprimirNfse (ERRO) ===");

            error_log("Erro ao buscar NFS-e: " . $e->getMessage());
            $this->error('Erro ao buscar NFS-e: ' . $e->getMessage());
        }
    }

    /**
     * Visualiza PDF da NFS-e
     */
    public function visualizarPdfNfse(): void
    {
        try {
            $vendaId = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            if (!$vendaId) {
                header('HTTP/1.1 400 Bad Request');
                echo 'ID da venda não informado';
                exit;
            }

            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda || empty($venda['nfse_numero'])) {
                header('HTTP/1.1 404 Not Found');
                echo 'NFS-e não encontrada';
                exit;
            }

            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                header('HTTP/1.1 404 Not Found');
                echo 'Empresa não encontrada';
                exit;
            }

            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');
            $numeroNfse = $venda['nfse_numero'];
            $nfsePdfPath = $venda['nfse_pdf_path'] ?? null;

            // Buscar PDF
            $pdfPath = null;
            if (!empty($nfsePdfPath)) {
                if (strpos($nfsePdfPath, '/') === 0 || strpos($nfsePdfPath, '\\') === 0) {
                    $pdfPath = \ROOT_PATH . $nfsePdfPath;
                } else {
                    $pdfPath = \ROOT_PATH . '/' . $nfsePdfPath;
                }
                $pdfPath = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $pdfPath);

                if (!file_exists($pdfPath)) {
                    $pdfPath = null;
                }
            }

            // Tentar estrutura padrão se não encontrou
            if (!$pdfPath) {
                $cnpjLimpo = preg_replace('/\D/', '', $cnpj);
                $anoMes = date('Y_m', strtotime($venda['nfse_data_emissao'] ?? 'now'));

                $possiveisNomes = [
                    sprintf('NFS-e_%s.pdf', str_pad((string) $numeroNfse, 9, '0', STR_PAD_LEFT)),
                    sprintf('NFS-e_%s.pdf', $numeroNfse),
                ];

                $diretorios = [
                    \ROOT_PATH . '/src/Integrations/NFSe/arquivos/' . $cnpjLimpo . '/nfse/pdf/' . $anoMes . '/',
                    \ROOT_PATH . '/src/Integrations/NFSe/arquivos/' . $cnpjLimpo . '/nfse/pdf/',
                ];

                foreach ($diretorios as $diretorio) {
                    foreach ($possiveisNomes as $nomeArquivo) {
                        $caminhoTeste = $diretorio . $nomeArquivo;
                        if (file_exists($caminhoTeste)) {
                            $pdfPath = $caminhoTeste;
                            break 2;
                        }
                    }
                }
            }

            if (!$pdfPath || !file_exists($pdfPath)) {
                header('HTTP/1.1 404 Not Found');
                echo 'PDF da NFS-e não encontrado';
                exit;
            }

            // Definir headers para PDF
            header('Content-Type: application/pdf; charset=utf-8');
            header('Content-Disposition: inline; filename="NFS-e_' . $numeroNfse . '.pdf"');
            header('Content-Length: ' . filesize($pdfPath));
            header('Cache-Control: private, max-age=0, must-revalidate');
            header('Pragma: public');

            // Enviar arquivo
            readfile($pdfPath);
            exit;
        } catch (Exception $e) {
            error_log("Erro ao visualizar PDF NFS-e: " . $e->getMessage());
            header('HTTP/1.1 500 Internal Server Error');
            echo 'Erro ao visualizar PDF: ' . $e->getMessage();
            exit;
        }
    }

    /**
     * Visualiza XML da NFS-e
     */
    public function visualizarXmlNfse(): void
    {
        try {
            $vendaId = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            if (!$vendaId) {
                header('HTTP/1.1 400 Bad Request');
                echo 'ID da venda não informado';
                exit;
            }

            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda || empty($venda['nfse_numero'])) {
                header('HTTP/1.1 404 Not Found');
                echo 'NFS-e não encontrada';
                exit;
            }

            $nfseXmlPath = $venda['nfse_xml_path'] ?? null;

            if (empty($nfseXmlPath)) {
                header('HTTP/1.1 404 Not Found');
                echo 'XML da NFS-e não encontrado';
                exit;
            }

            // Buscar XML
            $xmlPath = null;
            if (strpos($nfseXmlPath, '/') === 0 || strpos($nfseXmlPath, '\\') === 0) {
                $xmlPath = \ROOT_PATH . $nfseXmlPath;
            } else {
                $xmlPath = \ROOT_PATH . '/' . $nfseXmlPath;
            }
            $xmlPath = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $xmlPath);

            if (!file_exists($xmlPath)) {
                header('HTTP/1.1 404 Not Found');
                echo 'Arquivo XML não encontrado no servidor';
                exit;
            }

            // Definir headers para XML
            header('Content-Type: application/xml; charset=utf-8');
            header('Content-Disposition: inline; filename="NFS-e_' . $venda['nfse_numero'] . '.xml"');
            header('Content-Length: ' . filesize($xmlPath));
            header('Cache-Control: private, max-age=0, must-revalidate');
            header('Pragma: public');

            // Enviar arquivo
            readfile($xmlPath);
            exit;
        } catch (Exception $e) {
            error_log("Erro ao visualizar XML NFS-e: " . $e->getMessage());
            header('HTTP/1.1 500 Internal Server Error');
            echo 'Erro ao visualizar XML: ' . $e->getMessage();
            exit;
        }
    }

    /**
     * Serve o PDF da NF-e para download/visualização
     */
    public function visualizarPdf(): void
    {
        try {
            $chaveNfe = $this->request->get('chave');
            $companyId = $this->getCompanyId();

            if (empty($chaveNfe)) {
                http_response_code(404);
                echo "PDF não encontrado";
                return;
            }

            // Buscar dados da empresa para pegar CNPJ
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                http_response_code(404);
                echo "Empresa não encontrada";
                return;
            }

            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');

            // Extrair ano e mês da chave de acesso NF-e (44 dígitos)
            // Posições 2-3: AA (últimos 2 dígitos do ano)
            // Posições 4-5: MM (mês)
            // Formato: UU AAMM CNPJ...
            if (strlen($chaveNfe) >= 6) {
                $aa = substr($chaveNfe, 2, 2); // Ano (últimos 2 dígitos)
                $mm = substr($chaveNfe, 4, 2); // Mês
                // Converter AA para ano completo (assumindo 2000-2099)
                $ano = '20' . $aa;
                $anoMes = $ano . '_' . $mm;
            } else {
                // Fallback: usar data atual
                $anoMes = date('Y_m');
            }

            // Tentar múltiplos caminhos com variações de nome de arquivo
            $chaveNfeLimpa = preg_replace('/\D/', '', $chaveNfe);
            $diretorioBase = \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/pdf/' . $anoMes . '/emitidos/';

            // Lista de possíveis nomes de arquivo (com e sem espaço)
            $possiveisNomes = [
                'NFe_' . $chaveNfe . '.pdf',
                'NFe_ ' . $chaveNfe . '.pdf',  // Com espaço extra
                'NFe_' . $chaveNfeLimpa . '.pdf',
                'NFe_ ' . $chaveNfeLimpa . '.pdf'  // Com espaço extra
            ];

            $pdfPath = null;

            // Primeiro tentar no diretório com ano/mês
            foreach ($possiveisNomes as $nomeArquivo) {
                $caminhoTeste = $diretorioBase . $nomeArquivo;
                error_log("Visualizar PDF: Testando: {$caminhoTeste}");
                if (file_exists($caminhoTeste)) {
                    $pdfPath = $caminhoTeste;
                    error_log("Visualizar PDF: ✅ PDF encontrado: {$pdfPath}");
                    break;
                }
            }

            // Se não encontrou, tentar sem ano/mês
            if (!$pdfPath) {
                $diretorioBase = \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/pdf/emitidos/';
                foreach ($possiveisNomes as $nomeArquivo) {
                    $caminhoTeste = $diretorioBase . $nomeArquivo;
                    error_log("Visualizar PDF: Testando (sem ano/mês): {$caminhoTeste}");
                    if (file_exists($caminhoTeste)) {
                        $pdfPath = $caminhoTeste;
                        error_log("Visualizar PDF: ✅ PDF encontrado (sem ano/mês): {$pdfPath}");
                        break;
                    }
                }
            }

            // Se ainda não encontrou, tentar no storage
            if (!$pdfPath) {
                $caminhoStorage = \ROOT_PATH . '/storage/nfe/NFe_' . $chaveNfe . '.pdf';
                error_log("Visualizar PDF: Testando storage: {$caminhoStorage}");
                if (file_exists($caminhoStorage)) {
                    $pdfPath = $caminhoStorage;
                }
            }

            if (!$pdfPath || !file_exists($pdfPath)) {
                error_log("Visualizar PDF: ❌ PDF não encontrado em nenhum caminho");
                http_response_code(404);
                echo "PDF não encontrado. Chave: {$chaveNfe}";
                return;
            }

            error_log("Visualizar PDF: ✅ Servindo PDF: {$pdfPath}");

            // Servir o PDF
            header('Content-Type: application/pdf');
            header('Content-Disposition: inline; filename="NFe_' . $chaveNfe . '.pdf"');
            header('Content-Length: ' . filesize($pdfPath));
            header('Cache-Control: private, max-age=3600');

            readfile($pdfPath);
            exit;
        } catch (Exception $e) {
            error_log("Erro ao servir PDF: " . $e->getMessage());
            http_response_code(500);
            echo "Erro ao servir PDF";
        }
    }

    /**
     * Cancela NF-e
     */
    public function cancelarNfe(): void
    {
        try {
            $vendaId = (int) $this->request->post('venda_id');
            $justificativa = $this->request->post('justificativa');
            $companyId = $this->getCompanyId();

            error_log("Cancelar NF-e: Venda ID = {$vendaId}, Company ID = {$companyId}");
            error_log("Cancelar NF-e: Justificativa = " . substr($justificativa, 0, 50) . "...");

            if (empty($justificativa) || strlen(trim($justificativa)) < 15) {
                error_log("Cancelar NF-e: ERRO - Justificativa inválida");
                $this->error('Justificativa deve ter no mínimo 15 caracteres');
                return;
            }

            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $this->error('Venda não encontrada');
                return;
            }

            if (empty($venda['chave_nfe']) || empty($venda['protocolo_nfe'])) {
                $this->error('Esta venda não possui NF-e emitida ou protocolo inválido');
                return;
            }

            // Buscar dados da empresa
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $this->error('Dados da empresa não encontrados');
                return;
            }

            // Montar dados para cancelamento (formato esperado pelo NFeService)
            // Processar certificado_path
            $certificadoPath = $empresa['certificado_path'] ?? '';
            if (!empty($certificadoPath)) {
                if (strpos($certificadoPath, \ROOT_PATH) === 0) {
                    // Já tem \ROOT_PATH
                    $certificadoPath = $certificadoPath;
                } else {
                    // Adicionar \ROOT_PATH
                    $certificadoPath = \ROOT_PATH . '/' . ltrim($certificadoPath, '/');
                }
            }

            $dadosCancelamento = [
                'empresa' => [
                    'nome' => $empresa['name'] ?? $empresa['razao_social'] ?? '',
                    'cnpj' => preg_replace('/\D/', '', $empresa['document'] ?? $empresa['cnpj'] ?? ''),
                    'inscricao_estadual' => (!empty($empresa['state_registration']) ? $empresa['state_registration'] : (!empty($empresa['inscricao_estadual']) ? $empresa['inscricao_estadual'] : (!empty($empresa['rg_ie']) ? $empresa['rg_ie'] : ''))),
                    'estado' => $empresa['state'] ?? $empresa['uf'] ?? 'PE',
                    'certificado_path' => $certificadoPath,
                    'certificado_senha' => $empresa['senha_certificado'] ?? ''
                ],
                'chave_acesso' => $venda['chave_nfe'],
                'protocolo_autorizacao' => $venda['protocolo_nfe'],
                'justificativa' => trim($justificativa)
            ];

            // Usar serviço de integração NF-e
            error_log("Cancelar NF-e: Inicializando NFeIntegrationService...");
            $nfeIntegration = new \App\Services\NFeIntegrationService();

            error_log("Cancelar NF-e: Chamando cancelarNFe com dados: " . json_encode($dadosCancelamento, JSON_PRETTY_PRINT));
            $resultado = $nfeIntegration->cancelarNFe($dadosCancelamento);

            error_log("Cancelar NF-e: Resultado = " . json_encode($resultado, JSON_PRETTY_PRINT));

            if (!$resultado['success']) {
                $cStat = $resultado['cStat'] ?? '';
                error_log("Cancelar NF-e: ERRO - " . ($resultado['error'] ?? 'Erro desconhecido') . " (cStat: {$cStat})");
                $this->error($resultado['error'] ?? 'Erro ao cancelar NF-e', [
                    'cStat' => $cStat
                ]);
                return;
            }

            // Atualizar status na venda: SALVAR protocolo de cancelamento
            // NÃO LIMPAR dados da NF-e (chave_nfe, numero_nfe, protocolo_nfe) pois são necessários para histórico
            $protocoloCancelamento = $resultado['protocolo_cancelamento'] ?? $resultado['protocolo'] ?? null;
            $dataCancelamento = $resultado['data_cancelamento'] ?? date('Y-m-d H:i:s');
            $pdfCancelamento = $resultado['pdf_path'] ?? null;
            $xmlCancelamento = $resultado['xml_path'] ?? null;

            error_log("Cancelar NF-e: Salvando protocolo_cancelamento = {$protocoloCancelamento}");
            error_log("Cancelar NF-e: PDF = {$pdfCancelamento}");
            error_log("Cancelar NF-e: XML = {$xmlCancelamento}");

            // Verificar se a coluna existe antes de atualizar
            $stmt = $this->db->prepare("
                UPDATE vendas
                SET protocolo_cancelamento = :protocolo_cancelamento,
                    data_cancelamento = :data_cancelamento,
                    justificativa_cancelamento = :justificativa_cancelamento,
                    pdf_cancelamento = :pdf_cancelamento,
                    xml_cancelamento = :xml_cancelamento
                WHERE id = :id AND company_id = :company_id
            ");
            $stmt->execute([
                'protocolo_cancelamento' => $protocoloCancelamento,
                'data_cancelamento' => $dataCancelamento,
                'justificativa_cancelamento' => trim($justificativa),
                'pdf_cancelamento' => $pdfCancelamento,
                'xml_cancelamento' => $xmlCancelamento,
                'id' => $vendaId,
                'company_id' => $companyId
            ]);

            error_log("Cancelar NF-e: ✅ Dados salvos - Protocolo: {$protocoloCancelamento}, Data: {$dataCancelamento}");

            $this->success('NF-e cancelada com sucesso', [
                'protocolo_cancelamento' => $protocoloCancelamento,
                'data_cancelamento' => $dataCancelamento,
                'pdf_cancelamento' => $pdfCancelamento,
                'xml_cancelamento' => $xmlCancelamento
            ]);
        } catch (Exception $e) {
            error_log("Erro ao cancelar NF-e: " . $e->getMessage());

            // Verificar se é um erro específico de NF-e já cancelada
            $mensagem = $e->getMessage();
            if (strpos($mensagem, 'já foi cancelada') !== false) {
                $this->error($mensagem);
                return;
            }

            $this->error('Erro ao cancelar NF-e: ' . $mensagem);
        }
    }

    /**
     * Visualiza PDF de cancelamento
     */
    public function visualizarPdfCancelamento(): void
    {
        try {
            $chave = $this->request->get('chave');

            if (empty($chave)) {
                $this->error('Chave de acesso não informada');
                return;
            }

            // Buscar dados da venda pelo chave_nfe
            $stmt = $this->db->prepare("
                SELECT pdf_cancelamento, xml_cancelamento
                FROM vendas
                WHERE chave_nfe = :chave_nfe AND company_id = :company_id
            ");
            $stmt->execute([
                'chave_nfe' => $chave,
                'company_id' => $this->getCompanyId()
            ]);
            $venda = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$venda || empty($venda['pdf_cancelamento'])) {
                $this->error('PDF de cancelamento não encontrado');
                return;
            }

            $pdfPath = $venda['pdf_cancelamento'];

            // Converter caminho relativo para absoluto
            if (strpos($pdfPath, '/pontti_nfe/') === 0) {
                // Remover /pontti_nfe/ e adicionar \ROOT_PATH
                $pdfPath = str_replace('/pontti_nfe/', \ROOT_PATH . '/src/Integrations/NFe/', $pdfPath);
            }

            if (!file_exists($pdfPath)) {
                $this->error('Arquivo PDF não encontrado no servidor');
                return;
            }

            // Definir headers para PDF
            header('Content-Type: application/pdf');
            header('Content-Disposition: inline; filename="cancelamento_' . $chave . '.pdf"');
            header('Content-Length: ' . filesize($pdfPath));
            header('Cache-Control: private, max-age=0, must-revalidate');
            header('Pragma: public');

            // Enviar arquivo
            readfile($pdfPath);
            exit;
        } catch (Exception $e) {
            error_log("Erro ao visualizar PDF de cancelamento: " . $e->getMessage());
            $this->error('Erro ao visualizar PDF de cancelamento');
        }
    }

    /**
     * Visualiza XML da NF-e
     */
    public function visualizarXml(): void
    {
        try {
            $vendaId = (int) $this->request->get('id');
            $companyId = $this->getCompanyId();

            if (!$vendaId) {
                $this->error('ID da venda não informado');
                return;
            }

            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $this->error('Venda não encontrada');
                return;
            }

            if (empty($venda['chave_nfe'])) {
                $this->error('Esta venda não possui NF-e emitida');
                return;
            }

            // Buscar dados da empresa para pegar CNPJ
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $this->error('Dados da empresa não encontrados');
                return;
            }

            $chaveNfe = $venda['chave_nfe'];
            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');

            // Extrair ano e mês da chave de acesso
            if (strlen($chaveNfe) >= 6) {
                $aa = substr($chaveNfe, 2, 2);
                $mm = substr($chaveNfe, 4, 2);
                $ano = '20' . $aa;
                $anoMes = $ano . '_' . $mm;
            } else {
                $anoMes = date('Y_m', strtotime($venda['created_at'] ?? 'now'));
            }

            // Buscar XML - tentar múltiplos caminhos
            $possiveisCaminhos = [
                // Pasta assinados com ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/' . $anoMes . '/assinados/',
                // Pasta emitidos com ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/' . $anoMes . '/emitidos/',
                // Pasta assinados sem ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/assinados/',
                // Pasta emitidos sem ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/emitidos/',
            ];

            $possiveisNomes = [
                $chaveNfe . '.xml',
                $chaveNfe . '-procNFe.xml',
            ];

            $xmlPath = null;
            foreach ($possiveisCaminhos as $diretorioBase) {
                foreach ($possiveisNomes as $nomeArquivo) {
                    $caminhoTeste = $diretorioBase . $nomeArquivo;
                    if (file_exists($caminhoTeste)) {
                        $xmlPath = $caminhoTeste;
                        break 2; // Sai dos dois loops
                    }
                }
            }

            if (!$xmlPath) {
                $this->error('Arquivo XML não encontrado');
                return;
            }

            $this->success('XML encontrado', [
                'xml_path' => '/vendas/download-xml?chave=' . urlencode($chaveNfe)
            ]);
        } catch (Exception $e) {
            error_log("Erro ao buscar XML: " . $e->getMessage());
            $this->error('Erro ao buscar XML');
        }
    }

    /**
     * Download do XML da NF-e
     */
    public function downloadXml(): void
    {
        try {
            $chave = $this->request->get('chave');
            $companyId = $this->getCompanyId();

            if (empty($chave)) {
                header('HTTP/1.1 404 Not Found');
                echo 'Chave de acesso não informada';
                exit;
            }

            // Buscar dados da empresa
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                header('HTTP/1.1 404 Not Found');
                echo 'Dados da empresa não encontrados';
                exit;
            }

            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');

            // Extrair ano e mês da chave de acesso
            if (strlen($chave) >= 6) {
                $aa = substr($chave, 2, 2);
                $mm = substr($chave, 4, 2);
                $ano = '20' . $aa;
                $anoMes = $ano . '_' . $mm;
            } else {
                $anoMes = date('Y_m');
            }

            // Buscar XML - tentar múltiplos caminhos
            $possiveisCaminhos = [
                // Pasta assinados com ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/' . $anoMes . '/assinados/',
                // Pasta emitidos com ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/' . $anoMes . '/emitidos/',
                // Pasta assinados sem ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/assinados/',
                // Pasta emitidos sem ano/mês
                \ROOT_PATH . '/src/Integrations/NFe/arquivos/' . $cnpj . '/nfe/xml/emitidos/',
            ];

            $possiveisNomes = [
                $chave . '.xml',
                $chave . '-procNFe.xml',
            ];

            $xmlPath = null;
            foreach ($possiveisCaminhos as $diretorioBase) {
                foreach ($possiveisNomes as $nomeArquivo) {
                    $caminhoTeste = $diretorioBase . $nomeArquivo;
                    if (file_exists($caminhoTeste)) {
                        $xmlPath = $caminhoTeste;
                        break 2; // Sai dos dois loops
                    }
                }
            }

            if (!$xmlPath) {
                header('HTTP/1.1 404 Not Found');
                echo 'Arquivo XML não encontrado';
                exit;
            }

            // Definir headers para XML
            header('Content-Type: application/xml; charset=utf-8');
            header('Content-Disposition: inline; filename="' . $chave . '-procNFe.xml"');
            header('Content-Length: ' . filesize($xmlPath));
            header('Cache-Control: private, max-age=0, must-revalidate');
            header('Pragma: public');

            // Enviar arquivo
            readfile($xmlPath);
            exit;
        } catch (Exception $e) {
            error_log("Erro ao baixar XML: " . $e->getMessage());
            header('HTTP/1.1 500 Internal Server Error');
            echo 'Erro ao baixar XML';
            exit;
        }
    }

    /**
     * Emite Carta de Correção Eletrônica (CCE)
     */
    public function cartaCorrecaoNfe(): void
    {
        try {
            $vendaId = (int) $this->request->post('venda_id');
            $campoCorrecao = $this->request->post('campo_correcao');
            $correcao = $this->request->post('correcao');
            $companyId = $this->getCompanyId();

            if (empty($correcao) || strlen(trim($correcao)) > 1000) {
                $this->error('Correção deve ter entre 1 e 1000 caracteres');
                return;
            }

            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $this->error('Venda não encontrada');
                return;
            }

            if (empty($venda['chave_nfe']) || empty($venda['protocolo_nfe'])) {
                $this->error('Esta venda não possui NF-e emitida ou protocolo inválido');
                return;
            }

            // Buscar dados da empresa
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $this->error('Dados da empresa não encontrados');
                return;
            }

            // Montar dados para CCE (formato esperado pelo NFeService)
            // Processar certificado_path
            $certificadoPath = $empresa['certificado_path'] ?? '';
            if (!empty($certificadoPath)) {
                if (strpos($certificadoPath, \ROOT_PATH) === 0) {
                    // Já tem \ROOT_PATH
                    $certificadoPath = $certificadoPath;
                } else {
                    // Adicionar \ROOT_PATH
                    $certificadoPath = \ROOT_PATH . '/' . ltrim($certificadoPath, '/');
                }
            }

            $dadosCCE = [
                'empresa' => [
                    'nome' => $empresa['name'] ?? $empresa['razao_social'] ?? '',
                    'cnpj' => preg_replace('/\D/', '', $empresa['document'] ?? $empresa['cnpj'] ?? ''),
                    'inscricao_estadual' => (!empty($empresa['state_registration']) ? $empresa['state_registration'] : (!empty($empresa['inscricao_estadual']) ? $empresa['inscricao_estadual'] : (!empty($empresa['rg_ie']) ? $empresa['rg_ie'] : ''))),
                    'estado' => $empresa['state'] ?? $empresa['uf'] ?? 'PE',
                    'certificado_path' => $certificadoPath,
                    'certificado_senha' => $empresa['senha_certificado'] ?? ''
                ],
                'chave_acesso' => $venda['chave_nfe'],
                'protocolo_autorizacao' => $venda['protocolo_nfe'],
                'correcao' => trim($correcao),
                'grupo_alteracao' => $campoCorrecao
            ];

            // Usar serviço de integração NF-e
            $nfeIntegration = new \App\Services\NFeIntegrationService();
            $resultado = $nfeIntegration->cartaCorrecaoNFe($dadosCCE);

            if (!$resultado['success']) {
                $cStat = $resultado['cStat'] ?? '';
                $this->error($resultado['error'] ?? 'Erro ao enviar carta de correção', [
                    'cStat' => $cStat
                ]);
                return;
            }

            $this->success('Carta de Correção enviada com sucesso', [
                'protocolo_cce' => $resultado['protocolo'] ?? null,
                'sequencia_cce' => $resultado['sequencia'] ?? null
            ]);
        } catch (Exception $e) {
            error_log("Erro ao enviar carta de correção: " . $e->getMessage());
            $this->error('Erro ao enviar carta de correção: ' . $e->getMessage());
        }
    }

    /**
     * Pré-visualizar DANFE antes da emissão
     */
    public function previewDanfe()
    {
        header('Content-Type: application/json; charset=utf-8');
        $vendaId = (int) $this->request->get('id');
        $companyId = $this->getCompanyId();

        if (!$vendaId) {
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'ID da venda não fornecido']);
            return;
        }

        try {
            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                http_response_code(404);
                echo json_encode(['success' => false, 'error' => 'Venda não encontrada']);
                return;
            }

            // Verificar se já tem NF-e emitida
            if (!empty($venda['chave_nfe']) && !empty($venda['protocolo_nfe'])) {
                http_response_code(400);
                echo json_encode(['success' => false, 'error' => 'Esta venda já possui NF-e emitida']);
                return;
            }

            // Buscar dados completos do cliente
            $clienteId = isset($venda['customer_id']) && $venda['customer_id'] !== ''
                ? (int) $venda['customer_id']
                : null;
            $cliente = $this->buscarDadosCliente($clienteId);

            // Buscar próximo número de NF-e
            $proximoNumeroNfe = $this->buscarProximoNumeroNFe($companyId);

            // Buscar dados da empresa
            $empresa = $this->buscarDadosEmpresa($companyId);

            // Buscar itens da venda
            $stmt = $this->db->prepare("
                SELECT DISTINCT
                    vi.id,
                    vi.venda_id,
                    vi.product_id,
                    vi.product_name,
                    vi.product_sku,
                    vi.quantity,
                    vi.unit_price,
                    vi.total_price,
                    COALESCE(vi.ncm, '0000.00.00') as ncm,
                    COALESCE(vi.cfop, '5102') as cfop,
                    COALESCE(vi.cst_icms, '00') as cst_icms,
                    COALESCE(vi.aliquota_icms, 0) as aliquota_icms,
                    COALESCE(vi.valor_icms, 0) as valor_icms,
                    COALESCE(vi.base_calculo_icms, 0) as base_calculo_icms,
                    COALESCE(vi.cst_pis, '01') as cst_pis,
                    COALESCE(vi.aliquota_pis, 0) as aliquota_pis,
                    COALESCE(vi.valor_pis, 0) as valor_pis,
                    COALESCE(vi.base_calculo_pis, 0) as base_calculo_pis,
                    COALESCE(vi.cst_cofins, '01') as cst_cofins,
                    COALESCE(vi.aliquota_cofins, 0) as aliquota_cofins,
                    COALESCE(vi.valor_cofins, 0) as valor_cofins,
                    COALESCE(vi.base_calculo_cofins, 0) as base_calculo_cofins,
                    COALESCE(vi.cst_ipi, '99') as cst_ipi,
                    COALESCE(vi.aliquota_ipi, 0) as aliquota_ipi,
                    COALESCE(vi.valor_ipi, 0) as valor_ipi,
                    COALESCE(vi.origem, '0') as origem,
                    COALESCE(vi.origem_st, '0') as origem_st,
                    vi.company_id,
                    vi.created_at,
                    COALESCE(vi.origem_st, ic.origem_mercadoria, i.origem_mercadoria, vi.origem, '0') as origem_mercadoria
                FROM vendas_itens vi
                LEFT JOIN produtos p ON vi.product_id = p.id
                LEFT JOIN impostos i ON p.grupo_tributacao_id = i.id AND i.ativo = 'Sim'
                LEFT JOIN impostos_configuracoes ic ON i.hash_imposto = ic.hash_imposto AND ic.company_id = vi.company_id
                WHERE vi.venda_id = :venda_id AND vi.company_id = :company_id
                ORDER BY vi.id ASC
            ");
            $stmt->execute([
                'venda_id' => $vendaId,
                'company_id' => $companyId
            ]);
            $itens = $stmt->fetchAll();

            // Buscar configurações de impostos
            $stmt = $this->db->prepare("
                SELECT * FROM impostos_configuracoes
                WHERE company_id = :company_id
                ORDER BY id DESC
                LIMIT 1
            ");
            $stmt->execute(['company_id' => $companyId]);
            $configuracoes = $stmt->fetch() ?: [];

            // Mesclar configurações vindas da UI (query params) com defaults
            $configuracoes['presenca_comprador'] = $this->request->get('presenca_comprador') ?? ($configuracoes['presenca_comprador'] ?? '0');
            $configuracoes['intermediador'] = $this->request->get('intermediador') ?? ($configuracoes['intermediador'] ?? '0');
            $configuracoes['base_pis_cofins_icms'] = (int) ($this->request->get('base_pis_cofins_icms') ?? ($configuracoes['base_pis_cofins_icms'] ?? 0));
            $configuracoes['exibir_pis_cofins_danfe'] = (int) ($this->request->get('exibir_pis_cofins_danfe') ?? ($configuracoes['exibir_pis_cofins_danfe'] ?? 0));
            $configuracoes['subtrair_icms_desonerado'] = (int) ($this->request->get('subtrair_icms_desonerado') ?? ($configuracoes['subtrair_icms_desonerado'] ?? 0));
            $configuracoes['exibir_financeiro'] = (int) ($this->request->get('exibir_financeiro') ?? ($configuracoes['exibir_financeiro'] ?? 0));
            $configuracoes['enviar_por_email'] = (int) ($this->request->get('enviar_por_email') ?? ($configuracoes['enviar_por_email'] ?? 0));

            // Montar dados para a API
            $dadosNfe = $this->montarDadosNFeAPI($venda, $itens, $empresa, $cliente, $proximoNumeroNfe, $configuracoes);

            // Gerar DANFE de preview usando a API
            $nfeService = new \App\Services\NFeIntegrationService();
            $resultado = $nfeService->previewDanfe($dadosNfe);

            if (!empty($resultado['success'])) {
                echo json_encode([
                    'success' => true,
                    'pdf_url' => $resultado['pdf_url'] ?? ($resultado['pdf_path'] ?? ''),
                    'message' => $resultado['message'] ?? 'DANFE de preview gerado com sucesso'
                ]);
                return;
            }

            http_response_code(400);
            echo json_encode([
                'success' => false,
                'error' => $resultado['error'] ?? 'Falha ao gerar preview'
            ]);
            return;
        } catch (\Throwable $e) {
            error_log("Erro ao gerar preview DANFE: " . $e->getMessage());
            http_response_code(500);
            echo json_encode([
                'success' => false,
                'error' => 'Erro interno: ' . $e->getMessage()
            ]);
            return;
        }
    }

    /**
     * Visualizar PDF de preview (DANFE)
     */
    public function visualizarPdfPreview()
    {
        $path = $this->request->get('path');
        error_log("🔍 visualizarPdfPreview - Path recebido: " . ($path ?? 'NULL'));

        if (!$path) {
            http_response_code(400);
            echo 'Caminho não informado';
            return;
        }

        $real = realpath($path);
        $base = realpath(\ROOT_PATH . '/src/Integrations/NFe/arquivos');

        error_log("🔍 Path original: " . $path);
        error_log("🔍 Real path: " . ($real ?: 'FALSE'));
        error_log("🔍 Base path: " . ($base ?: 'FALSE'));
        error_log("🔍 File exists: " . (file_exists($path) ? 'SIM' : 'NÃO'));

        if ($real === false || strpos($real, $base) !== 0 || !is_file($real)) {
            http_response_code(404);
            echo 'Arquivo não encontrado<br>';
            echo 'Path: ' . htmlspecialchars($path) . '<br>';
            echo 'Real: ' . htmlspecialchars($real ?: 'false') . '<br>';
            echo 'Base: ' . htmlspecialchars($base ?: 'false');
            return;
        }

        header('Content-Type: application/pdf');
        header('Content-Disposition: inline; filename="Preview_DANFE.pdf"');
        header('Content-Length: ' . filesize($real));
        readfile($real);
    }

    public function emitirNfse(): void
    {
        try {
            // Verificar se é requisição JSON e ler do corpo
            $requestData = [];
            if ($this->request->isJson()) {
                $input = file_get_contents('php://input');
                $requestData = json_decode($input, true) ?? [];
            } else {
                // Ler de $_POST normal
                $requestData = $_POST;
            }

            $vendaId = (int) ($requestData['venda_id'] ?? $this->request->post('venda_id') ?? 0);
            $companyId = $this->getCompanyId();

            if ($vendaId <= 0) {
                error_log("Erro emitirNfse: venda_id inválido. Dados recebidos: " . json_encode($requestData));
                $this->error('ID da venda inválido');
                return;
            }

            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $this->error('Venda não encontrada');
                return;
            }

            if (!empty($venda['nfse_status']) && $venda['nfse_status'] !== 'cancelada') {
                $this->error('Esta venda já possui NFS-e gerada.');
                return;
            }

            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $this->error('Configure os dados da empresa e do certificado digital antes de emitir NFS-e.');
                return;
            }

            // ⚠️ VERIFICAÇÃO CRÍTICA: Confirmar que está usando Portal Nacional de Produção
            $usarPortalNacional = !empty($empresa['nfse_nacional_ativo']);
            if ($usarPortalNacional) {
                $temOAuth2 = !empty($empresa['nfse_nacional_client_id']) && !empty($empresa['nfse_nacional_client_secret']);
                $temCertificado = !empty($empresa['certificado_path']) && !empty($empresa['senha_certificado']);

                if (!$temOAuth2 && !$temCertificado) {
                    $this->error('Configure as credenciais OAuth2 ou certificado digital para emitir NFS-e em produção.');
                    return;
                }

                // Log de confirmação de ambiente PRODUÇÃO
                error_log("✅ CONFIRMAÇÃO: Emissão NFS-e em PRODUÇÃO (sefin.nfse.gov.br) - Venda: {$vendaId}, CNPJ: " . ($empresa['cnpj'] ?? $empresa['document'] ?? 'N/A'));
            }

            $inscricaoMunicipal = $this->sanitizarNumero($empresa['inscricao_municipal'] ?? '');
            if ($inscricaoMunicipal === '') {
                $this->error('Inscrição municipal do prestador não configurada.');
                return;
            }

            // Ler dados do request (JSON ou POST)
            $codigoServico = trim((string) ($requestData['codigo_servico'] ?? $this->request->post('codigo_servico') ?? ''));
            if ($codigoServico === '') {
                $this->error('Informe o código do serviço (lista municipal).');
                return;
            }

            $codigoTributacao = trim((string) ($requestData['codigo_tributacao'] ?? $this->request->post('codigo_tributacao') ?? ''));
            if ($codigoTributacao === '') {
                $codigoTributacao = $codigoServico;
            }

            $descricaoServico = trim((string) ($requestData['descricao_servico'] ?? $this->request->post('descricao_servico') ?? ''));
            if ($descricaoServico === '') {
                $descricaoServico = 'Serviços prestados conforme venda ' . ($venda['sale_number'] ?? $vendaId);
            }

            $aliquotaIss = (float) ($requestData['aliquota_iss'] ?? $this->request->post('aliquota_iss') ?? 0);
            if ($aliquotaIss <= 0) {
                $this->error('Informe a alíquota de ISS em percentual.');
                return;
            }

            $naturezaOperacao = $requestData['natureza_operacao'] ?? $this->request->post('natureza_operacao') ?? '1';
            $regimeTributacao = $requestData['regime_tributacao'] ?? $this->request->post('regime_tributacao') ?? '';
            $optanteSimples = $requestData['optante_simples'] ?? $this->request->post('optante_simples') ?? ($empresa['optante_simples'] ?? '2');
            $issRetido = ($requestData['iss_retido'] ?? $this->request->post('iss_retido') ?? '2') === '1';

            $numeroRps = $this->buscarProximoNumeroNFSe($empresa);
            $serieRps = $this->obterSerieNFSe($empresa);
            $numeroLote = $numeroRps;

            $clienteId = isset($venda['customer_id']) && $venda['customer_id'] !== ''
                ? (int) $venda['customer_id']
                : null;
            $cliente = $this->buscarDadosCliente($clienteId);
            $valorServicos = (float) ($venda['total'] ?? 0);

            // Verificar se Portal Nacional está configurado e ativo
            // Pode usar: (1) OAuth2 (Client ID + Secret) OU (2) Apenas certificado digital
            $temOAuth2 = !empty($empresa['nfse_nacional_client_id']) && !empty($empresa['nfse_nacional_client_secret']);
            $temCertificado = !empty($empresa['certificado_path']) && !empty($empresa['senha_certificado']);
            $usarPortalNacional = !empty($empresa['nfse_nacional_ativo']) && ($temOAuth2 || $temCertificado);

            if ($usarPortalNacional) {
                // EMITIR VIA PORTAL NACIONAL (com fallback para sistema legado)
                try {
                    // ⚠️ ATENÇÃO: SEMPRE usar PRODUÇÃO (emissão real) - homologação desabilitada
                    // URL de produção: https://sefin.nfse.gov.br/SefinNacional/
                    // URL de homologação: https://api-hml.nfse.gov.br (NÃO USADA)
                    $homologacao = false;

                    // Log crítico para auditoria
                    error_log("🚨 TENTATIVA EMISSÃO NFS-e PRODUÇÃO (Portal Nacional) - Venda ID: {$vendaId}, CNPJ: " . ($empresa['cnpj'] ?? $empresa['document'] ?? 'N/A'));

                    // Se tem OAuth2 configurado, usa OAuth2. Senão, usa apenas certificado digital
                    $clientId = $empresa['nfse_nacional_client_id'] ?? null;
                    $clientSecret = $empresa['nfse_nacional_client_secret'] ?? null;

                    if (!empty($clientId) && !empty($clientSecret)) {
                        // Modo OAuth2
                        $nfseService = new NFSeNacionalService(
                            $clientId,
                            $clientSecret,
                            $homologacao,
                            null // não precisa passar empresa para OAuth2
                        );
                    } else {
                        // Modo apenas certificado digital
                        $nfseService = new NFSeNacionalService(
                            null, // sem OAuth2
                            null, // sem OAuth2
                            $homologacao,
                            $empresa // passa empresa com certificado
                        );
                    }

                    // Preparar dados da venda com cliente
                    $dadosVenda = $venda;
                    $dadosVenda['cliente'] = $cliente;

                    // Montar configuração
                    $configuracao = [
                        'codigo_servico' => $codigoServico,
                        'codigo_tributacao' => $codigoTributacao,
                        'descricao_servico' => $descricaoServico,
                        'aliquota_iss' => $aliquotaIss,
                        'natureza_operacao' => $naturezaOperacao,
                        'regime_tributacao' => $regimeTributacao,
                        'optante_simples' => $optanteSimples,
                        'iss_retido' => $issRetido ? '1' : '2',
                    ];

                    // Emitir NFS-e
                    $resultado = $nfseService->emitirNfse($dadosVenda, $empresa, $configuracao);

                    // Verificar se houve erro
                    if (!$resultado['success']) {
                        $erroMsg = $resultado['erro'] ?? 'Erro desconhecido';
                        $erroLower = mb_strtolower($erroMsg);

                        // Verificar se é erro de conexão/DNS
                        $isConnectionError = (
                            strpos($erroLower, 'could not resolve') !== false ||
                            strpos($erroLower, 'resolve host') !== false ||
                            strpos($erroLower, 'resolve') !== false ||
                            strpos($erroLower, 'dns') !== false ||
                            strpos($erroLower, 'connection') !== false ||
                            strpos($erroLower, 'timeout') !== false ||
                            strpos($erroLower, 'failed to connect') !== false ||
                            strpos($erroLower, 'não foi possível conectar') !== false ||
                            strpos($erroLower, 'erro ao conectar') !== false ||
                            strpos($erroLower, 'erro curl') !== false ||
                            strpos($erroLower, 'sefin.nfse.gov.br') !== false ||
                            strpos($erroLower, 'conectar ao portal nacional') !== false
                        );

                        if ($isConnectionError) {
                            error_log('❌ Portal Nacional indisponível (erro de conexão/DNS). Não é possível emitir NFS-e no momento.');
                            $this->error('Não foi possível conectar ao Portal Nacional de NFS-e.\n\nO servidor não consegue acessar sefin.nfse.gov.br.\n\n🔧 Ações necessárias:\n1. Verificar conectividade com a internet\n2. Verificar configurações de DNS do servidor\n3. Verificar se firewall/proxy está bloqueando\n4. Contatar administrador do servidor/hospedagem\n\n⚠️ O webservice legado do Recife não está mais em uso. É necessário que o Portal Nacional esteja acessível para emitir NFS-e.');
                            return;
                        } else {
                            // Erro não relacionado a conexão (validação, autenticação, etc)
                            $this->error('Erro ao emitir NFS-e: ' . $erroMsg);
                            return;
                        }
                    }

                    if ($resultado['success']) {
                        // Salvar PDF se disponível
                        $pdfPath = null;
                        if (!empty($resultado['pdf_path'])) {
                            $pdfPath = $this->salvarPdfNfse(
                                $resultado['pdf_path'],
                                $empresa['cnpj'] ?? $empresa['document'] ?? '',
                                $resultado['numero_nfse']
                            );
                        }

                        // Baixar e salvar XML se disponível
                        $xmlPath = null;
                        if (!empty($resultado['xml_path'])) {
                            $xmlPath = $this->salvarXmlNfse(
                                $resultado['xml_path'],
                                $empresa['cnpj'] ?? $empresa['document'] ?? '',
                                $resultado['numero_nfse']
                            );

                            // Tentar extrair código de verificação do XML se não veio na resposta JSON
                            if (empty($resultado['codigo_verificacao']) && $xmlPath) {
                                $codigoVerificacao = $this->extrairCodigoVerificacaoDoXml($xmlPath);
                                if ($codigoVerificacao) {
                                    $resultado['codigo_verificacao'] = $codigoVerificacao;
                                }
                            }
                        }

                        // Atualizar banco de dados
                        $this->atualizarDadosVendaNFSe($vendaId, [
                            'nfse_numero' => $resultado['numero_nfse'],
                            'nfse_serie' => '1', // Portal Nacional usa série fixa
                            'nfse_status' => 'autorizado',
                            'nfse_codigo_verificacao' => $resultado['codigo_verificacao'],
                            'nfse_data_emissao' => $resultado['data_emissao'],
                            'nfse_xml_path' => $xmlPath ?? $resultado['xml_path'],
                            'nfse_pdf_path' => $pdfPath,
                        ]);

                        $mensagem = 'NFS-e emitida com sucesso via Portal Nacional!';
                        $dadosRetorno = [
                            'numero_nfse' => $resultado['numero_nfse'],
                            'codigo_verificacao' => $resultado['codigo_verificacao'],
                            'data_emissao' => $resultado['data_emissao'],
                        ];

                        if ($pdfPath) {
                            $dadosRetorno['pdf_path'] = $pdfPath;
                        }

                        if (!empty($resultado['qr_code'])) {
                            $dadosRetorno['qr_code'] = $resultado['qr_code'];
                        }

                        $this->success($mensagem, $dadosRetorno);
                        return;
                    }
                    // Se chegou aqui e não teve sucesso, o erro já foi tratado acima (fallback ou erro retornado)
                } catch (Exception $e) {
                    $errorMessage = $e->getMessage();
                    error_log('❌ Erro ao emitir NFS-e via Portal Nacional: ' . $errorMessage);

                    // Verificar se é erro de DNS/conectividade
                    $errorLower = mb_strtolower($errorMessage);
                    $isConnectionError = (
                        strpos($errorLower, 'could not resolve') !== false ||
                        strpos($errorLower, 'resolve host') !== false ||
                        strpos($errorLower, 'resolve') !== false ||
                        strpos($errorLower, 'dns') !== false ||
                        strpos($errorLower, 'connection') !== false ||
                        strpos($errorLower, 'timeout') !== false ||
                        strpos($errorLower, 'failed to connect') !== false ||
                        strpos($errorLower, 'não foi possível conectar') !== false ||
                        strpos($errorLower, 'erro ao conectar') !== false ||
                        strpos($errorLower, 'erro curl') !== false ||
                        strpos($errorLower, 'sefin.nfse.gov.br') !== false ||
                        strpos($errorLower, 'conectar ao portal nacional') !== false
                    );

                    if ($isConnectionError) {
                        error_log('❌ Portal Nacional indisponível (erro de conexão/DNS). Não é possível emitir NFS-e no momento.');
                        $this->error('Não foi possível conectar ao Portal Nacional de NFS-e.\n\nO servidor não consegue acessar sefin.nfse.gov.br.\n\n🔧 Ações necessárias:\n1. Verificar conectividade com a internet\n2. Verificar configurações de DNS do servidor\n3. Verificar se firewall/proxy está bloqueando\n4. Contatar administrador do servidor/hospedagem\n\n⚠️ O webservice legado do Recife não está mais em uso. É necessário que o Portal Nacional esteja acessível para emitir NFS-e.');
                        return;
                    } else {
                        // Erro não relacionado a conexão (validação, autenticação, etc)
                        $this->error('Erro ao emitir NFS-e via Portal Nacional: ' . $errorMessage);
                        return;
                    }
                }
            }

            // Se Portal Nacional não está ativo, não há alternativa (webservice legado descontinuado)
            if (!$usarPortalNacional) {
                error_log('❌ Portal Nacional não está ativo e webservice legado não está mais em uso.');
                $this->error('Não é possível emitir NFS-e.\n\nO Portal Nacional de NFS-e não está configurado e o webservice legado do Recife não está mais em uso.\n\nPor favor, configure o Portal Nacional de NFS-e nas configurações da empresa.');
                return;
            }
        } catch (Exception $e) {
            error_log('Erro ao gerar NFS-e: ' . $e->getMessage());
            $this->error('Erro ao gerar NFS-e: ' . $e->getMessage());
        }
    }

    private function buscarProximoNumeroNFSe(array $empresa): int
    {
        $ultimo = (int) ($empresa['numero_nfse'] ?? 0);
        if ($ultimo < 0) {
            $ultimo = 0;
        }
        return $ultimo + 1;
    }

    private function obterSerieNFSe(array $empresa): string
    {
        $serie = trim((string) ($empresa['serie_nfse'] ?? ''));
        if ($serie === '') {
            $serie = '1';
        }
        return $serie;
    }

    private function atualizarSequenciaNFSe(int $empresaId, int $novoNumero): void
    {
        try {
            $stmt = $this->db->prepare("\n                UPDATE empresas\n                SET numero_nfse = :numero\n                WHERE id = :id\n            ");
            $stmt->execute([
                'numero' => $novoNumero,
                'id' => $empresaId
            ]);
        } catch (Exception $e) {
            error_log('Falha ao atualizar sequência da NFS-e: ' . $e->getMessage());
        }
    }

    private function mapearDadosTomador(?array $cliente, array $venda): array
    {
        $nome = $cliente['trade_name'] ?? $cliente['name'] ?? $venda['customer_name'] ?? 'Consumidor Final';
        $documento = $cliente['document'] ?? $venda['customer_document'] ?? '';

        $endereco = [
            'logradouro' => $cliente['address'] ?? $venda['customer_address'] ?? '',
            'numero' => $cliente['address_number'] ?? $venda['customer_number'] ?? 'S/N',
            'complemento' => $cliente['address_complement'] ?? '',
            'bairro' => $cliente['district'] ?? $venda['customer_district'] ?? '',
            'cep' => $cliente['zip_code'] ?? $cliente['cep'] ?? $venda['customer_zip'] ?? '',
            'uf' => strtoupper($cliente['state'] ?? $venda['customer_state'] ?? 'PE'),
            'codigo_municipio' => '2611606',
        ];

        return [
            'cpf_cnpj' => $documento,
            'inscricao_municipal' => $cliente['inscricao_municipal'] ?? '',
            'razao_social' => $nome,
            'endereco' => $endereco,
            'contato' => [
                'telefone' => $cliente['phone'] ?? $cliente['mobile'] ?? $venda['customer_phone'] ?? '',
                'email' => $cliente['email'] ?? $venda['customer_email'] ?? '',
            ]
        ];
    }

    private function atualizarDadosVendaNFSe(int $vendaId, array $dados): void
    {
        $set = [
            'nfse_numero = :nfse_numero',
            'nfse_serie = :nfse_serie',
            'nfse_status = :nfse_status',
            'nfse_codigo_verificacao = :nfse_codigo_verificacao',
            'nfse_xml_path = :nfse_xml_path',
        ];

        if (!empty($dados['nfse_pdf_path'])) {
            $set[] = 'nfse_pdf_path = :nfse_pdf_path';
        }

        if (!empty($dados['nfse_data_emissao'])) {
            $set[] = 'nfse_data_emissao = :nfse_data_emissao';
        } else {
            $set[] = 'nfse_data_emissao = NOW()';
        }

        $sql = "UPDATE vendas SET " . implode(', ', $set) . " WHERE id = :id";
        $stmt = $this->db->prepare($sql);

        $params = [
            'nfse_numero' => $dados['nfse_numero'],
            'nfse_serie' => $dados['nfse_serie'],
            'nfse_status' => $dados['nfse_status'],
            'nfse_codigo_verificacao' => $dados['nfse_codigo_verificacao'] ?? null,
            'nfse_xml_path' => $dados['nfse_xml_path'] ?? null,
            'id' => $vendaId
        ];

        if (!empty($dados['nfse_pdf_path'])) {
            $params['nfse_pdf_path'] = $dados['nfse_pdf_path'];
        }

        if (!empty($dados['nfse_data_emissao'])) {
            $params['nfse_data_emissao'] = $dados['nfse_data_emissao'];
        }

        $stmt->execute($params);
    }

    /**
     * Regenera PDF da NFS-e consultando o serviço
     */
    private function regenerarPdfNfse(array $venda, array $empresa, string $numeroNfse): ?string
    {
        $logFile = \ROOT_PATH . '/storage/logs/php-errors.log';
        $writeLog = function ($message) use ($logFile) {
            $timestamp = date('Y-m-d H:i:s');
            $logMessage = "[{$timestamp}] [regenerarPdfNfse] {$message}" . PHP_EOL;
            file_put_contents($logFile, $logMessage, FILE_APPEND);
        };

        try {
            $writeLog("=== INÍCIO regenerarPdfNfse ===");
            $writeLog("Número NFS-e: {$numeroNfse}");

            // Verificar se está usando Portal Nacional
            $temOAuth2 = !empty($empresa['nfse_nacional_client_id']) && !empty($empresa['nfse_nacional_client_secret']);
            $temCertificado = !empty($empresa['certificado_path']) && !empty($empresa['senha_certificado']);
            $usarPortalNacional = !empty($empresa['nfse_nacional_ativo']) && ($temOAuth2 || $temCertificado);

            if (!$usarPortalNacional) {
                $writeLog("Não está usando Portal Nacional. PDF não pode ser regenerado automaticamente.");
                return null;
            }

            $writeLog("Usando Portal Nacional. Consultando NFS-e...");

            // SEMPRE usar PRODUÇÃO (emissão real) - homologação desabilitada
            $homologacao = false;
            $clientId = $empresa['nfse_nacional_client_id'] ?? null;
            $clientSecret = $empresa['nfse_nacional_client_secret'] ?? null;

            if (!empty($clientId) && !empty($clientSecret)) {
                $nfseService = new NFSeNacionalService($clientId, $clientSecret, $homologacao, null);
            } else {
                $nfseService = new NFSeNacionalService(null, null, $homologacao, $empresa);
            }

            $codigoVerificacao = $venda['nfse_codigo_verificacao'] ?? null;
            $writeLog("Consultando NFS-e número: {$numeroNfse}, código: " . ($codigoVerificacao ?? 'NULL'));

            $resultado = $nfseService->consultarNfse($numeroNfse, $codigoVerificacao);

            $writeLog("Resultado da consulta: " . json_encode($resultado, JSON_UNESCAPED_UNICODE));

            // Tentar diferentes formatos de resposta
            $pdfUrl = $resultado['pdf_url']
                ?? $resultado['pdfUrl']
                ?? $resultado['pdf']
                ?? $resultado['url_pdf']
                ?? null;

            if (empty($pdfUrl)) {
                $writeLog("PDF URL não encontrada na consulta. Campos disponíveis: " . implode(', ', array_keys($resultado)));
                return null;
            }

            $writeLog("PDF URL obtida: {$pdfUrl}");

            // Baixar e salvar PDF
            $cnpj = preg_replace('/\D/', '', $empresa['cnpj'] ?? $empresa['document'] ?? '');
            $pdfPath = $this->salvarPdfNfse($pdfUrl, $cnpj, $numeroNfse);

            if ($pdfPath) {
                // Atualizar caminho no banco
                $this->atualizarDadosVendaNFSe($venda['id'], [
                    'nfse_pdf_path' => $pdfPath
                ]);

                // Converter caminho relativo para absoluto
                $pdfPathAbsoluto = \ROOT_PATH . $pdfPath;
                $writeLog("PDF salvo e banco atualizado. Caminho relativo: {$pdfPath}, Absoluto: {$pdfPathAbsoluto}");

                $writeLog("=== FIM regenerarPdfNfse (SUCESSO) ===");
                return $pdfPathAbsoluto;
            }

            $writeLog("=== FIM regenerarPdfNfse (FALHA) ===");
            return null;
        } catch (Exception $e) {
            $writeLog("ERRO ao regenerar PDF: " . $e->getMessage());
            $writeLog("Stack trace: " . $e->getTraceAsString());
            return null;
        }
    }

    /**
     * Salva PDF da NFS-e (se disponível)
     */
    private function salvarPdfNfse(string $urlPdf, string $cnpj, string $numeroNfse): ?string
    {
        try {
            $cnpjLimpo = preg_replace('/\D/', '', $cnpj);
            $anoMes = date('Y_m');
            $baseDir = rtrim(ROOT_PATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Integrations' . DIRECTORY_SEPARATOR . 'NFSe' . DIRECTORY_SEPARATOR . 'arquivos' . DIRECTORY_SEPARATOR . $cnpjLimpo . DIRECTORY_SEPARATOR . 'nfse' . DIRECTORY_SEPARATOR . 'pdf' . DIRECTORY_SEPARATOR . $anoMes;

            if (!is_dir($baseDir) && !mkdir($baseDir, 0775, true) && !is_dir($baseDir)) {
                error_log('Não foi possível criar diretório para PDF da NFS-e');
                return null;
            }

            // Baixar PDF da URL
            $pdfContent = file_get_contents($urlPdf);
            if ($pdfContent === false) {
                error_log('Erro ao baixar PDF da NFS-e da URL: ' . $urlPdf);
                return null;
            }

            $arquivo = sprintf('NFS-e_%s.pdf', str_pad((string) $numeroNfse, 9, '0', STR_PAD_LEFT));
            $caminhoCompleto = $baseDir . DIRECTORY_SEPARATOR . $arquivo;

            if (file_put_contents($caminhoCompleto, $pdfContent) === false) {
                error_log('Falha ao salvar PDF da NFS-e');
                return null;
            }

            return str_replace(rtrim(ROOT_PATH, DIRECTORY_SEPARATOR), '', $caminhoCompleto);
        } catch (Exception $e) {
            error_log('Erro ao salvar PDF da NFS-e: ' . $e->getMessage());
            return null;
        }
    }

    private function sanitizarNumero(string $valor): string
    {
        return preg_replace('/\D/', '', $valor) ?? '';
    }

    private function getEmpresaNFSeConfig(int $companyId): array
    {
        try {
            $stmt = $this->db->prepare("\n                SELECT nfse_codigo_servico, nfse_codigo_tributacao, nfse_aliquota_iss, nfse_natureza_operacao, nfse_regime_tributacao, nfse_optante_simples, nfse_iss_retido_padrao\n                FROM empresas\n                WHERE id = :id OR company_id = :company_id\n                LIMIT 1\n            ");
            $stmt->execute(['id' => $companyId, 'company_id' => $companyId]);
            $row = $stmt->fetch();

            if (!$row) {
                return [];
            }

            return [
                'codigo_servico' => $row['nfse_codigo_servico'] ?? '',
                'codigo_tributacao' => $row['nfse_codigo_tributacao'] ?? '',
                'aliquota_iss' => $row['nfse_aliquota_iss'] ?? '',
                'natureza_operacao' => $row['nfse_natureza_operacao'] ?? '1',
                'regime_tributacao' => $row['nfse_regime_tributacao'] ?? '',
                'optante_simples' => $row['nfse_optante_simples'] ?? '2',
                'iss_retido_padrao' => $row['nfse_iss_retido_padrao'] ?? '2',
            ];
        } catch (Exception $e) {
            error_log('Falha ao obter configuração padrão de NFS-e: ' . $e->getMessage());
            return [];
        }
    }

    /**
     * Gera próximo número sequencial de venda no formato VND-000001
     */
    private function gerarProximoNumeroVenda(int $companyId): string
    {
        // Tentar pegar o maior sufixo numérico de sale_number no padrão VND-xxxxxx
        $prefix = $this->moduleNumberPrefix();
        $prefixLength = strlen($prefix);
        $startPos = $prefixLength + 1; // SUBSTRING é 1-based

        $stmt = $this->db->prepare("SELECT MAX(CAST(SUBSTRING(sale_number, {$startPos}) AS UNSIGNED)) AS maxnum
            FROM vendas
            WHERE company_id = :company_id AND sale_number LIKE :prefix");
        $stmt->execute([
            'company_id' => $companyId,
            'prefix' => $prefix . '%'
        ]);
        $row = $stmt->fetch();
        $max = (int) ($row['maxnum'] ?? 0);
        $next = $max + 1;
        $numero = $prefix . str_pad((string) $next, 6, '0', STR_PAD_LEFT);

        // Garantir unicidade (loop rápido)
        $tentativas = 0;
        while ($tentativas < 3) {
            $check = $this->db->prepare("SELECT 1 FROM vendas WHERE company_id = :company_id AND sale_number = :sale_number LIMIT 1");
            $check->execute(['company_id' => $companyId, 'sale_number' => $numero]);
            if (!$check->fetch()) {
                break;
            }
            $next++;
            $numero = $prefix . str_pad((string) $next, 6, '0', STR_PAD_LEFT);
            $tentativas++;
        }

        return $numero;
    }

    /**
     * Salva XML da NFS-e baixando da URL
     */
    private function salvarXmlNfse(string $urlXml, string $cnpj, string $numeroNfse): ?string
    {
        try {
            $cnpjLimpo = preg_replace('/\D/', '', $cnpj);
            $anoMes = date('Y_m');
            $baseDir = rtrim(ROOT_PATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Integrations' . DIRECTORY_SEPARATOR . 'NFSe' . DIRECTORY_SEPARATOR . 'arquivos' . DIRECTORY_SEPARATOR . $cnpjLimpo . DIRECTORY_SEPARATOR . 'nfse' . DIRECTORY_SEPARATOR . 'xml' . DIRECTORY_SEPARATOR . $anoMes;

            if (!is_dir($baseDir) && !mkdir($baseDir, 0775, true) && !is_dir($baseDir)) {
                error_log('Não foi possível criar diretório para XML da NFS-e');
                return null;
            }

            // Baixar XML da URL
            $xmlContent = file_get_contents($urlXml);
            if ($xmlContent === false) {
                error_log('Erro ao baixar XML da NFS-e da URL: ' . $urlXml);
                return null;
            }

            $arquivo = sprintf('NFS-e_%s.xml', str_pad((string) $numeroNfse, 9, '0', STR_PAD_LEFT));
            $caminhoCompleto = $baseDir . DIRECTORY_SEPARATOR . $arquivo;

            if (file_put_contents($caminhoCompleto, $xmlContent) === false) {
                error_log('Falha ao salvar XML da NFS-e');
                return null;
            }

            return str_replace(rtrim(ROOT_PATH, DIRECTORY_SEPARATOR), '', $caminhoCompleto);
        } catch (Exception $e) {
            error_log('Erro ao salvar XML da NFS-e: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Extrai código de verificação do XML da NFS-e
     * Nota: O código de verificação geralmente vem na resposta JSON da API
     * Este método é um fallback caso o código não venha na resposta JSON
     */
    private function extrairCodigoVerificacaoDoXml(string $xmlPath): ?string
    {
        try {
            $xmlPathAbsoluto = strpos($xmlPath, ROOT_PATH) === 0 ? $xmlPath : ROOT_PATH . $xmlPath;

            if (!file_exists($xmlPathAbsoluto)) {
                return null;
            }

            $xmlContent = file_get_contents($xmlPathAbsoluto);
            if ($xmlContent === false) {
                return null;
            }

            // Carregar XML
            libxml_use_internal_errors(true);
            $xml = simplexml_load_string($xmlContent);

            if ($xml === false) {
                return null;
            }

            // Registrar namespace
            $xml->registerXPathNamespace('nfse', 'http://www.sped.fazenda.gov.br/nfse');

            // Tentar encontrar código de verificação no XML
            // Nota: No XML fornecido, não há campo específico de código de verificação
            // O código geralmente vem na resposta JSON da API
            $result = $xml->xpath('//nfse:infNFSe/nfse:codigoVerificacao');
            if (!empty($result)) {
                return (string) $result[0];
            }

            return null;
        } catch (Exception $e) {
            error_log('Erro ao extrair código de verificação do XML: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Gera boleto híbrido (Pix + Boleto) via Shipay para uma conta a receber
     * Chamado automaticamente ao criar contas a partir de vendas
     */
    private function gerarBoletoShipayParaConta(int $contaId, array $data): void
    {
        try {
            // Buscar método de pagamento
            $metodoId = $data['metodo_pagamento_id'] ?? null;
            if (!$metodoId) {
                return;
            }

            $companyId = $this->getCompanyId();

            $stmt = $this->db->prepare("SELECT gateway, type FROM metodos_pagamento WHERE id = :id AND company_id = :company_id");
            $stmt->execute(['id' => $metodoId, 'company_id' => $companyId]);
            $metodo = $stmt->fetch();

            // Verificar se é Shipay e se é boleto
            if (empty($metodo) || ($metodo['gateway'] ?? '') !== 'shipay' || ($metodo['type'] ?? '') !== 'boleto') {
                return;
            }

            // Buscar empresa com configuração Shipay
            $stmtEmpresa = $this->db->prepare("
                SELECT
                    id, razao_social, nome_fantasia, cnpj, email, telefone,
                    endereco, numero, complemento, bairro, cidade, uf, cep,
                    shipay_enabled, shipay_environment, shipay_access_key, shipay_secret_key,
                    shipay_client_id, shipay_store_id, shipay_cashier_id
                FROM empresas
                WHERE company_id = :company_id AND shipay_enabled = 1
                LIMIT 1
            ");
            $stmtEmpresa->execute(['company_id' => $companyId]);
            $empresa = $stmtEmpresa->fetch();

            if (empty($empresa) || empty($empresa['shipay_access_key']) || empty($empresa['shipay_secret_key']) || empty($empresa['shipay_client_id'])) {
                error_log("Venda: Shipay não configurado para esta empresa");
                return;
            }

            // Buscar dados da pessoa (pagador)
            $pessoa = null;
            if (!empty($data['pessoa_id'])) {
                $stmtPessoa = $this->db->prepare("SELECT * FROM pessoas WHERE id = :id AND company_id = :company_id");
                $stmtPessoa->execute(['id' => $data['pessoa_id'], 'company_id' => $companyId]);
                $pessoa = $stmtPessoa->fetch();
            }

            if (empty($pessoa)) {
                error_log("Venda: Pessoa não encontrada para gerar boleto Shipay");
                return;
            }

            $documento = preg_replace('/\D/', '', $pessoa['document'] ?? '');
            if (empty($documento)) {
                error_log("Venda: Cliente sem CPF/CNPJ para gerar boleto Shipay");
                return;
            }

            // Preparar configuração Shipay
            $paymentConfig = require \ROOT_PATH . '/config/payment.php';
            $shipayConfig = [
                'access_key' => $empresa['shipay_access_key'],
                'secret_key' => $empresa['shipay_secret_key'],
                'client_id' => $empresa['shipay_client_id'],
                'environment' => $empresa['shipay_environment'] ?? 'sandbox',
                'base_url' => $paymentConfig['shipay']['base_url'] ?? [],
            ];

            // Criar cliente Shipay
            $shipay = new ShipayClient($shipayConfig);

            // Preparar payload
            $telefone = preg_replace('/\D/', '', $pessoa['phone'] ?? $pessoa['mobile'] ?? $pessoa['telefone'] ?? '');

            $payload = [
                'valor' => (float) $data['amount'],
                'vencimento' => $data['due_date'],
                'descricao' => $data['description'] ?? 'Conta a receber #' . $contaId,
                'pagador' => [
                    'nome' => $pessoa['name'] ?? $pessoa['nome'] ?? '',
                    'documento' => $documento,
                    'email' => $pessoa['email'] ?? '',
                    'telefone' => $telefone,
                    'endereco' => [
                        'logradouro' => $pessoa['address'] ?? $pessoa['endereco'] ?? '',
                        'numero' => $pessoa['numero'] ?? $pessoa['address_number'] ?? '',
                        'complemento' => $pessoa['complemento'] ?? $pessoa['address_complement'] ?? '',
                        'bairro' => $pessoa['bairro'] ?? $pessoa['neighborhood'] ?? '',
                        'cidade' => $pessoa['city'] ?? $pessoa['cidade'] ?? '',
                        'uf' => $pessoa['state'] ?? $pessoa['uf'] ?? '',
                        'cep' => preg_replace('/\D/', '', $pessoa['zip_code'] ?? $pessoa['cep'] ?? ''),
                    ],
                ],
            ];

            // Adicionar store_id e cashier_id se configurados
            if (!empty($empresa['shipay_store_id'])) {
                $payload['store_id'] = $empresa['shipay_store_id'];
            }
            if (!empty($empresa['shipay_cashier_id'])) {
                $payload['cashier_id'] = $empresa['shipay_cashier_id'];
            }

            // Gerar boleto na Shipay
            $response = $shipay->createHybridBoleto($payload);

            // Extrair dados da resposta (order_id para /order, id/charge_id para outros endpoints)
            $chargeId = $response['order_id'] ?? $response['id'] ?? $response['charge_id'] ?? null;
            $linhaDigitavel = $response['linha_digitavel'] ?? $response['digitable_line'] ?? null;
            $qrCode = $response['qr_code'] ?? $response['pix_copia_cola'] ?? $response['qr_code_text'] ?? null;
            $pdfUrl = $response['pdf_url'] ?? $response['pdf'] ?? $response['boleto_url'] ?? null;
            $status = $response['status'] ?? 'pending';

            if ($chargeId) {
                // Verificar se colunas Shipay existem
                $stmtCheck = $this->db->query("SHOW COLUMNS FROM contas_receber LIKE 'shipay_%'");
                $columns = $stmtCheck->fetchAll();
                $columnNames = array_column($columns, 'Field');

                $updateFields = ['updated_at = NOW()'];
                $updateValues = ['id' => $contaId, 'company_id' => $companyId];

                if (in_array('shipay_charge_id', $columnNames)) {
                    $updateFields[] = 'shipay_charge_id = :charge_id';
                    $updateValues['charge_id'] = $chargeId;
                }
                if (in_array('shipay_status', $columnNames)) {
                    $updateFields[] = 'shipay_status = :status';
                    $updateValues['status'] = $status;
                }
                if (in_array('shipay_linha_digitavel', $columnNames) && $linhaDigitavel) {
                    $updateFields[] = 'shipay_linha_digitavel = :linha_digitavel';
                    $updateValues['linha_digitavel'] = $linhaDigitavel;
                }
                if (in_array('shipay_qr_code', $columnNames) && $qrCode) {
                    $updateFields[] = 'shipay_qr_code = :qr_code';
                    $updateValues['qr_code'] = $qrCode;
                }
                if (in_array('shipay_pdf_url', $columnNames) && $pdfUrl) {
                    $updateFields[] = 'shipay_pdf_url = :pdf_url';
                    $updateValues['pdf_url'] = $pdfUrl;
                }
                if (in_array('shipay_payload', $columnNames)) {
                    $updateFields[] = 'shipay_payload = :payload';
                    $updateValues['payload'] = json_encode($response, JSON_UNESCAPED_UNICODE);
                }

                if (count($updateFields) > 1) {
                    $sql = "UPDATE contas_receber SET " . implode(', ', $updateFields) . " WHERE id = :id AND company_id = :company_id";
                    $stmtUpdate = $this->db->prepare($sql);
                    $stmtUpdate->execute($updateValues);
                    error_log("Venda: Boleto Shipay gerado para conta #{$contaId} - Charge ID: {$chargeId}");
                }
            }

        } catch (Exception $e) {
            error_log("Venda: Erro ao gerar boleto Shipay para conta #{$contaId}: " . $e->getMessage());
            throw $e;
        }
    }

    /**
     * Simula todos os preços disponíveis para um produto
     */
    public function simularPrecosProduto(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $produtoId = (int) $this->request->get('produto_id');

            if (!$produtoId) {
                $this->error('ID do produto não informado');
                return;
            }

            // Buscar todos os preços do produto em todas as tabelas
            $stmt = $this->db->prepare("
                SELECT
                    pp.id,
                    pp.tabela_preco_id,
                    COALESCE(tp.nome, 'Padrão') as tabela_nome,
                    pp.valor,
                    pp.quantidade_min
                FROM produto_preco pp
                LEFT JOIN tabela_preco tp ON pp.tabela_preco_id = tp.id AND tp.company_id = pp.company_id
                WHERE pp.company_id = :company_id
                  AND pp.produto_id = :produto_id
                  AND pp.ativo = 1
                ORDER BY pp.tabela_preco_id ASC, pp.ordem ASC
            ");
            $stmt->execute([
                'company_id' => $companyId,
                'produto_id' => $produtoId
            ]);
            $precos = $stmt->fetchAll();

            $this->success('Preços encontrados', ['precos' => $precos]);

        } catch (Exception $e) {
            error_log("Erro ao simular preços: " . $e->getMessage());
            $this->error('Erro ao simular preços: ' . $e->getMessage());
        }
    }

    /**
     * Busca o preço do produto na tabela produto_preco baseado na tabela de preço do cliente
     * Vincula: pessoas.tabela_preco_id -> tabela_preco.id -> produto_preco.tabela_preco_id
     */
    public function buscarPrecoProduto(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $produtoId = (int) $this->request->get('produto_id');
            $tabelaPrecoId = $this->request->get('tabela_preco_id');
            $quantidade = (int) ($this->request->get('quantidade', 1) ?: 1);

            if (!$produtoId) {
                $this->error('ID do produto não informado');
                return;
            }

            $precoFinal = 0;
            $origem = 'nenhum';

            // Verificar se a tabela produto_preco existe
            $stmtCheck = $this->db->query("SHOW TABLES LIKE 'produto_preco'");
            if ($stmtCheck->rowCount() === 0) {
                $this->error('Tabela produto_preco não existe');
                return;
            }

            // Verificar se a coluna tabela_preco_id existe
            $stmtCols = $this->db->query("SHOW COLUMNS FROM produto_preco LIKE 'tabela_preco_id'");
            $temColunaTabela = $stmtCols->rowCount() > 0;

            // PRIORIDADE 1: Buscar preço na tabela de preço do cliente
            if ($tabelaPrecoId && $temColunaTabela) {
                $stmtPrecoTabela = $this->db->prepare("
                    SELECT pp.valor
                    FROM produto_preco pp
                    WHERE pp.company_id = :company_id
                      AND pp.produto_id = :produto_id
                      AND pp.tabela_preco_id = :tabela_preco_id
                      AND pp.ativo = 1
                    ORDER BY pp.ordem ASC
                    LIMIT 1
                ");
                $stmtPrecoTabela->execute([
                    'company_id' => $companyId,
                    'produto_id' => $produtoId,
                    'tabela_preco_id' => $tabelaPrecoId
                ]);
                $precoTabela = $stmtPrecoTabela->fetch();

                if ($precoTabela) {
                    $precoFinal = (float) $precoTabela['valor'];
                    $origem = 'tabela_preco';
                }
            }

            // PRIORIDADE 2: Buscar preço por quantidade (sem tabela específica)
            if ($origem === 'nenhum') {
                $stmtPrecoQtd = $this->db->prepare("
                    SELECT pp.valor
                    FROM produto_preco pp
                    WHERE pp.company_id = :company_id
                      AND pp.produto_id = :produto_id
                      AND pp.quantidade_min <= :quantidade
                      AND pp.ativo = 1
                      AND (pp.tabela_preco_id IS NULL OR pp.tabela_preco_id = 0)
                    ORDER BY pp.quantidade_min DESC, pp.ordem ASC
                    LIMIT 1
                ");
                $stmtPrecoQtd->execute([
                    'company_id' => $companyId,
                    'produto_id' => $produtoId,
                    'quantidade' => $quantidade
                ]);
                $precoQtd = $stmtPrecoQtd->fetch();

                if ($precoQtd) {
                    $precoFinal = (float) $precoQtd['valor'];
                    $origem = 'quantidade';
                }
            }

            // PRIORIDADE 3: Buscar qualquer preço do produto em produto_preco
            if ($origem === 'nenhum') {
                $stmtPrecoPadrao = $this->db->prepare("
                    SELECT pp.valor
                    FROM produto_preco pp
                    WHERE pp.company_id = :company_id
                      AND pp.produto_id = :produto_id
                      AND pp.ativo = 1
                    ORDER BY pp.ordem ASC
                    LIMIT 1
                ");
                $stmtPrecoPadrao->execute([
                    'company_id' => $companyId,
                    'produto_id' => $produtoId
                ]);
                $precoPadrao = $stmtPrecoPadrao->fetch();

                if ($precoPadrao) {
                    $precoFinal = (float) $precoPadrao['valor'];
                    $origem = 'produto_preco';
                }
            }

            $this->success('Preço encontrado', [
                'preco' => round($precoFinal, 2),
                'origem' => $origem,
                'tabela_preco_id' => $tabelaPrecoId
            ]);

        } catch (Exception $e) {
            error_log("Erro ao buscar preço do produto: " . $e->getMessage());
            $this->error('Erro ao buscar preço: ' . $e->getMessage());
        }
    }

    /**
     * Busca produtos por empresa_id via AJAX
     */
    public function buscarProdutosPorEmpresa(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $empresaId = $this->request->get('empresa_id');

            error_log("buscarProdutosPorEmpresa - companyId: {$companyId}, empresaId: {$empresaId}");

            if (empty($empresaId)) {
                error_log("buscarProdutosPorEmpresa - empresaId vazio, retornando array vazio");
                $this->success('Produtos encontrados', ['produtos' => []]);
                return;
            }

            // Verificar se a tabela empresas_produtos existe
            $stmt = $this->db->query("SHOW TABLES LIKE 'empresas_produtos'");
            $temTabelaEmpresasProdutos = $stmt->rowCount() > 0;

            error_log("buscarProdutosPorEmpresa - Tabela empresas_produtos existe: " . ($temTabelaEmpresasProdutos ? 'SIM' : 'NÃO'));

            if ($temTabelaEmpresasProdutos) {
                // Buscar produtos vinculados à empresa através de empresas_produtos
                $query = "
                    SELECT DISTINCT
                        p.id,
                        p.name,
                        p.sku,
                        COALESCE(
                            (SELECT pp.valor FROM produto_preco pp
                             WHERE pp.produto_id = p.id
                             AND pp.company_id = p.company_id
                             AND pp.ativo = 1
                             ORDER BY pp.tabela_preco_id ASC, pp.ordem ASC
                             LIMIT 1),
                            0
                        ) as price,
                        p.stock_quantity,
                        p.unit,
                        COALESCE(p.fracionado, 0) as fracionado,
                        COALESCE(p.quantidade_embalagem, 1) as quantidade_embalagem,
                        p.grupo_tributacao_id,
                        COALESCE(i.ncm, p.ncm) as ncm,
                        COALESCE(i.cest, p.cest) as cest,
                        COALESCE(ic.cfop, i.cfop, p.cfop) as cfop,
                        COALESCE(ic.origem_mercadoria, i.origem_mercadoria, p.origem) as origem,
                        COALESCE(ic.cst_csosn, i.cst_csosn, p.cst_icms) as cst_icms,
                        COALESCE(ic.icms, i.icms, p.aliquota_icms) as aliquota_icms,
                        COALESCE(ic.cst_pis, i.cst_pis, p.cst_pis) as cst_pis,
                        COALESCE(ic.pis, i.pis, p.aliquota_pis) as aliquota_pis,
                        COALESCE(ic.cst_cofins, i.cst_cofins, p.cst_cofins) as cst_cofins,
                        COALESCE(ic.cofins, i.cofins, p.aliquota_cofins) as aliquota_cofins,
                        COALESCE(ic.cst_ipi, i.cst_ipi, p.cst_ipi) as cst_ipi,
                        COALESCE(ic.ipi, i.ipi, p.aliquota_ipi) as aliquota_ipi,
                        COALESCE(ic.cclass, '000001') as cclass,
                        COALESCE(ic.aliquota_ibs, 0) as aliquota_ibs,
                        COALESCE(ic.reducao_aliquota_ibs, 0) as reducao_aliquota_ibs,
                        COALESCE(ic.aliquota_cbs, 0) as aliquota_cbs,
                        COALESCE(ic.reducao_aliquota_cbs, 0) as reducao_aliquota_cbs,
                        COALESCE(ic.aliquota_ibs_municipal, 0) as aliquota_ibs_municipal,
                        COALESCE(ic.reducao_aliquota_ibs_municipal, 0) as reducao_aliquota_ibs_municipal
                    FROM produtos p
                    INNER JOIN empresas_produtos ep ON p.id = ep.produto_id
                    LEFT JOIN impostos i ON p.grupo_tributacao_id = i.id AND i.ativo = 'Sim'
                    LEFT JOIN impostos_configuracoes ic ON i.hash_imposto = ic.hash_imposto
                        AND ic.company_id = p.company_id
                    WHERE ep.empresa_id = :empresa_id
                      AND p.company_id = :company_id
                      AND p.is_active = 1
                    GROUP BY p.id
                    ORDER BY p.name ASC
                ";
                $stmt = $this->db->prepare($query);
                $stmt->execute([
                    'empresa_id' => $empresaId,
                    'company_id' => $companyId
                ]);
            } else {
                // Se não existe tabela empresas_produtos, buscar todos os produtos (comportamento antigo)
                error_log("buscarProdutosPorEmpresa - Buscando todos os produtos (tabela empresas_produtos não existe)");
                $query = "
                    SELECT
                        p.id,
                        p.name,
                        p.sku,
                        COALESCE(
                            (SELECT pp.valor FROM produto_preco pp
                             WHERE pp.produto_id = p.id
                             AND pp.company_id = p.company_id
                             AND pp.ativo = 1
                             ORDER BY pp.tabela_preco_id ASC, pp.ordem ASC
                             LIMIT 1),
                            0
                        ) as price,
                        p.stock_quantity,
                        p.unit,
                        COALESCE(p.fracionado, 0) as fracionado,
                        COALESCE(p.quantidade_embalagem, 1) as quantidade_embalagem,
                        p.grupo_tributacao_id,
                        COALESCE(i.ncm, p.ncm) as ncm,
                        COALESCE(i.cest, p.cest) as cest,
                        COALESCE(ic.cfop, i.cfop, p.cfop) as cfop,
                        COALESCE(ic.origem_mercadoria, i.origem_mercadoria, p.origem) as origem,
                        COALESCE(ic.cst_csosn, i.cst_csosn, p.cst_icms) as cst_icms,
                        COALESCE(ic.icms, i.icms, p.aliquota_icms) as aliquota_icms,
                        COALESCE(ic.cst_pis, i.cst_pis, p.cst_pis) as cst_pis,
                        COALESCE(ic.pis, i.pis, p.aliquota_pis) as aliquota_pis,
                        COALESCE(ic.cst_cofins, i.cst_cofins, p.cst_cofins) as cst_cofins,
                        COALESCE(ic.cofins, i.cofins, p.aliquota_cofins) as aliquota_cofins,
                        COALESCE(ic.cst_ipi, i.cst_ipi, p.cst_ipi) as cst_ipi,
                        COALESCE(ic.ipi, i.ipi, p.aliquota_ipi) as aliquota_ipi,
                        COALESCE(ic.cclass, '000001') as cclass,
                        COALESCE(ic.aliquota_ibs, 0) as aliquota_ibs,
                        COALESCE(ic.reducao_aliquota_ibs, 0) as reducao_aliquota_ibs,
                        COALESCE(ic.aliquota_cbs, 0) as aliquota_cbs,
                        COALESCE(ic.reducao_aliquota_cbs, 0) as reducao_aliquota_cbs,
                        COALESCE(ic.aliquota_ibs_municipal, 0) as aliquota_ibs_municipal,
                        COALESCE(ic.reducao_aliquota_ibs_municipal, 0) as reducao_aliquota_ibs_municipal
                    FROM produtos p
                    LEFT JOIN impostos i ON p.grupo_tributacao_id = i.id AND i.ativo = 'Sim'
                    LEFT JOIN impostos_configuracoes ic ON i.hash_imposto = ic.hash_imposto
                        AND ic.company_id = p.company_id
                    WHERE p.company_id = :company_id AND p.is_active = 1
                    GROUP BY p.id
                    ORDER BY p.name ASC
                ";
                $stmt = $this->db->prepare($query);
                $stmt->execute(['company_id' => $companyId]);
            }

            $produtos = $stmt->fetchAll();
            error_log("buscarProdutosPorEmpresa - Produtos encontrados: " . count($produtos));

            $this->success('Produtos encontrados', ['produtos' => $produtos]);

        } catch (Exception $e) {
            error_log("Erro ao buscar produtos por empresa: " . $e->getMessage());
            error_log("Stack trace: " . $e->getTraceAsString());
            $this->error('Erro ao buscar produtos: ' . $e->getMessage());
        }
    }

    /**
     * Busca clientes por empresa_id via AJAX
     */
    public function buscarClientesPorEmpresa(): void
    {
        try {
            $companyId = $this->getCompanyId();
            $empresaId = $this->request->get('empresa_id');

            error_log("buscarClientesPorEmpresa - companyId: {$companyId}, empresaId: {$empresaId}");

            if (empty($empresaId)) {
                error_log("buscarClientesPorEmpresa - empresaId vazio, retornando array vazio");
                $this->success('Clientes encontrados', ['clientes' => []]);
                return;
            }

            // Verificar se a tabela empresas_pessoas existe
            $stmt = $this->db->query("SHOW TABLES LIKE 'empresas_pessoas'");
            $temTabelaEmpresasPessoas = $stmt->rowCount() > 0;

            error_log("buscarClientesPorEmpresa - Tabela empresas_pessoas existe: " . ($temTabelaEmpresasPessoas ? 'SIM' : 'NÃO'));

            if ($temTabelaEmpresasPessoas) {
                // Buscar clientes vinculados à empresa através de empresas_pessoas
                $query = "
                    SELECT DISTINCT p.id, p.name, p.trade_name, p.document, p.phone, p.mobile, p.email, p.tabela_preco_id,
                           p.vendedor_id, p.vendedor_auxiliar_id
                    FROM pessoas p
                    INNER JOIN empresas_pessoas ep ON p.id = ep.pessoa_id
                    WHERE ep.empresa_id = :empresa_id
                      AND p.company_id = :company_id
                      AND p.type IN ('cliente', 'ambos')
                      AND p.is_active = 1
                    ORDER BY p.name ASC
                ";
                $stmt = $this->db->prepare($query);
                $stmt->execute([
                    'empresa_id' => $empresaId,
                    'company_id' => $companyId
                ]);
            } else {
                // Se não existe tabela empresas_pessoas, buscar todos os clientes (comportamento antigo)
                error_log("buscarClientesPorEmpresa - Buscando todos os clientes (tabela empresas_pessoas não existe)");
                $query = "
                    SELECT id, name, trade_name, document, phone, mobile, email, tabela_preco_id,
                           vendedor_id, vendedor_auxiliar_id
                    FROM pessoas
                    WHERE company_id = :company_id
                      AND type IN ('cliente', 'ambos')
                      AND is_active = 1
                    ORDER BY name ASC
                ";
                $stmt = $this->db->prepare($query);
                $stmt->execute(['company_id' => $companyId]);
            }

            $clientes = $stmt->fetchAll();
            error_log("buscarClientesPorEmpresa - Clientes encontrados: " . count($clientes));

            $this->success('Clientes encontrados', ['clientes' => $clientes]);

        } catch (Exception $e) {
            error_log("Erro ao buscar clientes por empresa: " . $e->getMessage());
            error_log("Stack trace: " . $e->getTraceAsString());
            $this->error('Erro ao buscar clientes: ' . $e->getMessage());
        }
    }

    /**
     * Busca licenças e registros de uma pessoa/cliente
     */
    public function buscarLicencasCliente(): void
    {
        try {
            $clienteId = $this->request->get('cliente_id');

            if (empty($clienteId)) {
                $this->success('Licenças encontradas', ['licencas' => null]);
                return;
            }

            $companyId = $this->getCompanyId();

            // Verificar se as colunas existem antes de buscar
            $stmt = $this->db->query("SHOW COLUMNS FROM pessoas LIKE 'data_anvisa'");
            $temColunaAnvisa = $stmt->rowCount() > 0;

            if (!$temColunaAnvisa) {
                $this->success('Licenças encontradas', ['licencas' => null]);
                return;
            }

            // Verificar se existe campo credit_limit
            $stmt = $this->db->query("SHOW COLUMNS FROM pessoas LIKE 'credit_limit'");
            $temCreditLimit = $stmt->rowCount() > 0;

            // Montar query dinamicamente
            $campos = [
                'data_anvisa',
                'data_conselho_farmacia',
                'data_vigilancia_sanitaria',
                'data_alvara_localizacao'
            ];

            if ($temCreditLimit) {
                $campos[] = 'credit_limit';
            }

            $query = "
                SELECT " . implode(', ', $campos) . "
                FROM pessoas
                WHERE id = :cliente_id
                  AND company_id = :company_id
            ";

            $stmt = $this->db->prepare($query);
            $stmt->execute([
                'cliente_id' => $clienteId,
                'company_id' => $companyId
            ]);

            $licencas = $stmt->fetch();

            // Calcular limite usado e inadimplência (soma de contas a receber pendentes)
            $limiteCredito = $temCreditLimit ? (float) ($licencas['credit_limit'] ?? 0) : 0;
            $limiteUtilizado = 0;
            $limiteDisponivel = $limiteCredito;
            $totalVencido = 0;
            $contasVencidas = 0;
            $situacaoCliente = 'regular'; // 'regular' ou 'inadimplente'
            $hoje = date('Y-m-d');

            // Verificar estrutura da tabela contas_receber
            $stmtCols = $this->db->query("SHOW COLUMNS FROM contas_receber");
            $colunas = $stmtCols->fetchAll(\PDO::FETCH_COLUMN, 0);

            $campoRemaining = in_array('amount_remaining', $colunas) ? 'amount_remaining' : 'amount';
            $campoPessoaId = in_array('pessoa_id', $colunas) ? 'pessoa_id' : 'customer_id';
            $campoStatus = in_array('status', $colunas) ? 'status' : null;
            $temDueDate = in_array('due_date', $colunas);

            // Buscar contas a receber para calcular limite usado e inadimplência
            $queryContas = "
                SELECT
                    {$campoRemaining} as remaining,
                    " . ($temDueDate ? "due_date" : "NULL as due_date") . "
                FROM contas_receber
                WHERE {$campoPessoaId} = :cliente_id
                  AND company_id = :company_id
            ";

            // Adicionar filtro de status se existir (excluir pagos e cancelados)
            if ($campoStatus) {
                $queryContas .= " AND status NOT IN ('pago', 'cancelado')";
            }

            $stmtContas = $this->db->prepare($queryContas);
            $stmtContas->execute([
                'cliente_id' => $clienteId,
                'company_id' => $companyId
            ]);

            $contas = $stmtContas->fetchAll();

            foreach ($contas as $conta) {
                $remaining = (float) ($conta['remaining'] ?? 0);
                $limiteUtilizado += $remaining;

                // Verificar se está vencida
                if ($temDueDate && !empty($conta['due_date'])) {
                    $vencimento = strtotime($conta['due_date']);
                    $hojeTimestamp = strtotime($hoje);

                    if ($vencimento < $hojeTimestamp && $remaining > 0) {
                        $totalVencido += $remaining;
                        $contasVencidas++;
                    }
                }
            }

            // Determinar situação do cliente
            if ($contasVencidas > 0 || $totalVencido > 0) {
                $situacaoCliente = 'inadimplente';
            }

            // Calcular limite disponível
            $limiteDisponivel = $limiteCredito - $limiteUtilizado;

            $this->success('Licenças encontradas', [
                'licencas' => $licencas ?: null,
                'limite_credito' => $limiteCredito,
                'limite_utilizado' => $limiteUtilizado,
                'limite_disponivel' => $limiteDisponivel,
                'situacao' => $situacaoCliente,
                'total_vencido' => $totalVencido,
                'contas_vencidas' => $contasVencidas
            ]);

        } catch (Exception $e) {
            error_log("Erro ao buscar licenças do cliente: " . $e->getMessage());
            $this->error('Erro ao buscar licenças: ' . $e->getMessage());
        }
    }

    /**
     * Busca empresas vinculadas ao usuário logado através da tabela empresas_users
     */
    private function getEmpresasVinculadasUsuario(): array
    {
        try {
            $userId = $this->getUserId();

            if (!$userId) {
                error_log("Vendas: Usuário não logado");
                return [];
            }

            // Verificar se a tabela empresas existe
            $stmt = $this->db->query("SHOW TABLES LIKE 'empresas'");
            if ($stmt->rowCount() === 0) {
                error_log("Vendas: Tabela 'empresas' não existe");
                return [];
            }

            // Verificar se a tabela empresas_users existe
            $stmt = $this->db->query("SHOW TABLES LIKE 'empresas_users'");
            $temTabelaEmpresasUsers = $stmt->rowCount() > 0;

            // Verificar se existe coluna 'ativo' ou 'status' na tabela empresas
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM empresas LIKE 'ativo'");
            $temColunaAtivo = $stmtCheck->rowCount() > 0;

            $stmtCheck = $this->db->query("SHOW COLUMNS FROM empresas LIKE 'status'");
            $temColunaStatus = $stmtCheck->rowCount() > 0;

            if ($temTabelaEmpresasUsers) {
                // Buscar empresas vinculadas ao usuário através de empresas_users
                $query = "
                    SELECT DISTINCT e.id, e.razao_social, e.nome_fantasia, e.cnpj
                    FROM empresas e
                    INNER JOIN empresas_users eu ON e.id = eu.empresa_id
                    WHERE eu.user_id = :user_id
                ";

                // Adicionar filtro de ativo se existir
                if ($temColunaAtivo) {
                    $query .= " AND (e.ativo = 'Sim' OR e.ativo = 1 OR e.ativo = '1')";
                } elseif ($temColunaStatus) {
                    $query .= " AND (e.status = 'ativo' OR e.status = 1 OR e.status = '1')";
                }

                $query .= " ORDER BY e.razao_social ASC";

                $stmt = $this->db->prepare($query);
                $stmt->execute(['user_id' => $userId]);
                return $stmt->fetchAll();
            } else {
                // Se não existe tabela empresas_users, buscar todas as empresas (comportamento antigo)
                $query = "
                    SELECT id, razao_social, nome_fantasia, cnpj
                    FROM empresas
                ";

                // Adicionar filtro se existir coluna ativo ou status
                if ($temColunaAtivo) {
                    $query .= " WHERE ativo = 'Sim' OR ativo = 1 OR ativo = '1'";
                } elseif ($temColunaStatus) {
                    $query .= " WHERE status = 'ativo' OR status = 1 OR status = '1'";
                }

                $query .= " ORDER BY razao_social ASC";

                $stmt = $this->db->prepare($query);
                $stmt->execute();
                return $stmt->fetchAll();
            }
        } catch (Exception $e) {
            error_log("Erro ao buscar empresas vinculadas ao usuário: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Duplica uma venda criando uma nova com novo número de pedido
     */
    public function duplicar(): void
    {
        // Verificar permissão de criação
        if (!$this->canCreate('vendas')) {
            $this->response->forbidden('Você não tem permissão para criar vendas.');
            return;
        }

        try {
            $id = (int) $this->request->post('id');
            $companyId = $this->getCompanyId();

            // Buscar venda original
            $stmt = $this->db->prepare("
                SELECT * FROM vendas
                WHERE id = :id AND company_id = :company_id
            ");
            $stmt->execute(['id' => $id, 'company_id' => $companyId]);
            $vendaOriginal = $stmt->fetch();

            if (!$vendaOriginal) {
                $this->error('Venda não encontrada');
                return;
            }

            $this->db->beginTransaction();

            // Gerar próximo número de venda
            $proximoNumero = $this->gerarProximoNumeroVenda($companyId);

            // Verificar se a coluna empresa_id existe
            $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas LIKE 'empresa_id'");
            $temColunaEmpresaId = $stmtCheck->rowCount() > 0;

            // Criar nova venda com os dados da original, mas com novo número
            $campoEmpresaId = $temColunaEmpresaId ? ', empresa_id' : '';
            $valorEmpresaId = $temColunaEmpresaId ? ', :empresa_id' : '';

            $data = [
                'company_id' => $companyId,
                'modulo_origem' => $vendaOriginal['modulo_origem'],
                'customer_type' => $vendaOriginal['customer_type'],
                'customer_id' => $vendaOriginal['customer_id'],
                'customer_name' => $vendaOriginal['customer_name'],
                'customer_document' => $vendaOriginal['customer_document'],
                'sale_number' => $proximoNumero,
                'sale_date' => date('Y-m-d'), // Data atual
                'payment_method_id' => $vendaOriginal['payment_method_id'],
                'payment_term_id' => $vendaOriginal['payment_term_id'],
                'installments' => $vendaOriginal['installments'],
                'subtotal' => $vendaOriginal['subtotal'],
                'discount' => $vendaOriginal['discount'],
                'discount_type' => $vendaOriginal['discount_type'],
                'additions' => $vendaOriginal['additions'],
                'shipping' => $vendaOriginal['shipping'],
                'total' => $vendaOriginal['total'],
                'status' => 'pendente', // Status inicial como pendente
                'notes' => $vendaOriginal['notes'],
                'observacoes_nfe' => $vendaOriginal['observacoes_nfe'],
                'vendor_id' => $vendaOriginal['vendor_id'],
                'auxiliary_vendor_id' => $vendaOriginal['auxiliary_vendor_id'],
                'generate_receivables' => 0, // Não gerar contas a receber automaticamente
                'update_stock' => 0, // Não atualizar estoque automaticamente
            ];

            if ($temColunaEmpresaId && !empty($vendaOriginal['empresa_id'])) {
                $data['empresa_id'] = $vendaOriginal['empresa_id'];
            }

            $stmt = $this->db->prepare("
                INSERT INTO vendas (
                    company_id{$campoEmpresaId}, modulo_origem, customer_type, customer_id, customer_name, customer_document,
                    sale_number, sale_date, payment_method_id, payment_term_id, installments,
                    subtotal, discount, discount_type, additions, shipping, total,
                    status, notes, observacoes_nfe, vendor_id, auxiliary_vendor_id, generate_receivables, update_stock,
                    created_at, updated_at
                ) VALUES (
                    :company_id{$valorEmpresaId}, :modulo_origem, :customer_type, :customer_id, :customer_name, :customer_document,
                    :sale_number, :sale_date, :payment_method_id, :payment_term_id, :installments,
                    :subtotal, :discount, :discount_type, :additions, :shipping, :total,
                    :status, :notes, :observacoes_nfe, :vendor_id, :auxiliary_vendor_id, :generate_receivables, :update_stock,
                    NOW(), NOW()
                )
            ");
            $stmt->execute($data);
            $novaVendaId = (int) $this->db->lastInsertId();

            // Buscar e duplicar itens da venda original
            $stmtItens = $this->db->prepare("
                SELECT * FROM vendas_itens
                WHERE venda_id = :venda_id
            ");
            $stmtItens->execute(['venda_id' => $id]);
            $itens = $stmtItens->fetchAll();

            // Verificar colunas opcionais
            $colunaVendedorIdExiste = false;
            $colunaComissaoExiste = false;
            $colunaComissaoPercentualExiste = false;
            $colunaProfissionalIdExiste = false;
            try {
                $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'vendedor_id'");
                $colunaVendedorIdExiste = $stmtCheck->fetch() !== false;

                $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'comissao'");
                $colunaComissaoExiste = $stmtCheck->fetch() !== false;

                $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'comissao_percentual'");
                $colunaComissaoPercentualExiste = $stmtCheck->fetch() !== false;

                $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_itens LIKE 'profissional_id'");
                $colunaProfissionalIdExiste = $stmtCheck->fetch() !== false;
            } catch (\Exception $e) {
                // Ignorar erro
            }

            $vendedorIdField = $colunaVendedorIdExiste ? ', vendedor_id' : '';
            $vendedorIdValue = $colunaVendedorIdExiste ? ', :vendedor_id' : '';
            $comissaoField = $colunaComissaoExiste ? ', comissao' : '';
            $comissaoValue = $colunaComissaoExiste ? ', :comissao' : '';
            $comissaoPercentualField = $colunaComissaoPercentualExiste ? ', comissao_percentual' : '';
            $comissaoPercentualValue = $colunaComissaoPercentualExiste ? ', :comissao_percentual' : '';
            $profissionalIdField = $colunaProfissionalIdExiste ? ', profissional_id' : '';
            $profissionalIdValue = $colunaProfissionalIdExiste ? ', :profissional_id' : '';

            $sqlItens = "
                INSERT INTO vendas_itens (
                    company_id, venda_id, product_id, product_name, product_sku,
                    quantity, unit_price, discount, discount_type, total_price, notes,
                    ncm, cest, cfop, origem, cst_icms, aliquota_icms, valor_icms, base_calculo_icms,
                    cst_pis, aliquota_pis, valor_pis, base_calculo_pis,
                    cst_cofins, aliquota_cofins, valor_cofins, base_calculo_cofins,
                    cst_ipi, aliquota_ipi, valor_ipi, origem_st,
                    numero_serie, lote, fabricacao, validade{$vendedorIdField}{$comissaoField}{$comissaoPercentualField}{$profissionalIdField}
                ) VALUES (
                    :company_id, :venda_id, :product_id, :product_name, :product_sku,
                    :quantity, :unit_price, :discount, :discount_type, :total_price, :notes,
                    :ncm, :cest, :cfop, :origem, :cst_icms, :aliquota_icms, :valor_icms, :base_calculo_icms,
                    :cst_pis, :aliquota_pis, :valor_pis, :base_calculo_pis,
                    :cst_cofins, :aliquota_cofins, :valor_cofins, :base_calculo_cofins,
                    :cst_ipi, :aliquota_ipi, :valor_ipi, :origem_st,
                    :numero_serie, :lote, :fabricacao, :validade{$vendedorIdValue}{$comissaoValue}{$comissaoPercentualValue}{$profissionalIdValue}
                )
            ";

            $stmtInsertItem = $this->db->prepare($sqlItens);

            foreach ($itens as $item) {
                $params = [
                    'company_id' => $companyId,
                    'venda_id' => $novaVendaId,
                    'product_id' => $item['product_id'] ?: null,
                    'product_name' => $item['product_name'] ?? '',
                    'product_sku' => $item['product_sku'] ?? '',
                    'quantity' => $item['quantity'] ?? 0,
                    'unit_price' => $item['unit_price'] ?? 0,
                    'discount' => $item['discount'] ?? 0,
                    'discount_type' => $item['discount_type'] ?? 'fixed',
                    'total_price' => $item['total_price'] ?? 0,
                    'notes' => $item['notes'] ?? null,
                    'ncm' => $item['ncm'] ?? null,
                    'cest' => $item['cest'] ?? null,
                    'cfop' => $item['cfop'] ?? null,
                    'origem' => $item['origem'] ?? null,
                    'cst_icms' => $item['cst_icms'] ?? null,
                    'aliquota_icms' => $item['aliquota_icms'] ?? 0,
                    'valor_icms' => $item['valor_icms'] ?? 0,
                    'base_calculo_icms' => $item['base_calculo_icms'] ?? 0,
                    'cst_pis' => $item['cst_pis'] ?? null,
                    'aliquota_pis' => $item['aliquota_pis'] ?? 0,
                    'valor_pis' => $item['valor_pis'] ?? 0,
                    'base_calculo_pis' => $item['base_calculo_pis'] ?? 0,
                    'cst_cofins' => $item['cst_cofins'] ?? null,
                    'aliquota_cofins' => $item['aliquota_cofins'] ?? 0,
                    'valor_cofins' => $item['valor_cofins'] ?? 0,
                    'base_calculo_cofins' => $item['base_calculo_cofins'] ?? 0,
                    'cst_ipi' => $item['cst_ipi'] ?? null,
                    'aliquota_ipi' => $item['aliquota_ipi'] ?? 0,
                    'valor_ipi' => $item['valor_ipi'] ?? 0,
                    'origem_st' => $item['origem_st'] ?? null,
                    'numero_serie' => $item['numero_serie'] ?? null,
                    'lote' => $item['lote'] ?? null,
                    'fabricacao' => $item['fabricacao'] ?? null,
                    'validade' => $item['validade'] ?? null,
                ];

                if ($colunaVendedorIdExiste) {
                    $params['vendedor_id'] = $item['vendedor_id'] ?? null;
                }
                if ($colunaComissaoExiste) {
                    $params['comissao'] = $item['comissao'] ?? null;
                }
                if ($colunaComissaoPercentualExiste) {
                    $params['comissao_percentual'] = $item['comissao_percentual'] ?? null;
                }
                if ($colunaProfissionalIdExiste) {
                    $params['profissional_id'] = $item['profissional_id'] ?? null;
                }

                $stmtInsertItem->execute($params);
            }

            // Duplicar informações de pagamento
            $stmtPagamentos = $this->db->prepare("
                SELECT * FROM vendas_pagamentos
                WHERE venda_id = :venda_id
            ");
            $stmtPagamentos->execute(['venda_id' => $id]);
            $pagamentos = $stmtPagamentos->fetchAll();

            if (!empty($pagamentos)) {
                // Verificar estrutura da tabela para usar os campos corretos
                $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas_pagamentos");
                $colunas = $stmtCheck->fetchAll(\PDO::FETCH_COLUMN);

                // Verificar se usa estrutura antiga (payment_method_id) ou nova (metodo_pagamento_id)
                $usaMetodoPagamentoId = in_array('metodo_pagamento_id', $colunas);
                $usaNumeroParcela = in_array('numero_parcela', $colunas);

                if ($usaMetodoPagamentoId && $usaNumeroParcela) {
                    // Estrutura nova: usa metodo_pagamento_id, numero_parcela, etc.
                    $stmtInsertPagamento = $this->db->prepare("
                        INSERT INTO vendas_pagamentos (
                            company_id, venda_id, numero_parcela, total_parcelas, valor_parcela,
                            data_vencimento, metodo_pagamento_id, payment_term_id, dias_prazo,
                            status, created_at, updated_at
                        ) VALUES (
                            :company_id, :venda_id, :numero_parcela, :total_parcelas, :valor_parcela,
                            :data_vencimento, :metodo_pagamento_id, :payment_term_id, :dias_prazo,
                            'pendente', NOW(), NOW()
                        )
                    ");

                    foreach ($pagamentos as $pagamento) {
                        // Calcular nova data de vencimento (a partir de hoje)
                        $dataVencimento = new \DateTime();
                        $diasPrazo = $pagamento['dias_prazo'] ?? 30;
                        if (!empty($pagamento['data_vencimento'])) {
                            // Calcular diferença de dias da data original
                            $dataOriginal = new \DateTime($pagamento['data_vencimento']);
                            $hoje = new \DateTime();
                            $diasPrazo = $hoje->diff($dataOriginal)->days;
                            if ($diasPrazo < 0) {
                                $diasPrazo = 30; // Se a data já passou, usar 30 dias
                            }
                        }
                        $dataVencimento->modify("+{$diasPrazo} days");

                        $stmtInsertPagamento->execute([
                            'company_id' => $companyId,
                            'venda_id' => $novaVendaId,
                            'numero_parcela' => $pagamento['numero_parcela'] ?? 1,
                            'total_parcelas' => $pagamento['total_parcelas'] ?? 1,
                            'valor_parcela' => $pagamento['valor_parcela'] ?? $pagamento['value'] ?? 0,
                            'data_vencimento' => $dataVencimento->format('Y-m-d'),
                            'metodo_pagamento_id' => $pagamento['metodo_pagamento_id'] ?? $pagamento['payment_method_id'] ?? null,
                            'payment_term_id' => $pagamento['payment_term_id'] ?? null,
                            'dias_prazo' => $diasPrazo,
                        ]);
                    }
                } else {
                    // Estrutura antiga ou alternativa - tentar inserir apenas campos básicos
                    // Não duplicar pagamentos se a estrutura não for conhecida
                    error_log("Estrutura de vendas_pagamentos não reconhecida. Pagamentos não serão duplicados.");
                }
            }

            $this->logActivity('create', 'vendas', $novaVendaId, [
                'action' => 'duplicar',
                'venda_original_id' => $id,
                'venda_original_numero' => $vendaOriginal['sale_number']
            ]);

            $this->db->commit();

            $this->success($this->moduleSingular() . ' duplicada com sucesso', [
                'id' => $novaVendaId,
                'sale_number' => $proximoNumero
            ]);

        } catch (Exception $e) {
            if ($this->db && $this->db->inTransaction()) {
                $this->db->rollBack();
            }
            error_log("Erro ao duplicar venda: " . $e->getMessage());
            error_log("Stack trace: " . $e->getTraceAsString());
            $this->error('Erro ao duplicar ' . mb_strtolower($this->moduleSingular()) . ': ' . $e->getMessage());
        }
    }
}
