<?php

namespace App\Controllers;

use Exception;
use App\Helpers\UrlHelper;

class PDVController extends BaseController
{
    public function login(): void
    {
        if ($this->session->get('pdv_user_id')) {
            $this->redirect('/pdv/caixa');
            return;
        }
        $this->view('pdv/login', ['pageTitle' => 'PDV - Login', 'hideLayout' => true]);
    }

    public function autenticar(): void
    {
        try {
            $email = $this->request->post('email');
            $password = $this->request->post('password');

            if (empty($email) || empty($password)) {
                $this->error('Email e senha sao obrigatorios');
                return;
            }

            $stmt = $this->db->prepare("SELECT id, name, email, password, company_id, role FROM users WHERE email = :email AND is_active = 1");
            $stmt->execute(['email' => $email]);
            $user = $stmt->fetch();

            if (!$user || !password_verify($password, $user['password'])) {
                $this->error('Email ou senha invalidos');
                return;
            }

            // Definir sessões do usuário primeiro
            $this->session->set('pdv_user_id', $user['id']);
            $this->session->set('pdv_user_name', $user['name']);
            $this->session->set('pdv_company_id', $user['company_id']);

            // Verificar se existe tabela de sessões
            $stmtCheck = $this->db->query("SHOW TABLES LIKE 'pdv_sessoes'");
            $tabelaExiste = $stmtCheck->fetch();
            $sessaoAberta = null;

            if ($tabelaExiste) {
                // Verificar se há caixa aberto para este usuário
                $stmt = $this->db->prepare("
                    SELECT id, valor_abertura, saldo_atual, created_at, status
                    FROM pdv_sessoes
                    WHERE user_id = :user_id
                    AND company_id = :company_id
                    AND status = 'aberto'
                    ORDER BY created_at DESC
                    LIMIT 1
                ");
                $stmt->execute(['user_id' => $user['id'], 'company_id' => $user['company_id']]);
                $sessaoAberta = $stmt->fetch();
            }

            if ($sessaoAberta && $sessaoAberta['status'] === 'aberto') {
                // Caixa está aberto - permitir acesso direto
                $this->session->set('pdv_sessao_id', $sessaoAberta['id']);
                $this->success('Login realizado com sucesso', [
                    'redirect' => '/pdv/caixa',
                    'sessao_aberta' => true,
                    'mensagem' => 'Caixa já está aberto'
                ]);
            } else {
                // Caixa não está aberto - redirecionar para abertura obrigatória
                $this->session->remove('pdv_sessao_id');
                $this->success('Login realizado com sucesso', [
                    'redirect' => '/pdv/abertura-caixa',
                    'sessao_aberta' => false,
                    'mensagem' => 'É necessário abrir o caixa antes de iniciar as vendas'
                ]);
            }
        } catch (\Exception $e) {
            error_log("Erro no login PDV: " . $e->getMessage());
            $this->error('Erro ao fazer login: ' . $e->getMessage());
        }
    }

    public function aberturaCaixa(): void
    {
        if (!$this->session->get('pdv_user_id')) {
            $this->redirect('/pdv/login');
            return;
        }
        $this->view('pdv/abertura-caixa', ['pageTitle' => 'Abertura de Caixa', 'hideLayout' => true, 'operador' => $this->session->get('pdv_user_name')]);
    }

    public function abrirCaixa(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Usuario nao autenticado');
                return;
            }

            $valorAbertura = (float) $this->request->post('valor_abertura', 0);
            $observacoes = $this->request->post('observacoes');
            $userId = $this->session->get('pdv_user_id');
            $companyId = $this->session->get('pdv_company_id');

            $stmt = $this->db->prepare("SELECT id FROM pdv_sessoes WHERE user_id = :user_id AND company_id = :company_id AND status = 'aberto'");
            $stmt->execute(['user_id' => $userId, 'company_id' => $companyId]);

            if ($stmt->fetch()) {
                $this->error('Ja existe um caixa aberto para este usuario');
                return;
            }

            $this->db->beginTransaction();

            $stmt = $this->db->prepare("INSERT INTO pdv_sessoes (company_id, user_id, valor_abertura, saldo_atual, observacoes_abertura, status, created_at) VALUES (:company_id, :user_id, :valor_abertura, :saldo_atual, :observacoes, 'aberto', NOW())");
            $stmt->execute(['company_id' => $companyId, 'user_id' => $userId, 'valor_abertura' => $valorAbertura, 'saldo_atual' => $valorAbertura, 'observacoes' => $observacoes]);

            $sessaoId = (int) $this->db->lastInsertId();

            // Registrar movimento de abertura (tipo 'abertura', sem método de pagamento específico)
            $this->db->prepare("
                INSERT INTO pdv_movimentos
                (company_id, sessao_id, venda_id, tipo, valor, metodo_pagamento_id, observacoes, created_by, created_at)
                VALUES
                (:company_id, :sessao_id, NULL, 'abertura', :valor, NULL, :observacoes, :user_id, NOW())
            ")->execute([
                        'company_id' => $companyId,
                        'sessao_id' => $sessaoId,
                        'valor' => $valorAbertura,
                        'observacoes' => $observacoes ?: 'Abertura de caixa',
                        'user_id' => $userId
                    ]);

            $this->db->commit();
            $this->session->set('pdv_sessao_id', $sessaoId);
            $this->success('Caixa aberto com sucesso', [
                'redirect' => '/pdv/caixa',
                'sessao_id' => $sessaoId
            ]);
        } catch (Exception $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            error_log("Erro ao abrir caixa: " . $e->getMessage());
            $this->error('Erro ao abrir caixa: ' . $e->getMessage());
        }
    }

    public function caixa(): void
    {
        // Verificar autenticação primeiro
        if (!$this->session->get('pdv_user_id')) {
            $this->redirect('/pdv/login');
            return;
        }

        $userId = $this->session->get('pdv_user_id');
        $companyId = $this->session->get('pdv_company_id');

        // Verificar se existe tabela de sessões
        $stmtCheck = $this->db->query("SHOW TABLES LIKE 'pdv_sessoes'");
        $tabelaExiste = $stmtCheck->fetch();

        $sessao = null;
        if ($tabelaExiste) {
            // Buscar sessão aberta para este usuário
            $stmt = $this->db->prepare("
                SELECT * FROM pdv_sessoes
                WHERE user_id = :user_id
                AND company_id = :company_id
                AND status = 'aberto'
                ORDER BY created_at DESC
                LIMIT 1
            ");
            $stmt->execute(['user_id' => $userId, 'company_id' => $companyId]);
            $sessao = $stmt->fetch();
        }

        // Se não encontrou sessão aberta ou sessão não está com status 'aberto', redirecionar para abertura
        if (!$sessao || ($sessao['status'] ?? '') !== 'aberto') {
            $this->session->remove('pdv_sessao_id');
            $this->redirect('/pdv/abertura-caixa');
            return;
        }

        // Atualizar sessão com ID da sessão aberta
        $this->session->set('pdv_sessao_id', $sessao['id']);

        $stmtCheck = $this->db->query("SHOW COLUMNS FROM metodos_pagamento LIKE 'use_shipay'");
        $hasUseShipay = $stmtCheck->rowCount() > 0;

        $stmtCheckUsarPdv = $this->db->query("SHOW COLUMNS FROM metodos_pagamento LIKE 'usar_pdv'");
        $hasUsarPdv = $stmtCheckUsarPdv->rowCount() > 0;

        $selectFields = 'id, name, type';
        if ($hasUseShipay) {
            $selectFields .= ', use_shipay';
        }
        if ($hasUsarPdv) {
            $selectFields .= ', usar_pdv';
        }

        $whereClause = "company_id = :company_id AND is_active = 1";
        if ($hasUsarPdv) {
            $whereClause .= " AND usar_pdv = 1";
        }

        $stmt = $this->db->prepare("SELECT {$selectFields} FROM metodos_pagamento WHERE {$whereClause} ORDER BY name");
        $stmt->execute(['company_id' => $this->session->get('pdv_company_id')]);
        $metodosPagamento = $stmt->fetchAll();

        if (!$hasUseShipay) {
            foreach ($metodosPagamento as &$m) {
                $m['use_shipay'] = 0;
            }
        }

        // Buscar tabelas de preço
        $tabelasPreco = [];
        try {
            $stmtTabelas = $this->db->prepare("SELECT id, nome FROM tabela_preco WHERE company_id = :company_id AND ativo = 1 ORDER BY ordem ASC, nome ASC");
            $stmtTabelas->execute(['company_id' => $this->session->get('pdv_company_id')]);
            $tabelasPreco = $stmtTabelas->fetchAll();
        } catch (Exception $e) {
            error_log("Erro ao buscar tabelas de preço no PDV: " . $e->getMessage());
        }

        $this->view('pdv/caixa', [
            'pageTitle' => 'PDV - Caixa',
            'hideLayout' => true,
            'sessao' => $sessao,
            'metodosPagamento' => $metodosPagamento,
            'operador' => $this->session->get('pdv_user_name'),
            'tabelasPreco' => $tabelasPreco
        ]);
    }

    public function listarGrupos(): void
    {
        try {
            $companyId = $this->session->get('pdv_company_id');
            if (!$companyId) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $stmt = $this->db->prepare("SELECT g.id, g.nome FROM grupos_pessoas g WHERE g.company_id = :company_id AND g.tipo = 'itens' ORDER BY g.nome ASC LIMIT 50");
            $stmt->execute(['company_id' => $companyId]);
            $grupos = $stmt->fetchAll();

            error_log("[PDV listarGrupos] Company: {$companyId}, Grupos encontrados: " . count($grupos));
            $this->success('Grupos listados', ['grupos' => $grupos]);
        } catch (\Exception $e) {
            error_log("Erro ao listar grupos PDV: " . $e->getMessage());
            $this->error('Erro ao listar grupos: ' . $e->getMessage());
        }
    }

    /**
     * Lista vendas/orçamentos pendentes para reabrir no PDV
     */
    public function listarVendasPendentes(): void
    {
        try {
            $companyId = $this->session->get('pdv_company_id');
            if (!$companyId) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $sql = "
                SELECT
                    v.id,
                    v.sale_number,
                    v.sale_date,
                    v.total,
                    v.status,
                    p.name AS cliente_nome,
                    COUNT(vi.id) AS total_itens
                FROM vendas v
                LEFT JOIN pessoas p ON v.customer_id = p.id
                LEFT JOIN vendas_itens vi ON vi.venda_id = v.id
                WHERE v.company_id = :company_id
                  AND v.modulo_origem = 'pdv'
                  AND v.status = 'orcamento'
                GROUP BY v.id
                ORDER BY v.created_at DESC
                LIMIT 100
            ";

            $stmt = $this->db->prepare($sql);
            $stmt->execute(['company_id' => $companyId]);
            $vendas = $stmt->fetchAll(\PDO::FETCH_ASSOC) ?: [];

            $this->success('Vendas pendentes listadas', ['vendas' => $vendas]);
        } catch (\Exception $e) {
            error_log("[PDV listarVendasPendentes] Erro: " . $e->getMessage());
            $this->error('Erro ao listar vendas pendentes: ' . $e->getMessage());
        }
    }

    public function buscarProdutos(): void
    {
        try {
            $busca = $this->request->get('busca', '');
            $grupoId = $this->request->get('grupo_id', '');
            $companyId = $this->session->get('pdv_company_id');

            if (!$companyId) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $stmtCols = $this->db->query("SHOW COLUMNS FROM produtos");
            $colunasRaw = $stmtCols->fetchAll(\PDO::FETCH_ASSOC);
            $colunas = array_column($colunasRaw, 'Field');

            $hasIsActive = in_array('is_active', $colunas);
            $hasStockQuantity = in_array('stock_quantity', $colunas);
            $hasQuantidade = in_array('quantidade', $colunas);
            $hasName = in_array('name', $colunas);
            $hasNome = in_array('nome', $colunas);
            $hasSku = in_array('sku', $colunas);
            $hasCodigo = in_array('codigo', $colunas);
            $hasBarcode = in_array('barcode', $colunas);
            $hasCodigoBarra = in_array('codigo_barra', $colunas);

            $colunaEstoque = $hasStockQuantity ? 'stock_quantity' : ($hasQuantidade ? 'quantidade' : null);
            $colunaNome = $hasName ? 'name' : ($hasNome ? 'nome' : 'id');
            $colunaSku = $hasSku ? 'sku' : ($hasCodigo ? 'codigo' : null);
            $colunaBarcode = $hasBarcode ? 'barcode' : ($hasCodigoBarra ? 'codigo_barra' : null);

            $stmtCheckPreco = $this->db->query("SHOW TABLES LIKE 'produto_preco'");
            $temTabelaPreco = $stmtCheckPreco->rowCount() > 0;

            $whereClause = "p.company_id = :company_id";
            $params = ['company_id' => $companyId];

            if ($hasIsActive) {
                $whereClause .= " AND (p.is_active = 1 OR p.is_active IS NULL)";
            }

            if (!empty($grupoId) && $grupoId !== 'todos') {
                $whereClause .= " AND p.grupo_id = :grupo_id";
                $params['grupo_id'] = $grupoId;
            }

            $selectFields = ["p.id", "p.{$colunaNome} as name"];

            if ($colunaSku) {
                $selectFields[] = "COALESCE(p.{$colunaSku}, '') as sku";
            } else {
                $selectFields[] = "'' as sku";
            }

            if ($colunaBarcode) {
                $selectFields[] = "COALESCE(p.{$colunaBarcode}, '') as barcode";
            } else {
                $selectFields[] = "'' as barcode";
            }

            if ($temTabelaPreco) {
                $selectFields[] = "COALESCE((SELECT pp.valor FROM produto_preco pp WHERE pp.produto_id = p.id AND pp.company_id = p.company_id AND (pp.ativo = 1 OR pp.ativo IS NULL) ORDER BY pp.ordem ASC LIMIT 1), 0) as price";
            } else {
                $selectFields[] = "0 as price";
            }

            if ($colunaEstoque) {
                $selectFields[] = "COALESCE(p.{$colunaEstoque}, 0) as stock_quantity";
            } else {
                $selectFields[] = "0 as stock_quantity";
            }

            $selectFields[] = "p.grupo_id";

            // Adicionar campos fiscais do imposto (mesma lógica do VendasController)
            $selectFields[] = "COALESCE(i.ncm, p.ncm) as ncm";
            $selectFields[] = "COALESCE(i.cest, p.cest) as cest";
            $selectFields[] = "COALESCE(ic.cfop, i.cfop, p.cfop) as cfop";
            $selectFields[] = "COALESCE(ic.origem_mercadoria, i.origem_mercadoria, p.origem) as origem";
            $selectFields[] = "COALESCE(ic.cst_csosn, i.cst_csosn, p.cst_icms) as cst_icms";
            $selectFields[] = "COALESCE(ic.icms, i.icms, p.aliquota_icms) as aliquota_icms";
            $selectFields[] = "COALESCE(ic.cst_pis, i.cst_pis, p.cst_pis) as cst_pis";
            $selectFields[] = "COALESCE(ic.pis, i.pis, p.aliquota_pis) as aliquota_pis";
            $selectFields[] = "COALESCE(ic.cst_cofins, i.cst_cofins, p.cst_cofins) as cst_cofins";
            $selectFields[] = "COALESCE(ic.cofins, i.cofins, p.aliquota_cofins) as aliquota_cofins";
            $selectFields[] = "COALESCE(ic.cst_ipi, i.cst_ipi, p.cst_ipi) as cst_ipi";
            $selectFields[] = "COALESCE(ic.ipi, i.ipi, p.aliquota_ipi) as aliquota_ipi";

            $selectClause = implode(", ", $selectFields);
            $sql = "
                SELECT {$selectClause}
                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 {$whereClause}
                ORDER BY p.{$colunaNome} ASC
            ";

            $stmt = $this->db->prepare($sql);
            $stmt->execute($params);
            $todosProdutos = $stmt->fetchAll();

            if (!empty($busca)) {
                $buscaLower = strtolower($busca);
                $produtos = array_filter($todosProdutos, function ($p) use ($buscaLower, $busca) {
                    return stripos($p['name'] ?? '', $buscaLower) !== false
                        || stripos($p['sku'] ?? '', $buscaLower) !== false
                        || ($p['barcode'] ?? '') === $busca;
                });
                $produtos = array_values($produtos);
                $produtos = array_slice($produtos, 0, 5000);
            } else {
                $produtos = array_slice($todosProdutos, 0, 5000);
            }

            $produtos = array_map(function ($p) {
                return [
                    'id' => $p['id'],
                    'name' => $p['name'] ?? '',
                    'sku' => $p['sku'] ?? '',
                    'barcode' => $p['barcode'] ?? '',
                    'preco' => (float) ($p['price'] ?? 0),
                    'estoque' => (int) ($p['stock_quantity'] ?? 0)
                ];
            }, $produtos);

            $this->success('Produtos encontrados', ['produtos' => $produtos]);
        } catch (\Exception $e) {
            error_log("Erro ao buscar produtos PDV: " . $e->getMessage());
            $this->error('Erro ao buscar produtos: ' . $e->getMessage());
        }
    }

    public function processarVenda(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $userId = $this->session->get('pdv_user_id');
            $sessaoId = $this->session->get('pdv_sessao_id');

            // Itens podem vir como array ou como string JSON
            $itensRaw = $this->request->post('itens');
            if (is_string($itensRaw)) {
                $itens = json_decode($itensRaw, true);
            } else {
                $itens = is_array($itensRaw) ? $itensRaw : [];
            }

            if (empty($itens)) {
                $this->error('Nenhum item na venda');
                return;
            }

            $subtotal = (float) $this->request->post('subtotal', 0);
            $desconto = (float) $this->request->post('desconto', 0);
            $total = (float) $this->request->post('total', 0);
            $clienteId = $this->request->post('cliente_id') ?: null;
            $metodoPagamentoId = (int) $this->request->post('metodo_pagamento_id');
            $parcelas = (int) $this->request->post('parcelas', 1);
            $valorRecebidoSingle = (float) $this->request->post('valor_recebido', $total);

            $pixOrderIdRaw = $this->request->post('pix_order_id');
            $pixOrderId = (!empty($pixOrderIdRaw) && $pixOrderIdRaw !== 'null' && $pixOrderIdRaw !== 'undefined') ? $pixOrderIdRaw : null;

            // Dados adicionais do PIX Shipay
            $pixQrCodeRaw = $this->request->post('pix_qrcode');
            $pixQrCode = (!empty($pixQrCodeRaw) && $pixQrCodeRaw !== 'null' && $pixQrCodeRaw !== 'undefined') ? $pixQrCodeRaw : null;

            $pixPayloadRaw = $this->request->post('pix_payload');
            $pixPayload = (!empty($pixPayloadRaw) && $pixPayloadRaw !== 'null' && $pixPayloadRaw !== 'undefined') ? $pixPayloadRaw : null;

            // Pagamentos podem vir como array (múltiplos) ou JSON
            $pagamentosRaw = $this->request->post('pagamentos');
            if (is_string($pagamentosRaw)) {
                $pagamentos = json_decode($pagamentosRaw, true);
            } elseif (is_array($pagamentosRaw)) {
                $pagamentos = $pagamentosRaw;
            } else {
                $pagamentos = [];
            }
            foreach ($pagamentos as &$pg) {
                if (!isset($pg['metodo_pagamento_id']) && isset($pg['metodo_id'])) {
                    $pg['metodo_pagamento_id'] = $pg['metodo_id'];
                }
            }
            unset($pg);

            if (empty($pagamentos)) {
                if (!$metodoPagamentoId) {
                    $this->error('Metodo de pagamento obrigatorio');
                    return;
                }

                $pagamentos[] = [
                    'metodo_pagamento_id' => $metodoPagamentoId,
                    'valor' => $total,
                    'valor_recebido' => $valorRecebidoSingle,
                    'parcelas' => $parcelas,
                    'pix_order_id' => $pixOrderId,
                    'pix_qrcode' => $pixQrCode,
                    'pix_payload' => $pixPayload
                ];
            }

            // Validar valores dos pagamentos
            $valorPagamentos = 0;
            $valorRecebidoTotal = 0;
            foreach ($pagamentos as $pg) {
                $valorPg = (float) ($pg['valor'] ?? 0);
                $valorRecebidoPg = (float) ($pg['valor_recebido'] ?? $valorPg);
                $valorPagamentos += $valorPg;
                $valorRecebidoTotal += $valorRecebidoPg;
            }

            if ($valorPagamentos <= 0) {
                $this->error('Valor de pagamento invalido');
                return;
            }

            if ($valorPagamentos + 0.01 < $total) {
                $faltante = number_format($total - $valorPagamentos, 2, ',', '.');
                $this->error("Pagamentos insuficientes. Falta R$ {$faltante}");
                return;
            }

            $trocoTotal = $valorRecebidoTotal > $total ? $valorRecebidoTotal - $total : 0;

            // Usar primeiro método para referencia
            $metodoPagamentoId = (int) ($pagamentos[0]['metodo_pagamento_id'] ?? $pagamentos[0]['metodo_id'] ?? $metodoPagamentoId);
            $pixOrderId = $pixOrderId ?: ($pagamentos[0]['pix_order_id'] ?? null);
            $pixQrCode = $pixQrCode ?: ($pagamentos[0]['pix_qrcode'] ?? null);
            $pixPayload = $pixPayload ?: ($pagamentos[0]['pix_payload'] ?? null);

            // Verificar se a venda já foi processada pelo webhook (evitar duplicação)
            if ($pixOrderId) {
                $stmtCheck = $this->db->prepare("
                    SELECT v.id, v.sale_number, v.status, v.total
                    FROM vendas v
                    INNER JOIN vendas_pagamentos vp ON vp.venda_id = v.id
                    WHERE vp.shipay_order_id = :pix_order_id
                    AND v.company_id = :company_id
                    AND v.status = 'finalizado'
                    LIMIT 1
                ");
                $stmtCheck->execute(['pix_order_id' => $pixOrderId, 'company_id' => $companyId]);
                $vendaExistente = $stmtCheck->fetch();

                if ($vendaExistente) {
                    // Venda já foi processada pelo webhook - retornar dados existentes
                    error_log("[PDV processarVenda] Venda já processada pelo webhook: #{$vendaExistente['id']} (order_id: {$pixOrderId})");
                    $this->success('Venda já foi finalizada', [
                        'venda' => [
                            'id' => $vendaExistente['id'],
                            'sale_number' => $vendaExistente['sale_number'],
                            'status' => $vendaExistente['status']
                        ],
                        'troco' => $trocoTotal,
                        'ja_processada' => true
                    ]);
                    return;
                }
            }

            $this->db->beginTransaction();

            $stmt = $this->db->prepare("SELECT COALESCE(MAX(CAST(SUBSTRING(sale_number, 5) AS UNSIGNED)), 0) + 1 as proximo FROM vendas WHERE company_id = :company_id AND sale_number LIKE 'PDV-%'");
            $stmt->execute(['company_id' => $companyId]);
            $proximo = $stmt->fetch()['proximo'];
            $saleNumber = 'PDV-' . str_pad($proximo, 6, '0', STR_PAD_LEFT);

            // Verificar se há pagamento PIX Shipay pendente - se houver, venda fica como 'orcamento'
            $temPixPendente = false;
            foreach ($pagamentos as $pg) {
                if (!empty($pg['pix_order_id'])) {
                    $temPixPendente = true;
                    break;
                }
            }

            $statusVenda = $temPixPendente ? 'orcamento' : 'finalizado';

            $stmt = $this->db->prepare("INSERT INTO vendas (company_id, customer_id, sale_number, sale_date, subtotal, discount, total, status, payment_method_id, vendor_id, modulo_origem, notes, created_at, updated_at) VALUES (:company_id, :customer_id, :sale_number, NOW(), :subtotal, :discount, :total, :status, :payment_method_id, :vendor_id, 'pdv', 'PDV', NOW(), NOW())");
            $stmt->execute(['company_id' => $companyId, 'customer_id' => $clienteId, 'sale_number' => $saleNumber, 'subtotal' => $subtotal, 'discount' => $desconto, 'total' => $total, 'status' => $statusVenda, 'payment_method_id' => $metodoPagamentoId, 'vendor_id' => $userId]);

            $vendaId = (int) $this->db->lastInsertId();

            // Preparar INSERT com campos fiscais (mesmo padrão do módulo de Vendas)
            $sqlItem = "
                INSERT INTO vendas_itens (
                    company_id, venda_id, product_id, product_name, quantity, unit_price, discount, total_price,
                    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,
                    created_at
                ) VALUES (
                    :company_id, :venda_id, :product_id, :product_name, :quantity, :unit_price, :discount, :total_price,
                    :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,
                    NOW()
                )
            ";

            $stmtItem = $this->db->prepare($sqlItem);

            $descontoRestante = $desconto;
            $totalItens = count($itens);
            $itemAtual = 0;

            foreach ($itens as $item) {
                $itemAtual++;
                $valorItemOriginal = (float) $item['total'];
                $descontoItem = 0;

                if ($subtotal > 0 && $desconto > 0) {
                    if ($itemAtual === $totalItens) {
                        $descontoItem = $descontoRestante;
                    } else {
                        $descontoItem = round(($valorItemOriginal / $subtotal) * $desconto, 2);
                        $descontoRestante -= $descontoItem;
                    }
                }

                $totalPriceItem = $valorItemOriginal - $descontoItem;

                // Calcular valores dos impostos (mesmo padrão do VendasController)
                $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'] ?? $totalPriceItem);
                $baseCalculoPis = floatval($item['base_calculo_pis'] ?? $totalPriceItem);
                $baseCalculoCofins = floatval($item['base_calculo_cofins'] ?? $totalPriceItem);

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

                $paramsItem = [
                    'company_id' => $companyId,
                    'venda_id' => $vendaId,
                    'product_id' => $item['id'],
                    'product_name' => $item['nome'],
                    'quantity' => $item['quantidade'],
                    'unit_price' => $item['preco'],
                    'discount' => $descontoItem,
                    'total_price' => $totalPriceItem,
                    // Campos fiscais: vêm do carrinho ou ficam NULL
                    'ncm' => $item['ncm'] ?? null,
                    'cest' => $item['cest'] ?? null,
                    'cfop' => $item['cfop'] ?? null,
                    'origem' => $item['origem'] ?? null,
                    'cst_icms' => $item['cst_icms'] ?? null,
                    'aliquota_icms' => $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,
                ];

                $stmtItem->execute($paramsItem);

                // Só baixar estoque se a venda estiver finalizada (não é orçamento)
                if ($statusVenda === 'finalizado') {
                    $this->db->prepare("UPDATE produtos SET stock_quantity = stock_quantity - :qtd WHERE id = :id AND company_id = :company_id")->execute(['qtd' => $item['quantidade'], 'id' => $item['id'], 'company_id' => $companyId]);
                }
            }

            // Registrar pagamentos individuais
            $metodosCache = [];
            foreach ($pagamentos as $indexPg => $pg) {
                $valorPg = (float) ($pg['valor'] ?? 0);
                $valorRecebidoPg = (float) ($pg['valor_recebido'] ?? $valorPg);
                $parcelasPg = (int) ($pg['parcelas'] ?? 1);
                $metodoPgId = (int) ($pg['metodo_pagamento_id'] ?? $pg['metodo_id'] ?? $metodoPagamentoId);
                $shipayOrderIdPg = $pg['pix_order_id'] ?? null;

                if ($valorPg <= 0 || !$metodoPgId) {
                    continue;
                }

                // Se for PIX via Shipay e ainda não foi confirmado, status = 'pendente', senão 'pago'
                $statusPagamento = 'pago';
                if ($shipayOrderIdPg) {
                    // PIX via Shipay: verificar se já foi confirmado (se não, fica pendente até webhook)
                    // Por padrão, PIX Shipay fica pendente até confirmação via webhook
                    $statusPagamento = 'pendente';
                }

                $this->db->prepare("INSERT INTO vendas_pagamentos (company_id, venda_id, numero_parcela, total_parcelas, valor_parcela, valor_pago, data_vencimento, data_pagamento, metodo_pagamento_id, status, shipay_order_id, created_at, updated_at) VALUES (:company_id, :venda_id, :numero_parcela, :total_parcelas, :valor_parcela, :valor_pago, CURDATE(), NOW(), :metodo_id, :status, :shipay_order_id, NOW(), NOW())")->execute([
                    'company_id' => $companyId,
                    'venda_id' => $vendaId,
                    'numero_parcela' => $indexPg + 1,
                    'total_parcelas' => max($parcelasPg, count($pagamentos)),
                    'valor_parcela' => $valorPg,
                    'valor_pago' => $valorRecebidoPg,
                    'metodo_id' => $metodoPgId,
                    'status' => $statusPagamento,
                    'shipay_order_id' => $shipayOrderIdPg
                ]);

                if (!isset($metodosCache[$metodoPgId])) {
                    $stmtMetodo = $this->db->prepare("SELECT name, type FROM metodos_pagamento WHERE id = :id");
                    $stmtMetodo->execute(['id' => $metodoPgId]);
                    $metodosCache[$metodoPgId] = $stmtMetodo->fetch() ?: ['name' => 'PDV', 'type' => ''];
                }
            }

            $metodoPrincipal = $metodosCache[$metodoPagamentoId] ?? reset($metodosCache) ?? ['name' => 'PDV', 'type' => ''];
            $metodoNome = $metodoPrincipal['name'] ?? 'PDV';
            $metodoTipo = $metodoPrincipal['type'] ?? '';

            // Montar resumo dos métodos/valores
            $resumoPagamentos = [];
            foreach ($pagamentos as $pg) {
                $metodoPgId = (int) ($pg['metodo_pagamento_id'] ?? $pg['metodo_id'] ?? 0);
                $metodoData = $metodosCache[$metodoPgId] ?? ['name' => 'Método', 'type' => ''];
                $resumoPagamentos[] = sprintf("%s (R$ %.2f)", $metodoData['name'], (float) ($pg['valor'] ?? 0));
            }
            $metodoResumo = implode(' + ', $resumoPagamentos);

            // Criar registro em contas_receber com status_id = 24 (Pago)
            $this->db->prepare("
                INSERT INTO contas_receber (
                    company_id, customer_id, description, amount, amount_received, amount_remaining,
                    due_date, payment_date, status, status_id, payment_method,
                    shipay_charge_id, shipay_status, shipay_tipo, shipay_qr_code, shipay_payload,
                    notes, created_at, updated_at
                ) VALUES (
                    :company_id, :customer_id, :description, :amount, :amount_received, :amount_remaining,
                    CURDATE(), NOW(), 'pago', 24, :payment_method,
                    :shipay_charge_id, :shipay_status, :shipay_tipo, :shipay_qr_code, :shipay_payload,
                    :notes, NOW(), NOW()
                )
            ")->execute([
                        'company_id' => $companyId,
                        'customer_id' => $clienteId,
                        'description' => "PDV - {$saleNumber}",
                        'amount' => $total,
                        'amount_received' => min($valorRecebidoTotal, $total),
                        'amount_remaining' => 0,
                        'payment_method' => $metodoResumo ?: $metodoNome,
                        'shipay_charge_id' => $pixOrderId,
                        'shipay_status' => $pixOrderId ? 'paid' : null,
                        'shipay_tipo' => ($pixOrderId && stripos($metodoTipo, 'pix') !== false) ? 'pix' : null,
                        'shipay_qr_code' => $pixQrCode,
                        'shipay_payload' => $pixPayload,
                        'notes' => "Venda #{$vendaId} - PDV" . ($trocoTotal > 0 ? " | Troco: R$ " . number_format($trocoTotal, 2, ',', '.') : '')
                    ]);

            $this->db->prepare("UPDATE pdv_sessoes SET saldo_atual = saldo_atual + :total, updated_at = NOW() WHERE id = :sessao_id")->execute(['total' => $total, 'sessao_id' => $sessaoId]);

            foreach ($pagamentos as $pg) {
                $valorPg = (float) ($pg['valor'] ?? 0);
                $metodoPgId = (int) ($pg['metodo_pagamento_id'] ?? $metodoPagamentoId);
                if ($valorPg <= 0 || !$metodoPgId) {
                    continue;
                }
                $this->db->prepare("INSERT INTO pdv_movimentos (company_id, sessao_id, venda_id, tipo, valor, metodo_pagamento_id, created_by, created_at) VALUES (:company_id, :sessao_id, :venda_id, 'venda', :valor, :metodo_id, :user_id, NOW())")->execute(['company_id' => $companyId, 'sessao_id' => $sessaoId, 'venda_id' => $vendaId, 'valor' => $valorPg, 'metodo_id' => $metodoPgId, 'user_id' => $userId]);
            }

            $this->db->commit();

            $this->success('Venda finalizada com sucesso!', [
                'venda' => ['id' => $vendaId, 'sale_number' => $saleNumber, 'total' => $total],
                'pix_order_id' => $pixOrderId,
                'troco' => $trocoTotal,
                'pagamentos' => $pagamentos
            ]);
        } catch (\Exception $e) {
            $this->db->rollBack();
            error_log("Erro ao processar PDV: " . $e->getMessage());
            $this->error('Erro ao processar venda: ' . $e->getMessage());
        }
    }

    public function imprimirAberturaCaixa(): void
    {
        try {
            $sessaoId = (int) $this->request->get('id');

            if (!$sessaoId) {
                echo 'Sessao nao encontrada';
                return;
            }

            $companyId = $this->session->get('pdv_company_id') ?? $this->session->get('company_id');
            $userId = $this->session->get('pdv_user_id');

            // Buscar dados da sessão
            $stmt = $this->db->prepare("
                SELECT ps.*, u.name as operador_nome
                FROM pdv_sessoes ps
                LEFT JOIN users u ON ps.user_id = u.id
                WHERE ps.id = :sessao_id
                AND ps.company_id = :company_id
            ");
            $stmt->execute(['sessao_id' => $sessaoId, 'company_id' => $companyId]);
            $sessao = $stmt->fetch();

            if (!$sessao) {
                echo 'Sessao nao encontrada';
                return;
            }

            // Buscar dados da empresa
            $stmt = $this->db->prepare("SELECT * FROM empresas WHERE id = :id");
            $stmt->execute(['id' => $companyId]);
            $empresa = $stmt->fetch() ?: [];

            $this->view('pdv/abertura-caixa-print', [
                'sessao' => $sessao,
                'empresa' => $empresa,
                'hideLayout' => true
            ]);
        } catch (\Exception $e) {
            echo 'Erro ao carregar comprovante: ' . $e->getMessage();
        }
    }

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

            if (!$vendaId) {
                echo 'Venda nao encontrada';
                return;
            }

            $companyId = $this->session->get('pdv_company_id') ?? $this->session->get('company_id');

            if ($companyId) {
                $stmt = $this->db->prepare("SELECT v.*, p.name as cliente_nome, mp.name as metodo_nome 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 WHERE v.id = :id AND v.company_id = :company_id");
                $stmt->execute(['id' => $vendaId, 'company_id' => $companyId]);
            } else {
                $stmt = $this->db->prepare("SELECT v.*, p.name as cliente_nome, mp.name as metodo_nome 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 WHERE v.id = :id");
                $stmt->execute(['id' => $vendaId]);
            }

            $venda = $stmt->fetch();

            if (!$venda) {
                echo 'Venda nao encontrada';
                return;
            }

            $companyId = $companyId ?? $venda['company_id'];

            $stmt = $this->db->prepare("SELECT * FROM vendas_itens WHERE venda_id = :venda_id");
            $stmt->execute(['venda_id' => $vendaId]);
            $itens = $stmt->fetchAll();

            $stmt = $this->db->prepare("SELECT * FROM empresas WHERE id = :id");
            $stmt->execute(['id' => $companyId]);
            $empresa = $stmt->fetch() ?: [];

            $this->view('pdv/cupom', ['venda' => $venda, 'itens' => $itens ?: [], 'empresa' => $empresa, 'hideLayout' => true]);
        } catch (\Exception $e) {
            echo 'Erro ao carregar cupom: ' . $e->getMessage();
        }
    }

    public function registrarSangria(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $sessaoId = $this->session->get('pdv_sessao_id');
            $userId = $this->session->get('pdv_user_id');

            $valor = (float) $this->request->post('valor', 0);
            $observacoes = trim($this->request->post('observacoes', ''));

            if ($valor <= 0) {
                $this->error('Valor invalido');
                return;
            }

            $stmt = $this->db->prepare("SELECT saldo_atual FROM pdv_sessoes WHERE id = :sessao_id AND company_id = :company_id");
            $stmt->execute(['sessao_id' => $sessaoId, 'company_id' => $companyId]);
            $sessao = $stmt->fetch();

            if (!$sessao || $valor > $sessao['saldo_atual']) {
                $this->error('Saldo insuficiente');
                return;
            }

            $this->db->beginTransaction();

            $this->db->prepare("INSERT INTO pdv_movimentos (company_id, sessao_id, venda_id, tipo, valor, metodo_pagamento_id, observacoes, created_by, created_at) VALUES (:company_id, :sessao_id, NULL, 'sangria', :valor, 1, :observacoes, :created_by, NOW())")->execute(['company_id' => $companyId, 'sessao_id' => $sessaoId, 'valor' => $valor, 'observacoes' => $observacoes ?: 'Sangria de caixa', 'created_by' => $userId]);

            $this->db->prepare("UPDATE pdv_sessoes SET saldo_atual = saldo_atual - :valor, updated_at = NOW() WHERE id = :sessao_id")->execute(['valor' => $valor, 'sessao_id' => $sessaoId]);

            $this->db->commit();

            $stmt = $this->db->prepare("SELECT saldo_atual FROM pdv_sessoes WHERE id = :id");
            $stmt->execute(['id' => $sessaoId]);
            $this->success('Sangria registrada!', ['valor' => $valor, 'novo_saldo' => $stmt->fetch()['saldo_atual']]);
        } catch (Exception $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            $this->error('Erro: ' . $e->getMessage());
        }
    }

    public function registrarSuprimento(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $sessaoId = $this->session->get('pdv_sessao_id');
            $userId = $this->session->get('pdv_user_id');

            $valor = (float) $this->request->post('valor', 0);
            $observacoes = trim($this->request->post('observacoes', ''));

            if ($valor <= 0) {
                $this->error('Valor invalido');
                return;
            }

            $this->db->beginTransaction();

            $this->db->prepare("INSERT INTO pdv_movimentos (company_id, sessao_id, venda_id, tipo, valor, metodo_pagamento_id, observacoes, created_by, created_at) VALUES (:company_id, :sessao_id, NULL, 'suprimento', :valor, 1, :observacoes, :created_by, NOW())")->execute(['company_id' => $companyId, 'sessao_id' => $sessaoId, 'valor' => $valor, 'observacoes' => $observacoes ?: 'Suprimento de caixa', 'created_by' => $userId]);

            $this->db->prepare("UPDATE pdv_sessoes SET saldo_atual = saldo_atual + :valor, updated_at = NOW() WHERE id = :sessao_id")->execute(['valor' => $valor, 'sessao_id' => $sessaoId]);

            $this->db->commit();

            $stmt = $this->db->prepare("SELECT saldo_atual FROM pdv_sessoes WHERE id = :id");
            $stmt->execute(['id' => $sessaoId]);
            $this->success('Suprimento registrado!', ['valor' => $valor, 'novo_saldo' => $stmt->fetch()['saldo_atual']]);
        } catch (Exception $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            $this->error('Erro: ' . $e->getMessage());
        }
    }

    public function gerarPixShipay(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $totalRaw = $this->request->post('total') ?? $_POST['total'] ?? 0;
            $total = (float) str_replace(',', '.', (string) $totalRaw);
            $clienteId = $this->request->post('cliente_id') ?: null;

            if ($total <= 0) {
                $this->error('Valor invalido: ' . $total);
                return;
            }

            $stmt = $this->db->prepare("SELECT shipay_enabled, shipay_environment, shipay_access_key, shipay_secret_key, shipay_client_id FROM empresas WHERE id = :company_id");
            $stmt->execute(['company_id' => $companyId]);
            $empresa = $stmt->fetch();

            if (!$empresa || empty($empresa['shipay_enabled'])) {
                $this->error('Integracao Shipay nao esta ativada para esta empresa');
                return;
            }

            $accessKey = $empresa['shipay_access_key'] ?? '';
            $secretKey = $empresa['shipay_secret_key'] ?? '';
            $clientIdShipay = $empresa['shipay_client_id'] ?? '';
            $environment = $empresa['shipay_environment'] ?? 'sandbox';

            if (empty($accessKey) || empty($secretKey) || empty($clientIdShipay)) {
                $this->error('Configure as credenciais Shipay na empresa');
                return;
            }

            $clienteNome = 'Cliente PDV';
            $clienteDoc = '00000000000';

            if ($clienteId) {
                $stmt = $this->db->prepare("SELECT name, document FROM pessoas WHERE id = :id AND company_id = :company_id");
                $stmt->execute(['id' => $clienteId, 'company_id' => $companyId]);
                $cliente = $stmt->fetch();
                if ($cliente) {
                    $clienteNome = $cliente['name'];
                    $clienteDoc = preg_replace('/\D/', '', $cliente['document'] ?? '00000000000');
                }
            }

            $orderRef = 'pdv_' . $companyId . '_' . time() . '_' . rand(1000, 9999);

            $shipayConfig = [
                'access_key' => $accessKey,
                'secret_key' => $secretKey,
                'client_id' => $clientIdShipay,
                'environment' => $environment,
                'base_url' => ['sandbox' => 'https://api-staging.shipay.com.br', 'production' => 'https://api.shipay.com.br']
            ];

            $shipay = new \App\Integrations\ShipayClient($shipayConfig);

            $payload = [
                'valor' => $total,
                'descricao' => 'PDV',
                'vencimento' => date('Y-m-d'),
                'order_ref' => $orderRef,
                'cliente' => ['nome' => $clienteNome, 'documento' => $clienteDoc],
                'itens' => [['descricao' => 'PDV', 'quantidade' => 1, 'valor' => $total]]
            ];

            $response = $shipay->createHybridBoleto($payload);

            $qrCode = $response['qr_code'] ?? null;
            $qrCodeText = $response['qr_code_text'] ?? $response['pix_copia_cola'] ?? null;
            $orderId = $response['order_id'] ?? $response['id'] ?? null;

            if (empty($qrCode) && empty($qrCodeText)) {
                $this->error('Shipay nao retornou QR Code.');
                return;
            }

            $this->success('QR Code PIX gerado com sucesso', ['order_id' => $orderId, 'order_ref' => $orderRef, 'qrcode' => $qrCode, 'qrcode_text' => $qrCodeText, 'valor' => $total]);
        } catch (\Exception $e) {
            error_log("Erro ao gerar PIX Shipay PDV: " . $e->getMessage());
            $this->error('Erro ao gerar QR Code PIX: ' . $e->getMessage());
        }
    }

    public function consultarStatusPix(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $orderId = $this->request->post('order_id') ?? $this->request->get('order_id');

            if (empty($orderId)) {
                $this->error('Order ID nao informado');
                return;
            }

            $stmt = $this->db->prepare("SELECT shipay_enabled, shipay_environment, shipay_access_key, shipay_secret_key, shipay_client_id FROM empresas WHERE id = :company_id");
            $stmt->execute(['company_id' => $companyId]);
            $empresa = $stmt->fetch();

            if (!$empresa || empty($empresa['shipay_enabled'])) {
                $this->error('Integracao Shipay nao esta ativada');
                return;
            }

            $accessKey = $empresa['shipay_access_key'] ?? '';
            $secretKey = $empresa['shipay_secret_key'] ?? '';
            $clientIdShipay = $empresa['shipay_client_id'] ?? '';
            $environment = $empresa['shipay_environment'] ?? 'sandbox';

            if (empty($accessKey) || empty($secretKey) || empty($clientIdShipay)) {
                $this->error('Credenciais Shipay nao configuradas');
                return;
            }

            $shipayConfig = [
                'access_key' => $accessKey,
                'secret_key' => $secretKey,
                'client_id' => $clientIdShipay,
                'environment' => $environment,
                'base_url' => ['sandbox' => 'https://api-staging.shipay.com.br', 'production' => 'https://api.shipay.com.br']
            ];

            $shipay = new \App\Integrations\ShipayClient($shipayConfig);
            $response = $shipay->getOrder($orderId);

            $status = $response['status'] ?? $response['order_status'] ?? 'pending';
            $statusMap = ['pending' => 'pendente', 'approved' => 'pago', 'paid' => 'pago', 'cancelled' => 'cancelado', 'expired' => 'expirado', 'refunded' => 'estornado'];
            $statusNormalizado = $statusMap[strtolower($status)] ?? $status;

            $this->success('Status consultado', ['order_id' => $orderId, 'status' => $statusNormalizado, 'status_original' => $status, 'pago' => in_array($statusNormalizado, ['pago', 'approved', 'paid'])]);
        } catch (\Exception $e) {
            error_log("Erro ao consultar status PIX: " . $e->getMessage());
            $this->error('Erro ao consultar status: ' . $e->getMessage());
        }
    }

    /**
     * Cancelar cobrança PIX Shipay pendente
     *
     * Usado quando o modal de pagamento é fechado sem confirmar,
     * ou quando o método de pagamento é alterado, para não gerar
     * pendências no painel da Shipay.
     */
    public function cancelarPixShipay(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $orderId = $this->request->post('order_id') ?? $this->request->get('order_id');

            if (empty($orderId)) {
                $this->error('Order ID nao informado');
                return;
            }

            $stmt = $this->db->prepare("SELECT shipay_enabled, shipay_environment, shipay_access_key, shipay_secret_key, shipay_client_id FROM empresas WHERE id = :company_id");
            $stmt->execute(['company_id' => $companyId]);
            $empresa = $stmt->fetch();

            if (!$empresa || empty($empresa['shipay_enabled'])) {
                $this->error('Integracao Shipay nao esta ativada');
                return;
            }

            $accessKey = $empresa['shipay_access_key'] ?? '';
            $secretKey = $empresa['shipay_secret_key'] ?? '';
            $clientIdShipay = $empresa['shipay_client_id'] ?? '';
            $environment = $empresa['shipay_environment'] ?? 'sandbox';

            if (empty($accessKey) || empty($secretKey) || empty($clientIdShipay)) {
                $this->error('Credenciais Shipay nao configuradas');
                return;
            }

            $shipayConfig = [
                'access_key' => $accessKey,
                'secret_key' => $secretKey,
                'client_id' => $clientIdShipay,
                'environment' => $environment,
                'base_url' => ['sandbox' => 'https://api-staging.shipay.com.br', 'production' => 'https://api.shipay.com.br']
            ];

            $shipay = new \App\Integrations\ShipayClient($shipayConfig);

            // Tentar cancelar a cobrança
            $response = $shipay->cancelOrder($orderId);

            // Atualizar status no banco local
            $stmt = $this->db->prepare("UPDATE vendas_pagamentos SET status = 'cancelado' WHERE shipay_order_id = :order_id AND company_id = :company_id");
            $stmt->execute(['order_id' => $orderId, 'company_id' => $companyId]);

            error_log("[PDV] Cobranca PIX cancelada - Order ID: {$orderId}");

            $this->success('Cobranca cancelada com sucesso', [
                'order_id' => $orderId,
                'status' => 'cancelled'
            ]);
        } catch (\Exception $e) {
            $errorMsg = $e->getMessage();
            $errorLower = strtolower($errorMsg);

            // Tratar casos onde a cobrança já não está mais pendente como sucesso
            // (já foi cancelada, expirada, estornada ou paga)
            $jaProcessada = strpos($errorMsg, '404') !== false
                || stripos($errorMsg, 'not found') !== false
                || stripos($errorMsg, 'already refunded') !== false
                || stripos($errorMsg, 'already cancelled') !== false
                || stripos($errorMsg, 'already canceled') !== false
                || stripos($errorMsg, 'already paid') !== false
                || stripos($errorMsg, 'expired') !== false;

            if ($jaProcessada) {
                // Atualizar status no banco local para não aparecer mais na lista
                $novoStatus = 'cancelado';
                if (stripos($errorMsg, 'refunded') !== false) {
                    $novoStatus = 'estornado';
                } elseif (stripos($errorMsg, 'paid') !== false) {
                    $novoStatus = 'pago';
                } elseif (stripos($errorMsg, 'expired') !== false) {
                    $novoStatus = 'expirado';
                }

                $stmt = $this->db->prepare("UPDATE vendas_pagamentos SET status = :status WHERE shipay_order_id = :order_id AND company_id = :company_id");
                $stmt->execute(['status' => $novoStatus, 'order_id' => $orderId, 'company_id' => $companyId]);

                error_log("[PDV] Cobranca PIX ja estava processada - Order ID: {$orderId} - Motivo: {$errorMsg} - Status atualizado: {$novoStatus}");
                $this->success('Cobranca ja estava processada (cancelada, paga ou expirada)', [
                    'order_id' => $orderId,
                    'status' => 'already_processed'
                ]);
                return;
            }

            error_log("[PDV] Erro ao cancelar PIX Shipay: " . $errorMsg);
            $this->error('Erro ao cancelar cobranca: ' . $errorMsg);
        }
    }

    /**
     * Listar cobranças Shipay recentes (para consulta/cancelamento/estorno)
     */
    public function listarCobrancasShipay(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $tipo = $this->request->get('tipo') ?? 'todas'; // todas, cancelaveis, estornaveis

            // Buscar pagamentos com PIX dos últimos 7 dias
            $sql = "SELECT vp.id, vp.venda_id, vp.valor_parcela, vp.shipay_order_id, vp.status, vp.created_at,
                           v.sale_number, v.total as venda_total,
                           p.name as cliente_nome
                    FROM vendas_pagamentos vp
                    INNER JOIN vendas v ON vp.venda_id = v.id
                    LEFT JOIN pessoas p ON v.customer_id = p.id
                    WHERE vp.company_id = :company_id
                      AND vp.shipay_order_id IS NOT NULL
                      AND vp.shipay_order_id != ''
                      AND vp.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)";

            // Filtrar por tipo (baseado no status do pagamento)
            // Excluir sempre os já cancelados/estornados/expirados
            $sql .= " AND vp.status NOT IN ('cancelado', 'cancelled', 'estornado', 'refunded', 'expirado', 'expired')";

            if ($tipo === 'cancelaveis') {
                $sql .= " AND vp.status IN ('pendente', 'pending', 'aguardando')";
            } elseif ($tipo === 'estornaveis') {
                $sql .= " AND vp.status IN ('pago', 'paid', 'approved')";
            }

            $sql .= " ORDER BY vp.created_at DESC LIMIT 50";

            $stmt = $this->db->prepare($sql);
            $stmt->execute(['company_id' => $companyId]);
            $cobrancas = $stmt->fetchAll(\PDO::FETCH_ASSOC);

            // Formatar dados
            $lista = [];
            foreach ($cobrancas as $cob) {
                $status = strtolower($cob['status'] ?? 'pendente');
                $lista[] = [
                    'id' => $cob['id'],
                    'venda_id' => $cob['venda_id'],
                    'sale_number' => $cob['sale_number'],
                    'valor' => (float) $cob['valor_parcela'],
                    'valor_parcela' => (float) $cob['valor_parcela'],
                    'shipay_order_id' => $cob['shipay_order_id'],
                    'shipay_status' => $status,
                    'cliente_nome' => $cob['cliente_nome'] ?? 'Cliente não identificado',
                    'data' => date('d/m/Y H:i', strtotime($cob['created_at'])),
                    'cancelavel' => in_array($status, ['pendente', 'pending', 'aguardando']),
                    'estornavel' => in_array($status, ['pago', 'paid', 'approved'])
                ];
            }

            $this->success('Cobrancas listadas', ['cobrancas' => $lista, 'total' => count($lista)]);
        } catch (\Exception $e) {
            error_log("[PDV] Erro ao listar cobrancas Shipay: " . $e->getMessage());
            $this->error('Erro ao listar cobrancas: ' . $e->getMessage());
        }
    }

    /**
     * Listar PIX canceláveis (pendentes)
     */
    public function listarPixCancelaveis(): void
    {
        $_GET['tipo'] = 'cancelaveis';
        $this->listarCobrancasShipay();
    }

    /**
     * Listar PIX estornáveis (pagos)
     */
    public function listarPixEstornaveis(): void
    {
        $_GET['tipo'] = 'estornaveis';
        $this->listarCobrancasShipay();
    }

    /**
     * Estornar pagamento PIX Shipay (já pago)
     */
    public function estornarPixShipay(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessao PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            $orderId = $this->request->post('order_id') ?? $this->request->get('order_id');
            $amount = $this->request->post('amount') ? (float) $this->request->post('amount') : null;
            $reason = $this->request->post('reason') ?? 'Estorno solicitado pelo operador';

            if (empty($orderId)) {
                $this->error('Order ID nao informado');
                return;
            }

            $stmt = $this->db->prepare("SELECT shipay_enabled, shipay_environment, shipay_access_key, shipay_secret_key, shipay_client_id FROM empresas WHERE id = :company_id");
            $stmt->execute(['company_id' => $companyId]);
            $empresa = $stmt->fetch();

            if (!$empresa || empty($empresa['shipay_enabled'])) {
                $this->error('Integracao Shipay nao esta ativada');
                return;
            }

            $accessKey = $empresa['shipay_access_key'] ?? '';
            $secretKey = $empresa['shipay_secret_key'] ?? '';
            $clientIdShipay = $empresa['shipay_client_id'] ?? '';
            $environment = $empresa['shipay_environment'] ?? 'sandbox';

            if (empty($accessKey) || empty($secretKey) || empty($clientIdShipay)) {
                $this->error('Credenciais Shipay nao configuradas');
                return;
            }

            $shipayConfig = [
                'access_key' => $accessKey,
                'secret_key' => $secretKey,
                'client_id' => $clientIdShipay,
                'environment' => $environment,
                'base_url' => ['sandbox' => 'https://api-staging.shipay.com.br', 'production' => 'https://api.shipay.com.br']
            ];

            $shipay = new \App\Integrations\ShipayClient($shipayConfig);

            // Realizar estorno
            $response = $shipay->refundOrder($orderId, $amount, $reason);

            // Atualizar status no pagamento local
            $stmt = $this->db->prepare("UPDATE vendas_pagamentos SET status = 'estornado' WHERE shipay_order_id = :order_id AND company_id = :company_id");
            $stmt->execute(['order_id' => $orderId, 'company_id' => $companyId]);

            error_log("[PDV] PIX estornado - Order ID: {$orderId}");

            $this->success('Estorno realizado com sucesso', [
                'order_id' => $orderId,
                'status' => 'refunded'
            ]);
        } catch (\Exception $e) {
            error_log("[PDV] Erro ao estornar PIX Shipay: " . $e->getMessage());
            $this->error('Erro ao estornar: ' . $e->getMessage());
        }
    }

    public function fecharCaixa(): void
    {
        try {
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Usuario nao autenticado');
                return;
            }

            $userId = $this->session->get('pdv_user_id');
            $companyId = $this->session->get('pdv_company_id');
            $sessaoId = $this->session->get('pdv_sessao_id');

            if (!$sessaoId) {
                $this->error('Nenhuma sessao de caixa encontrada');
                return;
            }

            // Receber valores por método de pagamento
            $valoresMetodosRaw = $this->request->post('valores_metodos', '');
            $valoresMetodos = [];

            // Tentar decodificar JSON se for string
            if (!empty($valoresMetodosRaw)) {
                if (is_string($valoresMetodosRaw)) {
                    $decoded = json_decode($valoresMetodosRaw, true);
                    if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                        $valoresMetodos = $decoded;
                    }
                } elseif (is_array($valoresMetodosRaw)) {
                    $valoresMetodos = $valoresMetodosRaw;
                }
            }

            // Validar se há valores informados
            if (empty($valoresMetodos) || !is_array($valoresMetodos)) {
                $this->error('Informe os valores recebidos por método de pagamento');
                return;
            }

            $observacoes = trim($this->request->post('observacoes', ''));

            // Calcular valor total de fechamento a partir dos valores informados por método
            $valorFechamento = 0;
            $valoresProcessados = [];

            foreach ($valoresMetodos as $metodoId => $valor) {
                $metodoId = (int) $metodoId;
                $valorMetodo = (float) $valor;

                if ($valorMetodo > 0) {
                    $valorFechamento += $valorMetodo;
                    $valoresProcessados[$metodoId] = $valorMetodo;
                }
            }

            // Validar se o total é maior que zero
            if ($valorFechamento <= 0) {
                $this->error('Informe pelo menos um valor maior que zero');
                return;
            }

            // Usar valores processados (apenas os > 0)
            $valoresMetodos = $valoresProcessados;

            // Buscar dados da sessão
            $stmt = $this->db->prepare("
                SELECT * FROM pdv_sessoes
                WHERE id = :sessao_id
                AND user_id = :user_id
                AND company_id = :company_id
                AND status = 'aberto'
            ");
            $stmt->execute(['sessao_id' => $sessaoId, 'user_id' => $userId, 'company_id' => $companyId]);
            $sessao = $stmt->fetch();

            if (!$sessao) {
                $this->error('Sessao de caixa nao encontrada ou ja foi fechada');
                return;
            }

            $this->db->beginTransaction();

            // Calcular totais de movimentos
            $stmt = $this->db->prepare("
                SELECT
                    COALESCE(SUM(CASE WHEN tipo = 'venda' THEN valor ELSE 0 END), 0) as total_vendas,
                    COALESCE(SUM(CASE WHEN tipo = 'suprimento' THEN valor ELSE 0 END), 0) as total_suprimentos,
                    COALESCE(SUM(CASE WHEN tipo = 'sangria' THEN valor ELSE 0 END), 0) as total_sangrias
                FROM pdv_movimentos
                WHERE sessao_id = :sessao_id
            ");
            $stmt->execute(['sessao_id' => $sessaoId]);
            $totais = $stmt->fetch();

            $totalVendas = (float) ($totais['total_vendas'] ?? 0);
            $totalSuprimentos = (float) ($totais['total_suprimentos'] ?? 0);
            $totalSangrias = (float) ($totais['total_sangrias'] ?? 0);

            // Buscar valor de abertura dos movimentos (tipo 'abertura')
            $stmtAbertura = $this->db->prepare("
                SELECT COALESCE(SUM(valor), 0) as valor_abertura
                FROM pdv_movimentos
                WHERE sessao_id = :sessao_id AND tipo = 'abertura'
            ");
            $stmtAbertura->execute(['sessao_id' => $sessaoId]);
            $aberturaMov = $stmtAbertura->fetch();
            $valorAberturaMov = (float) ($aberturaMov['valor_abertura'] ?? $sessao['valor_abertura'] ?? 0);

            // Saldo esperado = valor_abertura + vendas + suprimentos - sangrias
            $saldoEsperado = $valorAberturaMov + $totalVendas + $totalSuprimentos - $totalSangrias;
            $diferenca = $valorFechamento - $saldoEsperado;

            // Registrar movimentos de fechamento (um para cada método de pagamento)
            foreach ($valoresMetodos as $metodoId => $valor) {
                $valorMetodo = (float) $valor;
                if ($valorMetodo > 0) {
                    $this->db->prepare("
                        INSERT INTO pdv_movimentos
                        (company_id, sessao_id, venda_id, tipo, valor, metodo_pagamento_id, observacoes, created_by, created_at)
                        VALUES
                        (:company_id, :sessao_id, NULL, 'fechamento', :valor, :metodo_id, :observacoes, :user_id, NOW())
                    ")->execute([
                                'company_id' => $companyId,
                                'sessao_id' => $sessaoId,
                                'valor' => $valorMetodo,
                                'metodo_id' => $metodoId,
                                'observacoes' => $observacoes ?: "Fechamento de caixa - Método ID {$metodoId}",
                                'user_id' => $userId
                            ]);
                }
            }

            // Fechar a sessão (sem saldo_final que não existe)
            $stmt = $this->db->prepare("
                UPDATE pdv_sessoes
                SET
                    status = 'fechado',
                    valor_fechamento = :valor_fechamento,
                    diferenca = :diferenca,
                    observacoes_fechamento = :observacoes,
                    updated_at = NOW(),
                    closed_at = NOW()
                WHERE id = :sessao_id
            ");
            $stmt->execute([
                'sessao_id' => $sessaoId,
                'valor_fechamento' => $valorFechamento,
                'diferenca' => $diferenca,
                'observacoes' => $observacoes ?: null
            ]);

            $this->db->commit();

            // Guardar sessao_id antes de limpar (precisa para impressão)
            $sessaoIdParaImpressao = $sessaoId;

            // Limpar sessão
            $this->session->remove('pdv_sessao_id');

            $this->success('Caixa fechado com sucesso', [
                'redirect' => '/pdv/login',
                'sessao_id' => $sessaoIdParaImpressao,
                'dados' => [
                    'valor_abertura' => $sessao['valor_abertura'],
                    'total_vendas' => $totalVendas,
                    'total_suprimentos' => $totalSuprimentos,
                    'total_sangrias' => $totalSangrias,
                    'saldo_esperado' => $saldoEsperado,
                    'valor_fechamento' => $valorFechamento,
                    'diferenca' => $diferenca,
                    'valores_metodos' => $valoresMetodos
                ]
            ]);
        } catch (Exception $e) {
            if ($this->db->inTransaction()) {
                $this->db->rollBack();
            }
            error_log("Erro ao fechar caixa: " . $e->getMessage());
            $this->error('Erro ao fechar caixa: ' . $e->getMessage());
        }
    }

    public function imprimirFechamentoCaixa(): void
    {
        try {
            $sessaoId = (int) $this->request->get('id');

            if (!$sessaoId) {
                echo 'Sessao nao encontrada';
                return;
            }

            // Buscar dados da sessão (sem validar company_id primeiro, pois a sessão pode estar limpa)
            $stmt = $this->db->prepare("
                SELECT ps.*, u.name as operador_nome
                FROM pdv_sessoes ps
                LEFT JOIN users u ON ps.user_id = u.id
                WHERE ps.id = :sessao_id
                AND ps.status = 'fechado'
            ");
            $stmt->execute(['sessao_id' => $sessaoId]);
            $sessao = $stmt->fetch();

            if (!$sessao) {
                echo 'Sessao nao encontrada ou ainda nao foi fechada';
                return;
            }

            // Usar company_id da sessão
            $companyId = $sessao['company_id'];

            // Buscar dados da empresa
            $stmt = $this->db->prepare("SELECT * FROM empresas WHERE id = :id");
            $stmt->execute(['id' => $companyId]);
            $empresa = $stmt->fetch() ?: [];

            // Calcular totais de movimentos
            $stmt = $this->db->prepare("
                SELECT
                    COALESCE(SUM(CASE WHEN tipo = 'abertura' THEN valor ELSE 0 END), 0) as valor_abertura,
                    COALESCE(SUM(CASE WHEN tipo = 'venda' THEN valor ELSE 0 END), 0) as total_vendas,
                    COALESCE(SUM(CASE WHEN tipo = 'suprimento' THEN valor ELSE 0 END), 0) as total_suprimentos,
                    COALESCE(SUM(CASE WHEN tipo = 'sangria' THEN valor ELSE 0 END), 0) as total_sangrias,
                    COALESCE(SUM(CASE WHEN tipo = 'fechamento' THEN valor ELSE 0 END), 0) as valor_fechamento
                FROM pdv_movimentos
                WHERE sessao_id = :sessao_id
            ");
            $stmt->execute(['sessao_id' => $sessaoId]);
            $totais = $stmt->fetch();

            $valorAbertura = (float) ($totais['valor_abertura'] ?? $sessao['valor_abertura'] ?? 0);
            $totalVendas = (float) ($totais['total_vendas'] ?? 0);
            $totalSuprimentos = (float) ($totais['total_suprimentos'] ?? 0);
            $totalSangrias = (float) ($totais['total_sangrias'] ?? 0);
            $valorFechamento = (float) ($totais['valor_fechamento'] ?? $sessao['valor_fechamento'] ?? 0);

            // Saldo esperado = valor_abertura + vendas + suprimentos - sangrias
            $saldoEsperado = $valorAbertura + $totalVendas + $totalSuprimentos - $totalSangrias;
            $diferenca = $valorFechamento - $saldoEsperado;

            // Buscar valores por método de pagamento do fechamento
            $stmt = $this->db->prepare("
                SELECT
                    m.valor,
                    m.metodo_pagamento_id,
                    mp.name as metodo_nome
                FROM pdv_movimentos m
                LEFT JOIN metodos_pagamento mp ON m.metodo_pagamento_id = mp.id
                WHERE m.sessao_id = :sessao_id
                AND m.tipo = 'fechamento'
                ORDER BY m.metodo_pagamento_id
            ");
            $stmt->execute(['sessao_id' => $sessaoId]);
            $valoresMetodos = $stmt->fetchAll() ?: [];

            // Contar quantidade de vendas
            $stmt = $this->db->prepare("
                SELECT COUNT(DISTINCT venda_id) as qtd_vendas
                FROM pdv_movimentos
                WHERE sessao_id = :sessao_id
                AND tipo = 'venda'
                AND venda_id IS NOT NULL
            ");
            $stmt->execute(['sessao_id' => $sessaoId]);
            $qtdVendas = (int) ($stmt->fetch()['qtd_vendas'] ?? 0);

            $this->view('pdv/fechamento-caixa-print', [
                'sessao' => $sessao,
                'empresa' => $empresa,
                'valor_abertura' => $valorAbertura,
                'total_vendas' => $totalVendas,
                'total_suprimentos' => $totalSuprimentos,
                'total_sangrias' => $totalSangrias,
                'saldo_esperado' => $saldoEsperado,
                'valor_fechamento' => $valorFechamento,
                'diferenca' => $diferenca,
                'valores_metodos' => $valoresMetodos,
                'qtd_vendas' => $qtdVendas,
                'hideLayout' => true
            ]);
        } catch (\Exception $e) {
            echo 'Erro ao carregar comprovante: ' . $e->getMessage();
        }
    }

    public function logout(): void
    {
        $this->session->remove('pdv_user_id');
        $this->session->remove('pdv_user_name');
        $this->session->remove('pdv_company_id');
        $this->session->remove('pdv_sessao_id');

        $isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';

        if ($isAjax) {
            $this->success('Logout realizado', ['redirect' => '/pdv/login']);
        } else {
            $this->redirect('/pdv/login');
        }
    }

    /**
     * Proxy para emissão de NFC-e via API interna
     * POST /pdv/emitir-nfce
     * Recebe venda_id e busca todos os dados necessários do banco
     */
    public function emitirNFCe(): void
    {
        try {
            // Validar autenticação PDV
            if (!$this->session->get('pdv_user_id')) {
                $this->error('Sessão PDV expirada');
                return;
            }

            $companyId = $this->session->get('pdv_company_id');
            if (!$companyId) {
                $this->error('Sessão PDV inválida');
                return;
            }

            // Ler payload JSON da requisição
            $input = file_get_contents('php://input');
            $payload = json_decode($input, true);

            if (!$payload) {
                $this->error('Payload JSON inválido');
                return;
            }

            // Obter venda_id do payload
            $vendaId = (int) ($payload['venda_id'] ?? 0);
            if ($vendaId <= 0) {
                $this->error('ID da venda é obrigatório');
                return;
            }

            error_log("[PDV::emitirNFCe] Iniciando emissão NFC-e para venda #{$vendaId}");

            // Buscar dados da venda
            $stmt = $this->db->prepare("
                SELECT v.*, p.name as customer_name, p.document as customer_document,
                       p.email as customer_email, p.phone as customer_phone,
                       p.address as customer_address, p.bairro as customer_neighborhood,
                       p.city as customer_city, p.state as customer_state, p.zip_code as customer_zipcode
                FROM vendas v
                LEFT JOIN pessoas p ON v.customer_id = p.id
                WHERE v.id = :venda_id AND v.company_id = :company_id
            ");
            $stmt->execute(['venda_id' => $vendaId, 'company_id' => $companyId]);
            $venda = $stmt->fetch();

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

            // Buscar itens da venda
            $stmt = $this->db->prepare("
                SELECT vi.*,
                       COALESCE(vi.ncm, '00000000') as ncm,
                       COALESCE(vi.cfop, '5102') as cfop,
                       COALESCE(vi.cst_icms, '00') as cst_icms,
                       COALESCE(vi.origem, '0') as origem,
                       COALESCE(vi.aliquota_icms, 0) as aliquota_icms
                FROM vendas_itens vi
                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();

            if (empty($itens)) {
                $this->error('Venda não possui itens');
                return;
            }

            // Buscar pagamentos da venda
            $stmt = $this->db->prepare("
                SELECT vp.*, mp.name as metodo_nome, mp.type as metodo_type
                FROM vendas_pagamentos vp
                LEFT JOIN metodos_pagamento mp ON vp.metodo_pagamento_id = mp.id
                WHERE vp.venda_id = :venda_id AND vp.company_id = :company_id
                ORDER BY vp.id ASC
            ");
            $stmt->execute(['venda_id' => $vendaId, 'company_id' => $companyId]);
            $pagamentos = $stmt->fetchAll();

            // Buscar dados da empresa (tabela usa colunas em português)
            $stmt = $this->db->prepare("SELECT * FROM empresas WHERE id = :company_id");
            $stmt->execute(['company_id' => $companyId]);
            $empresaRaw = $stmt->fetch();

            if (!$empresaRaw) {
                $this->error('Dados da empresa não encontrados');
                return;
            }

            // Log para debug - mostrar todas as colunas disponíveis relacionadas a CSC
            $colunasCSC = [];
            foreach ($empresaRaw as $key => $value) {
                if (stripos($key, 'csc') !== false || stripos($key, 'token') !== false) {
                    $colunasCSC[$key] = !empty($value) ? substr($value, 0, 20) . '...' : '(vazio)';
                }
            }
            error_log("[PDV::emitirNFCe] Colunas CSC encontradas: " . json_encode($colunasCSC, JSON_UNESCAPED_UNICODE));

            // Normalizar estado (UF) - deve ter 2 caracteres em maiúsculas
            // Tabela empresas usa 'uf' (minúsculo)
            $estadoRaw = $empresaRaw['uf'] ?? $empresaRaw['UF'] ?? $empresaRaw['estado'] ?? $empresaRaw['state'] ?? '';
            $estado = strtoupper(trim($estadoRaw));

            // Se estado estiver vazio ou inválido
            if (empty($estado) || strlen($estado) < 2) {
                error_log("[PDV::emitirNFCe] ERRO: Estado inválido ou vazio. Estado recebido: '{$estadoRaw}'");
                error_log("[PDV::emitirNFCe] Colunas disponíveis: " . implode(', ', array_keys($empresaRaw)));
                $this->error('Estado (UF) da empresa é obrigatório e deve ter 2 caracteres (ex: PE, SP, RJ). Verifique o cadastro da empresa.');
                return;
            }

            // Garantir que tem exatamente 2 caracteres
            $estado = substr($estado, 0, 2);

            // Montar dados da empresa no formato esperado pela API NFC-e
            // Mapear colunas em português para o formato esperado
            $empresa = [
                'nome' => $empresaRaw['razao_social'] ?? $empresaRaw['name'] ?? $empresaRaw['nome'] ?? '',
                'nome_fantasia' => $empresaRaw['nome_fantasia'] ?? $empresaRaw['trade_name'] ?? $empresaRaw['razao_social'] ?? '',
                'cnpj' => preg_replace('/\D/', '', $empresaRaw['cnpj'] ?? $empresaRaw['document'] ?? ''),
                'inscricao_estadual' => $empresaRaw['inscricao_estadual'] ?? $empresaRaw['state_registration'] ?? '',
                'regime_tributario' => (int) ($empresaRaw['regime_tributario'] ?? $empresaRaw['tax_regime'] ?? 1),
                'estado' => $estado, // Estado normalizado
                'cidade' => $empresaRaw['cidade'] ?? $empresaRaw['city'] ?? '',
                'endereco' => $empresaRaw['endereco'] ?? $empresaRaw['address'] ?? '',
                'numero' => $empresaRaw['numero'] ?? $empresaRaw['address_number'] ?? '',
                'bairro' => $empresaRaw['bairro'] ?? $empresaRaw['neighborhood'] ?? '',
                'cep' => preg_replace('/\D/', '', $empresaRaw['cep'] ?? $empresaRaw['zipcode'] ?? ''),
                'email' => $empresaRaw['email'] ?? '',
                'telefone' => preg_replace('/\D/', '', $empresaRaw['telefone'] ?? $empresaRaw['phone'] ?? '')
            ];

            // Adicionar CSC ao bloco empresa (obrigatório para NFC-e)
            // Tentar múltiplas variações de nomes de colunas
            $empresa['csc'] = $empresaRaw['csc_nfce']
                ?? $empresaRaw['csc']
                ?? $empresaRaw['token_csc']
                ?? $empresaRaw['csc_token']
                ?? $empresaRaw['codigo_seguranca']
                ?? '';

            $empresa['csc_id'] = $empresaRaw['id_token_nfce']
                ?? $empresaRaw['csc_id']
                ?? $empresaRaw['id_csc']
                ?? $empresaRaw['token_id']
                ?? $empresaRaw['csc_token_id']
                ?? '000001';

            // Log detalhado do que foi encontrado
            error_log("[PDV::emitirNFCe] CSC encontrado: " . (!empty($empresa['csc']) ? 'SIM (' . strlen($empresa['csc']) . ' caracteres)' : 'NÃO'));
            error_log("[PDV::emitirNFCe] CSC_ID encontrado: " . ($empresa['csc_id']));

            // Se ainda estiver vazio, listar todas as chaves disponíveis para debug
            if (empty($empresa['csc'])) {
                error_log("[PDV::emitirNFCe] ERRO: CSC não encontrado. Todas as chaves disponíveis: " . implode(', ', array_keys($empresaRaw)));
            }

            // Validar campos obrigatórios
            $camposObrigatorios = ['nome', 'cnpj', 'inscricao_estadual', 'estado', 'cidade', 'endereco', 'numero', 'bairro', 'cep', 'csc', 'csc_id'];
            foreach ($camposObrigatorios as $campo) {
                if (empty($empresa[$campo])) {
                    error_log("[PDV::emitirNFCe] ERRO: Campo obrigatório '{$campo}' está vazio");
                    if ($campo === 'csc') {
                        $this->error('CSC (Código de Segurança do Contribuinte) não está configurado. Configure na empresa antes de emitir NFC-e.');
                    } else {
                        $this->error("Campo obrigatório da empresa não preenchido: {$campo}");
                    }
                    return;
                }
            }

            // Montar itens no formato esperado pela API NFC-e
            $itensNfce = [];
            foreach ($itens as $index => $item) {
                // Validar campos obrigatórios do item
                $codigo = $item['product_sku'] ?? $item['product_id'] ?? '';
                $descricao = $item['product_name'] ?? '';
                $ncm = preg_replace('/\D/', '', $item['ncm'] ?? '00000000');
                $cfop = $item['cfop'] ?? '5102';
                $cst_icms = $item['cst_icms'] ?? '00';
                $origem = $item['origem'] ?? '0';

                if (empty($codigo)) {
                    error_log("[PDV::emitirNFCe] Aviso: Item #{$index} sem código, usando ID do produto");
                    $codigo = $item['product_id'] ?? ($index + 1);
                }

                if (empty($descricao)) {
                    error_log("[PDV::emitirNFCe] ERRO: Item #{$index} sem descrição");
                    $this->error("Item #" . ($index + 1) . " não possui descrição");
                    return;
                }

                $itensNfce[] = [
                    'codigo' => (string) $codigo,
                    'descricao' => $descricao,
                    'ncm' => $ncm,
                    'cfop' => $cfop,
                    'unidade_comercial' => 'UN',
                    'quantidade_comercial' => (float) $item['quantity'],
                    'valor_unitario' => (float) $item['unit_price'],
                    'valor_total' => (float) $item['total_price'],
                    'cst_icms' => $cst_icms,
                    'origem' => $origem,
                    'aliquota_icms' => (float) ($item['aliquota_icms'] ?? 0)
                ];
            }

            // Montar pagamentos no formato esperado pela API NFC-e
            // Mapear métodos de pagamento para códigos da SEFAZ
            $mapFormaPagamento = [
                'dinheiro' => '01',
                'especie' => '01',
                'cheque' => '02',
                'cartao_credito' => '03',
                'credito' => '03',
                'cartao_debito' => '04',
                'debito' => '04',
                'pix' => '17',
                'boleto' => '15',
                'deposito' => '16',
                'transferencia' => '18',
                'carne' => '99',
                'crediario' => '99',
                'voucher' => '99',
                'operacoes_internas' => '99',
                'outro' => '99'
            ];

            $pagamentosNfce = [];
            $trocoTotal = 0;
            foreach ($pagamentos as $pg) {
                $metodoType = strtolower($pg['metodo_type'] ?? '');
                $metodoNome = strtolower($pg['metodo_nome'] ?? '');

                // Tentar mapear pelo type (campo da tabela) ou nome
                $formaPagamento = $mapFormaPagamento[$metodoType] ??
                    $mapFormaPagamento[$metodoNome] ??
                    '99'; // Outros

                $valorPago = (float) ($pg['valor_pago'] ?? $pg['valor_parcela'] ?? 0);
                $valorParcela = (float) ($pg['valor_parcela'] ?? 0);

                // Calcular troco se valor pago > valor parcela
                if ($valorPago > $valorParcela) {
                    $trocoTotal += ($valorPago - $valorParcela);
                }

                $pagamentosNfce[] = [
                    'forma' => $formaPagamento,
                    'valor' => $valorParcela
                ];
            }

            // Se houver troco, adicionar como pagamento em dinheiro
            if ($trocoTotal > 0) {
                $pagamentosNfce[] = [
                    'forma' => '01', // Dinheiro
                    'valor' => $trocoTotal,
                    'troco' => $trocoTotal
                ];
            }

            // Montar dados do cliente (opcional)
            $cliente = null;
            if (!empty($venda['customer_id']) && !empty($venda['customer_document'])) {
                $cliente = [
                    'nome' => $venda['customer_name'] ?? '',
                    'cpf' => strlen(preg_replace('/\D/', '', $venda['customer_document'])) === 11
                        ? preg_replace('/\D/', '', $venda['customer_document'])
                        : null,
                    'cnpj' => strlen(preg_replace('/\D/', '', $venda['customer_document'])) === 14
                        ? preg_replace('/\D/', '', $venda['customer_document'])
                        : null,
                    'email' => $venda['customer_email'] ?? '',
                    'telefone' => preg_replace('/\D/', '', $venda['customer_phone'] ?? '')
                ];
            }

            // Montar payload completo para API NFC-e
            // Converter ambiente de string para int (1=Produção, 2=Homologação)
            $ambienteStr = $empresaRaw['ambiente_nfce'] ?? $empresaRaw['ambiente'] ?? 'homologacao';
            $ambiente = ($ambienteStr === 'producao' || $ambienteStr === '1' || $ambienteStr === 1) ? 1 : 2;

            // Log para debug - verificar se CSC está no array empresa
            error_log("[PDV::emitirNFCe] Debug - CSC no array empresa: " . (isset($empresa['csc']) ? 'SIM (' . substr($empresa['csc'], 0, 10) . '...)' : 'NÃO'));
            error_log("[PDV::emitirNFCe] Debug - CSC_ID no array empresa: " . (isset($empresa['csc_id']) ? 'SIM (' . $empresa['csc_id'] . ')' : 'NÃO'));

            $payloadNfce = [
                'empresa' => $empresa, // CSC já está incluído em $empresa (linha 1389-1390)
                'nfce' => [
                    'ambiente' => $ambiente,
                    'serie' => $empresaRaw['serie_nfce'] ?? '001',
                    'numero' => $empresaRaw['numero_nfce'] ?? null // Se null, API gera automaticamente
                ],
                'itens' => $itensNfce,
                'pagamentos' => $pagamentosNfce,
                'desconto' => (float) ($venda['discount'] ?? 0),
                'informacoes_complementares' => 'Venda realizada no PDV - ' . ($venda['sale_number'] ?? ''),
                'natureza_operacao' => 'Venda de mercadorias',
                'responsavel_tecnico' => [
                    'cnpj' => '30305332000145', // Systhema Tecnologia
                    'contato' => 'Systhema Tecnologia',
                    'email' => 'desenvolvimento@Systhema.com.br',
                    'telefone' => '81997102197'
                ]
            ];

            // Adicionar cliente se houver
            if ($cliente) {
                $payloadNfce['cliente'] = $cliente;
            }

            // Chamar o serviço NFCeService diretamente (sem HTTP)
            $nfceServicePath = ROOT_PATH . '/src/Integrations/NFe/src/Services/NFCeService.php';

            if (!file_exists($nfceServicePath)) {
                error_log("[PDV::emitirNFCe] ERRO: NFCeService não encontrado em {$nfceServicePath}");
                $this->error('Serviço NFC-e não configurado');
                return;
            }

            // Carregar autoload do módulo NFe
            $autoloadPath = ROOT_PATH . '/src/Integrations/NFe/vendor/autoload.php';
            if (file_exists($autoloadPath)) {
                require_once $autoloadPath;
            }

            // Carregar o serviço
            require_once $nfceServicePath;

            // Instanciar e chamar o serviço diretamente
            $service = new \App\Services\NFCeService();
            $resultado = $service->emitir($payloadNfce);

            // Se NFC-e foi emitida com sucesso, salvar dados na venda (se houver colunas)
            if ($resultado['success'] ?? false) {
                error_log('[PDV::emitirNFCe] ✅ NFC-e emitida com sucesso. Número: ' . ($resultado['numero'] ?? 'N/A'));

                // Tentar salvar chave de acesso na venda (se coluna existir)
                try {
                    $stmtCheck = $this->db->query("SHOW COLUMNS FROM vendas LIKE 'nfce_chave_acesso'");
                    if ($stmtCheck->rowCount() > 0 && !empty($resultado['chave_acesso'])) {
                        $stmt = $this->db->prepare("
                            UPDATE vendas
                            SET nfce_chave_acesso = :chave_acesso,
                                nfce_numero = :numero,
                                nfce_protocolo = :protocolo,
                                updated_at = NOW()
                            WHERE id = :venda_id AND company_id = :company_id
                        ");
                        $stmt->execute([
                            'chave_acesso' => $resultado['chave_acesso'] ?? '',
                            'numero' => $resultado['numero'] ?? null,
                            'protocolo' => $resultado['protocolo'] ?? null,
                            'venda_id' => $vendaId,
                            'company_id' => $companyId
                        ]);
                    }
                } catch (\Exception $e) {
                    error_log("[PDV::emitirNFCe] Aviso: Não foi possível salvar dados da NFC-e na venda: " . $e->getMessage());
                }

                $this->success('NFC-e emitida com sucesso', $resultado);
            } else {
                error_log('[PDV::emitirNFCe] ❌ Erro na emissão: ' . ($resultado['error'] ?? 'Erro desconhecido'));
                $this->error($resultado['error'] ?? 'Erro ao emitir NFC-e', ['cStat' => $resultado['cStat'] ?? null]);
            }

        } catch (\Exception $e) {
            error_log('[PDV::emitirNFCe] Exception: ' . $e->getMessage());
            error_log('[PDV::emitirNFCe] Stack trace: ' . $e->getTraceAsString());
            $this->error('Erro ao emitir NFC-e: ' . $e->getMessage());
        }
    }
}

