<?php

declare(strict_types=1);

namespace App\Controllers;

use Exception;

class OrdemServicoController extends BaseController
{
    protected function moduleOrigin(): string
    {
        return 'os';
    }
    public function index(): void
    {
        // Verificar permissão de visualização
        if (!$this->canView('ordem-servico')) {
            $this->response->forbidden('Você não tem permissão para visualizar ordens de serviço.');
            return;
        }

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

            $query = "
                SELECT v.*,
                    COALESCE(p.name, v.customer_name, 'Consumidor Final') as customer_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
                  AND v.modulo_origem = 'os'
            ";

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

            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 vendas para o filtro
            $stmt = $this->db->prepare("
                SELECT codigo, emite_nfe
                FROM modulo_status
                WHERE company_id = :company_id
                  AND modulo = 'vendas'
                  AND ativo = 1
            ");
            $stmt->execute(['company_id' => $companyId]);
            $statusVendas = $stmt->fetchAll();

            $emiteMap = [];
            foreach ($statusVendas as $statusLinha) {
                $codigo = $statusLinha['codigo'] ?? '';
                if ($codigo !== '') {
                    $emiteMap[$codigo] = !empty($statusLinha['emite_nfe']) && (int) $statusLinha['emite_nfe'] === 1;
                }
            }

            $emitirPorStatus = [];
            foreach ($vendas as $venda) {
                $codigoStatus = $venda['status'] ?? '';
                $emitirPorStatus[$venda['id']] = $codigoStatus !== '' && !empty($emiteMap[$codigoStatus]);
            }

            $this->view('ordem-servico/index', [
                'ordens' => $vendas,
                'search' => $search,
                'status' => $status,
                'podeEmitirNfePorVenda' => $emitirPorStatus,
                'pageTitle' => 'Ordens de Serviço',
                'activeMenu' => 'ordem-servico'
            ]);

        } catch (Exception $e) {
            error_log("Erro ao carregar ordens de serviço: " . $e->getMessage());
            error_log("Stack trace: " . $e->getTraceAsString());
            $this->error('Erro ao carregar ordens de serviço: ' . $e->getMessage());
        }
    }

    public function create(): void
    {
        try {
            $companyId = $this->getCompanyId();

            // Busca clientes
            $stmt = $this->db->prepare("
                SELECT id, name, trade_name, document, phone, mobile, email FROM pessoas
                WHERE company_id = :company_id AND type IN ('cliente', 'ambos') AND is_active = 1
                ORDER BY name ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $clientes = $stmt->fetchAll();

            // Busca produtos com dados fiscais
            // Se grupo_tributacao_id estiver populado, busca dados de impostos
            // Caso contrário, usa os dados direto da tabela produtos
            $stmt = $this->db->prepare("
                SELECT
                    p.id,
                    p.name,
                    p.sku,
                    p.price,
                    p.stock_quantity,
                    p.unit,
                    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.origem_mercadoria, p.origem, '0') as origem_st
                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->execute(['company_id' => $companyId]);
            $produtos = $stmt->fetchAll();

            // 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();

            // 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 = 'vendas'
                  AND ativo = 1
                ORDER BY ordem ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $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;
                }
            }

            $this->view('ordem-servico/create', [
                'clientes' => $clientes,
                'produtos' => $produtos,
                'metodosPagamento' => $metodosPagamento,
                'vendedores' => $vendedores,
                'statusVendas' => $statusVendas,
                'statusOrdens_servico' => $statusVendas,
                'statusPadrao' => $statusPadrao,
                'proximoNumero' => $proximoNumero,
                'pageTitle' => 'Nova Ordem de Serviço',
                'activeMenu' => 'ordem-servico'
            ]);

        } catch (Exception $e) {
            error_log("Erro ao carregar formulário de venda: " . $e->getMessage());
            $this->error('Erro ao carregar formulário');
        }
    }

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

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

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

            error_log("Nova venda: Número gerado automaticamente: {$proximoNumero} (último: " . ($ultimaVenda['sale_number'] ?? 'nenhum') . ")");

            $saleNumberRequest = $this->request->post('sale_number') ?: $this->request->post('service_number');
            $saleDateRequest = $this->request->post('sale_date') ?: $this->request->post('service_date');

            $data = [
                'company_id' => $companyId,
                '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' => $saleNumberRequest ?: $proximoNumero, // Usar número gerado automaticamente
                'sale_date' => $saleDateRequest ?: 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' => $this->request->post('generate_receivables') ? 1 : 0,
                'update_stock' => $this->request->post('update_stock') ? 1 : 0,
            ];

            // 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;
                }
            }

            $this->db->beginTransaction();

            $stmt = $this->db->prepare("
                INSERT INTO vendas (
                    company_id, 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, 'os', :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']);
            if (!empty($itens)) {
                $this->salvarItens($vendaId, $itens, $statusCodigo, $updateStock);
            }

            // 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('Ordem de Serviço criada com sucesso', [
                'id' => $vendaId,
                'redirect' => '/ordem-servico'
            ]);

        } catch (Exception $e) {
            $this->db->rollBack();
            error_log("Erro ao criar venda: " . $e->getMessage());
            $this->error('Erro ao criar venda: ' . $e->getMessage());
        }
    }

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

        $stmt = $this->db->prepare("
            INSERT INTO vendas_itens (
                company_id, venda_id, product_id, product_name, product_sku,
                altura, largura, quantity, quantity2, unit_price, discount, discount_type, total_price, notes, imagem,
                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
            ) VALUES (
                :company_id, :venda_id, :product_id, :product_name, :product_sku,
                :altura, :largura, :quantity, :quantity2, :unit_price, :discount, :discount_type, :total_price, :notes, :imagem,
                :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
            )
        ");

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

            // Processar imagem se fornecida (base64)
            $imagemPath = null;
            if (!empty($item['imagem'])) {
                try {
                    $imagemPath = $this->salvarImagemBase64($item['imagem'], $vendaId, $companyId);
                } catch (Exception $e) {
                    error_log("Erro ao salvar imagem do item: " . $e->getMessage());
                    // Continua sem a imagem se der erro
                }
            }

            // 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;

            // quantity = M² (altura × largura), quantity2 = quantidade de itens
            $m2 = floatval($item['m2'] ?? ($item['altura'] ?? 0) * ($item['largura'] ?? 0));
            $quantity2 = floatval($item['quantity2'] ?? $item['quantity'] ?? 1);

            $stmt->execute([
                'company_id' => $companyId,
                'venda_id' => $vendaId,
                'product_id' => $item['product_id'] ?: null,
                'product_name' => $item['product_name'] ?? '',
                'product_sku' => $item['product_sku'] ?? '',
                'altura' => !empty($item['altura']) ? floatval($item['altura']) : null,
                'largura' => !empty($item['largura']) ? floatval($item['largura']) : null,
                'quantity' => $m2, // M² (altura × largura)
                'quantity2' => $quantity2, // Quantidade de itens
                '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,
                'imagem' => $imagemPath,
                // 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
            ]);
        }

        // 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) {
                $quantity2 = floatval($item['quantity2'] ?? $item['quantity'] ?? 0);
                return !empty($item['product_id']) && $quantity2 > 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 - :quantity2
            WHERE id = :product_id AND company_id = :company_id
        ");

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

    /**
     * Salva uma imagem em base64 como arquivo
     *
     * @param string $base64String String base64 da imagem (com ou sem prefixo data:image)
     * @param int $vendaId ID da venda/ordem de serviço
     * @param int $companyId ID da empresa
     * @return string|null Caminho relativo da imagem salva ou null em caso de erro
     */
    private function salvarImagemBase64(string $base64String, int $vendaId, int $companyId): ?string
    {
        try {
            // Remover prefixo data:image se existir
            if (preg_match('/^data:image\/(\w+);base64,/', $base64String, $matches)) {
                $imageType = $matches[1];
                $base64String = preg_replace('/^data:image\/\w+;base64,/', '', $base64String);
            } else {
                // Tentar detectar o tipo pela string base64
                $imageType = 'jpg'; // padrão
            }

            // Validar extensão
            $allowedTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
            if (!in_array(strtolower($imageType), $allowedTypes)) {
                $imageType = 'jpg'; // fallback
            }

            // Decodificar base64
            $imageData = base64_decode($base64String, true);
            if ($imageData === false) {
                throw new Exception('Erro ao decodificar imagem base64');
            }

            // Criar diretório se não existir
            $uploadDir = ROOT_PATH . '/storage/uploads/ordem-servico/' . $vendaId . '/';
            if (!is_dir($uploadDir)) {
                mkdir($uploadDir, 0755, true);
            }

            // Gerar nome único para o arquivo
            $filename = uniqid('img_', true) . '.' . $imageType;
            $filepath = $uploadDir . $filename;

            // Salvar arquivo
            if (file_put_contents($filepath, $imageData) === false) {
                throw new Exception('Erro ao salvar arquivo de imagem');
            }

            // Retornar caminho relativo
            return '/storage/uploads/ordem-servico/' . $vendaId . '/' . $filename;

        } catch (Exception $e) {
            error_log("Erro ao salvar imagem base64: " . $e->getMessage());
            return null;
        }
    }

    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 = "Venda #{$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 = "Venda #{$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++;
                                }
                            } 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, created_at
        ) VALUES (
            :company_id, :product_id, :origem_id, 'venda', 'saida', :quantity, :reason, 'venda', :reference_id, 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
            ];

            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('ordem-servico')) {
            $this->response->forbidden('Você não tem permissão para editar ordens de serviço.');
            return;
        }

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

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

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

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

            // Busca clientes
            $stmt = $this->db->prepare("
                SELECT id, name, trade_name, document, phone, mobile, email FROM pessoas
                WHERE company_id = :company_id AND type IN ('cliente', 'ambos') AND is_active = 1
                ORDER BY name ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $clientes = $stmt->fetchAll();

            // Busca produtos com dados fiscais
            // Se grupo_tributacao_id estiver populado, busca dados de impostos
            // Caso contrário, usa os dados direto da tabela produtos
            $stmt = $this->db->prepare("
                SELECT
                    p.id,
                    p.name,
                    p.sku,
                    p.price,
                    p.stock_quantity,
                    p.unit,
                    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.origem_mercadoria, p.origem, '0') as origem_st
                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->execute(['company_id' => $companyId]);
            $produtos = $stmt->fetchAll();

            // 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() ?: [];

            // 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 = 'vendas'
                  AND ativo = 1
                ORDER BY ordem ASC
            ");
            $stmt->execute(['company_id' => $companyId]);
            $statusVendas = $stmt->fetchAll() ?: [];

            // Busca itens da venda (vendas.id = vendas_itens.venda_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,
                    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();

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

            error_log("Preparando dados para a view...");
            $ordem = $this->mapVendaParaOrdem($venda);

            $statusPadrao = !empty($statusVendas) ? $statusVendas[0]['codigo'] : ($venda['status'] ?? 'orcamento');
            foreach ($statusVendas as $status) {
                if (!empty($status['is_default'])) {
                    $statusPadrao = $status['codigo'];
                    break;
                }
            }

            $viewData = [
                'ordem' => $ordem,
                'itens' => $itens,
                'clientes' => $clientes,
                'produtos' => $produtos,
                'metodosPagamento' => $metodosPagamento,
                'vendedores' => $vendedores,
                'statusVendas' => $statusVendas,
                'statusOrdens_servico' => $statusVendas,
                'statusPadrao' => $statusPadrao,
                'pageTitle' => 'Editar Ordem de Serviço #' . ($ordem['service_number'] ?? $venda['sale_number']),
                'activeMenu' => 'ordem-servico'
            ];

            error_log("Chamando view com " . count($viewData) . " parâmetros");
            $this->view('ordem-servico/edit', $viewData);

        } catch (Exception $e) {
            error_log("Erro ao editar venda: " . $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('Venda 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('ordem-servico/show', [
                'venda' => $venda,
                'itens' => $itens,
                'pageTitle' => 'Venda #' . $venda['sale_number'],
                'activeMenu' => 'ordem-servico'
            ]);

        } catch (Exception $e) {
            error_log("Erro ao visualizar venda: " . $e->getMessage());
            $this->error('Erro ao carregar venda: ' . $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 "Venda 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 com imagens (prioriza imagem do item, depois do produto)
            $stmt = $this->db->prepare("
                SELECT vi.*,
                    COALESCE(vi.imagem, p.image) as product_image
                FROM vendas_itens vi
                LEFT JOIN produtos p ON vi.product_id = p.id
                WHERE vi.venda_id = :venda_id
                ORDER BY vi.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();
            }

            // Preparar dados no formato esperado pela view
            $ordem = $this->mapVendaParaOrdem($venda);

            $this->view('ordem-servico/print', [
                'ordem' => $ordem,
                'venda' => $venda,
                'itens' => $itens,
                'empresa' => $empresa,
                'cliente' => $cliente,
                'metodoPagamento' => $metodoPagamento,
                'vendedor' => $vendedor
            ]);

        } catch (Exception $e) {
            error_log("Erro ao imprimir venda: " . $e->getMessage());
            echo "Erro ao gerar impressão: " . $e->getMessage();
        }
    }

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

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

            if (!$venda) {
                echo "Venda 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();

            // Buscar itens da venda com imagens (prioriza imagem do item, depois do produto)
            $stmt = $this->db->prepare("
                SELECT vi.*,
                    COALESCE(vi.imagem, p.image) as product_image
                FROM vendas_itens vi
                LEFT JOIN produtos p ON vi.product_id = p.id
                WHERE vi.venda_id = :venda_id
                ORDER BY vi.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();
            }

            // Preparar dados no formato esperado pela view
            $ordem = $this->mapVendaParaOrdem($venda);

            $this->view('ordem-servico/print-producao', [
                'ordem' => $ordem,
                'venda' => $venda,
                'itens' => $itens,
                'empresa' => $empresa,
                'cliente' => $cliente
            ]);

        } catch (Exception $e) {
            error_log("Erro ao imprimir venda para produção: " . $e->getMessage());
            echo "Erro ao gerar impressão: " . $e->getMessage();
        }
    }

    public function enviarEmail(): void
    {
        try {
            // Aceitar tanto venda_id quanto ordem_servico_id
            $vendaId = (int) ($this->request->post('venda_id') ?: $this->request->post('ordem_servico_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' => 'ID da ordem de serviço e email são obrigatórios'
                ]);
                return;
            }

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

            if (!$venda) {
                $this->json([
                    'success' => false,
                    'message' => 'Venda 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 com imagens (prioriza imagem do item, depois do produto)
            $stmt = $this->db->prepare("
                SELECT vi.*,
                    COALESCE(vi.imagem, p.image) as product_image
                FROM vendas_itens vi
                LEFT JOIN produtos p ON vi.product_id = p.id
                WHERE vi.venda_id = :venda_id
                ORDER BY vi.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();
            }

            // Preparar dados no formato esperado pela view
            $ordem = $this->mapVendaParaOrdem($venda);

            // Preparar variáveis para a view
            $viewPath = \ROOT_PATH . '/views/ordem-servico/print.php';

            // Gerar HTML do pedido usando a view de ordem-servico
            ob_start();
            if (file_exists($viewPath)) {
                // Variáveis disponíveis na view
                $pageTitle = 'Ordem de Serviço';
                $activeMenu = 'ordem-servico';
                include $viewPath;
            } else {
                // Fallback para vendas/print.php se ordem-servico/print.php não existir
                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);
                }

                $numeroOrdem = $ordem['service_number'] ?? $venda['sale_number'] ?? '';
                $pdfFilename = 'ordem_servico_' . $numeroOrdem . '_' . 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);
            $numeroOrdem = $ordem['service_number'] ?? $venda['sale_number'] ?? '';
            $mail->Subject = $assunto ?: "Ordem de Serviço {$numeroOrdem}";

            $htmlBody = "<html><body>";
            $htmlBody .= "<p>Olá,</p>";
            if ($mensagem) {
                $htmlBody .= "<p>" . nl2br(htmlspecialchars($mensagem)) . "</p>";
            }
            $htmlBody .= "<p>Segue em anexo a ordem de serviço <strong>{$numeroOrdem}</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('ordem-servico')) {
            $this->response->forbidden('Você não tem permissão para editar ordens de serviço.');
            return;
        }

        try {
            error_log("=== INÍCIO UPDATE VENDA ===");
            error_log("POST recebido: " . json_encode($_POST));

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

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

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

            if (!$venda) {
                error_log("ERRO: Venda não encontrada para ID: {$id}");
                $this->error('Venda 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 venda #{$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;
            }

            $saleNumberPost = $this->request->post('sale_number') ?: $this->request->post('service_number');
            $saleDatePost = $this->request->post('sale_date') ?: $this->request->post('service_date');

            $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' => $saleNumberPost ?: $venda['sale_number'], // Fallback para venda existente
                'sale_date' => $saleDatePost ?: ($venda['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' => $this->request->post('vendor_id') ?: null,
                'auxiliary_vendor_id' => $this->request->post('auxiliary_vendor_id') ?: null,
                'generate_receivables' => $this->request->post('generate_receivables') ? 1 : 0, // Incluir checkbox
                '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
            ]));

            // 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...");

            // 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'],
            ];

            $stmt = $this->db->prepare("
                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,
                    updated_at = NOW()
                WHERE id = :id AND company_id = :company_id
            ");
            $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 = "Venda #{$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('Ordem de Serviço atualizada com sucesso', [
                'redirect' => '/ordem-servico'
            ]);

        } 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('ordem-servico')) {
            $this->response->forbidden('Você não tem permissão para excluir ordens de serviço.');
            return;
        }

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

            if (!$venda) {
                $this->error('Venda 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");
            $stmt->execute(['id' => $id, 'company_id' => $this->getCompanyId()]);

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

            $this->success('Venda excluída com sucesso');

        } catch (Exception $e) {
            $this->db->rollBack();
            error_log("Erro ao excluir venda: " . $e->getMessage());
            $this->error('Erro ao excluir venda');
        }
    }

    private function getVenda(int $id, ?string $modulo = null): ?array
    {
        $params = ['id' => $id, 'company_id' => $this->getCompanyId()];
        $filtroModulo = '';
        if ($modulo !== null) {
            $filtroModulo = ' AND modulo_origem = :modulo';
            $params['modulo'] = $modulo;
        }

        $stmt = $this->db->prepare("SELECT * FROM vendas WHERE id = :id AND company_id = :company_id{$filtroModulo}");
        $stmt->execute($params);
        return $stmt->fetch() ?: null;
    }

    private function mapVendaParaOrdem(array $venda): array
    {
        $ordem = $venda;

        $mapeamentos = [
            'service_number' => 'sale_number',
            'service_date' => 'sale_date',
        ];

        foreach ($mapeamentos as $destino => $origem) {
            if (!array_key_exists($destino, $ordem) && array_key_exists($origem, $venda)) {
                $ordem[$destino] = $venda[$origem];
            } elseif (array_key_exists($origem, $venda)) {
                $ordem[$destino] = $venda[$origem];
            }
        }

        return $ordem;
    }

    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 {
            $vendaId = (int) ($this->request->post('venda_id') ?: $this->request->post('ordem_servico_id'));
            $companyId = $this->getCompanyId();

            if ($vendaId <= 0) {
                $this->error('Ordem de serviço inválida');
                return;
            }

            // Buscar dados da venda
            $venda = $this->getVenda($vendaId, $this->moduleOrigin());
            if (!$venda) {
                $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->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(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");
            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));

            // Buscar dados da empresa
            $empresa = $this->buscarDadosEmpresa($companyId);
            if (!$empresa) {
                $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'));

            // 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);

            // Log dos itens encontrados
            error_log("NFe #{$vendaId}: Total de itens encontrados no banco: " . count($itens));

            // 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'] ?? []));

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

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

                // 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})");

                // 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}");

                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!");
            }

            // 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->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) {
            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 buscarDadosEmpresa(int $companyId): ?array
    {
        // Prioriza a empresa vinculada ao usuário atual
        $stmt = $this->db->prepare("
            SELECT * FROM empresas
            WHERE id = :id OR (company_id IS NOT NULL AND company_id = :company_id)
            LIMIT 1
        ");
        $stmt->execute(['id' => $companyId, 'company_id' => $companyId]);
        $empresa = $stmt->fetch();

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

        if (!$empresa) {
            // Último fallback: tabela companies
            $stmt = $this->db->prepare("
                SELECT * FROM companies
                WHERE certificado_path IS NOT NULL AND certificado_path != '' AND certificado_path != 'NULL'
                LIMIT 1
            ");
            $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'] ?? '');

        $dados = [
            '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']
                            : 'ISENTO'))),
                '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
            ],
            'cliente' => [
                'nome' => $cliente ? ($cliente['name'] ?? $cliente['trade_name'] ?? '') : ($venda['customer_name'] ?? ''),
                'cnpj' => $cliente ? preg_replace('/\D/', '', $cliente['document'] ?? '') : preg_replace('/\D/', '', $venda['customer_document'] ?? ''),
                'inscricao_estadual' => $cliente['state_registration']
                    ?? $cliente['inscricao_estadual']
                    ?? $cliente['rg_ie']
                    ?? ($venda['customer_ie'] ?? ''),
                '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'] ?? ''
            ],
            '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)
            ];
        }

        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'));

            $nfeCancelada = !empty($venda['protocolo_cancelamento']) && !empty($venda['data_cancelamento']);
            if ($nfeCancelada && !empty($venda['pdf_cancelamento'])) {
                $pdfCancelamentoUrl = \App\Helpers\UrlHelper::url('/ordem-servico/visualizar-pdf-cancelamento?chave=' . urlencode($venda['chave_nfe']));
                error_log("Reimprimir NF-e: retorno de PDF de cancelamento via endpoint {$pdfCancelamentoUrl}");
                $this->success('PDF de cancelamento disponível', [
                    'pdf_path' => $pdfCancelamentoUrl,
                    'tipo' => 'cancelamento',
                    'chave_nfe' => $venda['chave_nfe']
                ]);
                return;
            }

            // 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());
        }
    }

    /**
     * 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') ?: $this->request->post('ordem_servico_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'] : 'ISENTO')),
                    '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}");

            // Atualizar campos disponíveis na tabela vendas
            $colunasVendas = $this->colunasTabela('vendas');
            $sets = [];
            $paramsUpdate = [
                'id' => $vendaId,
                'company_id' => $companyId,
            ];

            if (in_array('protocolo_cancelamento', $colunasVendas, true)) {
                $sets[] = 'protocolo_cancelamento = :protocolo_cancelamento';
                $paramsUpdate['protocolo_cancelamento'] = $protocoloCancelamento;
            }
            if (in_array('data_cancelamento', $colunasVendas, true)) {
                $sets[] = 'data_cancelamento = :data_cancelamento';
                $paramsUpdate['data_cancelamento'] = $dataCancelamento;
            }
            if (in_array('justificativa_cancelamento', $colunasVendas, true)) {
                $sets[] = 'justificativa_cancelamento = :justificativa_cancelamento';
                $paramsUpdate['justificativa_cancelamento'] = trim($justificativa);
            }
            if (in_array('pdf_cancelamento', $colunasVendas, true)) {
                $sets[] = 'pdf_cancelamento = :pdf_cancelamento';
                $paramsUpdate['pdf_cancelamento'] = $pdfCancelamento;
            }
            if (in_array('xml_cancelamento', $colunasVendas, true)) {
                $sets[] = 'xml_cancelamento = :xml_cancelamento';
                $paramsUpdate['xml_cancelamento'] = $xmlCancelamento;
            }

            if (!empty($sets)) {
                $sqlUpdate = sprintf(
                    'UPDATE vendas SET %s WHERE id = :id AND company_id = :company_id',
                    implode(', ', $sets)
                );
                $stmt = $this->db->prepare($sqlUpdate);
                $stmt->execute($paramsUpdate);
            }

            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'] : 'ISENTO')),
                    '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);
            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
            $cliente = $this->buscarDadosCliente($venda['customer_id']);

            // 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);
    }

    /**
     * Gera próximo número sequencial de ordem de serviço no formato OS-000001
     */
    private function gerarProximoNumeroVenda(int $companyId): string
    {
        // Tentar pegar o maior sufixo numérico de sale_number no padrão OS-xxxxxx
        // Buscar apenas ordens de serviço (modulo_origem = 'os')
        $stmt = $this->db->prepare("SELECT MAX(CAST(SUBSTRING(sale_number, 4) AS UNSIGNED)) AS maxnum
            FROM vendas
            WHERE company_id = :company_id AND modulo_origem = 'os' AND sale_number LIKE 'OS-%'");
        $stmt->execute(['company_id' => $companyId]);
        $row = $stmt->fetch();
        $max = (int) ($row['maxnum'] ?? 0);
        $next = $max + 1;
        $numero = 'OS-' . 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 = 'OS-' . str_pad((string) $next, 6, '0', STR_PAD_LEFT);
            $tentativas++;
        }

        return $numero;
    }
}
