<?php

namespace App\Controllers;

use App\Core\Request;
use App\Core\Response;
use App\Core\Session;
use App\Core\TenantManager;
use PDO;

/**
 * Controlador responsável pelos parâmetros gerais do sistema.
 */
class ParametrosController extends BaseController
{
    public function gruposAcesso(): void
    {
        if (!$this->tabelaExiste('grupos_acesso')) {
            $this->response->json(['success' => false, 'message' => 'Tabela de grupos não encontrada.'], 404);
            return;
        }

        if ($this->request->getMethod() === 'GET') {
            $companyId = $this->getCompanyId();
            $grupos = $this->listarGruposAcesso($companyId);
            $this->response->json(['success' => true, 'data' => $grupos]);
            return;
        }

        if ($this->request->getMethod() !== 'POST') {
            $this->response->json(['success' => false, 'message' => 'Método não permitido.'], 405);
            return;
        }

        try {
            $nome = trim((string) $this->request->post('nome'));
            $modulos = $this->request->post('modulos', []);
            $observacoes = trim((string) $this->request->post('observacoes'));

            if ($nome === '') {
                $this->response->json(['success' => false, 'message' => 'Informe o nome do grupo.'], 422);
                return;
            }

            if (!is_array($modulos) || count($modulos) === 0) {
                $this->response->json(['success' => false, 'message' => 'Selecione ao menos um módulo.'], 422);
                return;
            }

            $slug = strtolower(preg_replace('/[^a-z0-9]+/', '_', $nome));
            $companyId = $this->getCompanyId();

            $this->db->beginTransaction();

            $colunasGrupo = $this->colunasTabela('grupos_acesso');
            $campos = ['nome', 'slug'];
            $placeholders = [':nome', ':slug'];
            $paramsInsert = [
                'nome' => $nome,
                'slug' => $slug,
            ];

            if (in_array('descricao', $colunasGrupo, true)) {
                $campos[] = 'descricao';
                $placeholders[] = ':descricao';
                $paramsInsert['descricao'] = $observacoes;
            }
            if (in_array('observacoes', $colunasGrupo, true)) {
                $campos[] = 'observacoes';
                $placeholders[] = ':observacoes';
                $paramsInsert['observacoes'] = $observacoes;
            }
            if (in_array('ativo', $colunasGrupo, true)) {
                $campos[] = 'ativo';
                $placeholders[] = ':ativo';
                $paramsInsert['ativo'] = 'SIM';
            }
            if (in_array('company_id', $colunasGrupo, true)) {
                $campos[] = 'company_id';
                $placeholders[] = ':company_id';
                $paramsInsert['company_id'] = $companyId;
            }

            $agora = date('Y-m-d H:i:s');
            if (in_array('criado_em', $colunasGrupo, true)) {
                $campos[] = 'criado_em';
                $placeholders[] = ':criado_em';
                $paramsInsert['criado_em'] = $agora;
            }
            if (in_array('atualizado_em', $colunasGrupo, true)) {
                $campos[] = 'atualizado_em';
                $placeholders[] = ':atualizado_em';
                $paramsInsert['atualizado_em'] = $agora;
            }

            $sqlInsert = sprintf(
                'INSERT INTO grupos_acesso (%s) VALUES (%s)',
                implode(', ', $campos),
                implode(', ', $placeholders)
            );
            $stmt = $this->db->prepare($sqlInsert);
            $stmt->execute($paramsInsert);

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

            if ($this->tabelaExiste('grupos_acesso_modulos')) {
                $colunasModulo = $this->colunasTabela('grupos_acesso_modulos');
                if (in_array('grupo_id', $colunasModulo, true) && in_array('modulo_id', $colunasModulo, true)) {
                    $camposModulo = ['grupo_id', 'modulo_id'];
                    $placeholdersModulo = [':grupo_id', ':modulo_id'];
                    $paramsModuloBase = ['grupo_id' => $grupoId];

                    if (in_array('criado_em', $colunasModulo, true)) {
                        $camposModulo[] = 'criado_em';
                        $placeholdersModulo[] = ':criado_em';
                        $paramsModuloBase['criado_em'] = $agora;
                    }
                    if (in_array('atualizado_em', $colunasModulo, true)) {
                        $camposModulo[] = 'atualizado_em';
                        $placeholdersModulo[] = ':atualizado_em';
                        $paramsModuloBase['atualizado_em'] = $agora;
                    }
                    if (in_array('ativo', $colunasModulo, true)) {
                        $camposModulo[] = 'ativo';
                        $placeholdersModulo[] = ':ativo';
                        $paramsModuloBase['ativo'] = 'SIM';
                    }

                    $sqlInsertModulo = sprintf(
                        'INSERT INTO grupos_acesso_modulos (%s) VALUES (%s)',
                        implode(', ', $camposModulo),
                        implode(', ', $placeholdersModulo)
                    );
                    $insertModulo = $this->db->prepare($sqlInsertModulo);
                    foreach ($modulos as $moduloId) {
                        $paramsModulo = $paramsModuloBase;
                        $paramsModulo['modulo_id'] = (int) $moduloId;
                        $insertModulo->execute($paramsModulo);
                    }
                }
            }

            $this->db->commit();

            $this->response->json([
                'success' => true,
                'message' => 'Grupo de acesso criado com sucesso.',
            ]);
        } catch (\Throwable $e) {
            $this->db->rollBack();
            $this->response->json([
                'success' => false,
                'message' => 'Erro ao salvar grupo: ' . $e->getMessage(),
            ], 500);
        }
    }
    /**
     * Definições de parâmetros booleanos (sim/não) organizados por módulo.
     * Cada item contém rótulo, descrição e permissões sugeridas.
     */
    private array $parametrosBooleanos = [
        'operacional' => [
            'icone' => 'bi-gear-wide-connected',
            'titulo' => 'Operacional',
            'permissoes' => [
                'Operacional > Vendas > Autorizar venda sem estoque'
            ],
            'itens' => [
                'vender_estoque_negativo' => [
                    'label' => 'Vender com estoque negativo',
                    'descricao' => 'Permite concluir vendas mesmo quando o saldo do produto estiver abaixo de zero.'
                ]
            ]
        ],
        'lotes' => [
            'icone' => 'bi-boxes',
            'titulo' => 'Gestão de Lotes',
            'permissoes' => [
                'Estoque > Lotes > Gerenciar lotes em compras',
                'Estoque > Lotes > Gerenciar lotes em vendas',
                'Estoque > Lotes > Ajustes manuais de lotes'
            ],
            'itens' => [
                'usar_lote_venda' => [
                    'label' => 'Usar lote na venda',
                    'descricao' => 'Solicita a seleção de lote em cada item na finalização da venda.'
                ],
                'usar_lote_compra' => [
                    'label' => 'Usar lote na compra',
                    'descricao' => 'Obrigatório informar lote ao receber mercadorias do fornecedor.'
                ],
                'usar_lote_estoque' => [
                    'label' => 'Usar lote no estoque',
                    'descricao' => 'Controla o estoque físico considerando lotes separados.'
                ],
                'vender_lote_fifo' => [
                    'label' => 'Vender com lote automático (FIFO)',
                    'descricao' => 'Seleciona automaticamente o lote mais antigo (FIFO) durante a venda.'
                ]
            ]
        ],
        'comercial' => [
            'icone' => 'bi-briefcase',
            'titulo' => 'Comercial & Comissões',
            'permissoes' => [
                'Comercial > Comissões > Definir regras por venda',
                'Comercial > Comissões > Definir regras por item',
                'Comercial > Comissões > Definir comissionamento por funcionário'
            ],
            'itens' => [
                'comissao_por_venda' => [
                    'label' => 'Comissão por venda',
                    'descricao' => 'Habilita cálculo de comissão com base no total da venda.'
                ],
                'comissao_por_item' => [
                    'label' => 'Comissão por item',
                    'descricao' => 'Permite configurar porcentagens de comissão diretamente no produto.'
                ],
                'comissao_funcionario_item' => [
                    'label' => 'Comissão por funcionário no item',
                    'descricao' => 'Calcula comissões individualmente por vendedor associado a cada item.'
                ],
                'comissao_por_titulo_recebido' => [
                    'label' => 'Comissão por título recebido',
                    'descricao' => 'Calcula e libera comissões apenas quando o título (conta a receber) for recebido/pago.'
                ]
            ]
        ],
        'regulacao' => [
            'icone' => 'bi-shield-check',
            'titulo' => 'Regulação e Saúde',
            'permissoes' => [
                'Regulação > Produtos > Campos ANVISA',
                'Regulação > Clientes > Documentos de Área de Saúde'
            ],
            'itens' => [
                'habilitar_campos_anvisa_produtos' => [
                    'label' => 'Habilitar Campos Anvisa em Produtos',
                    'descricao' => 'Exibe os campos relacionados à ANVISA (anvisa, validade anvisa, classificação risco, código SIMPRO, código SUS e datas de licenças) no cadastro de produtos.'
                ],
                'habilitar_documentos_area_saude_clientes' => [
                    'label' => 'Habilitar Documentos (Área de Saúde) em Clientes',
                    'descricao' => 'Exibe campos e funcionalidades específicas da área de saúde no cadastro de clientes/pessoas.'
                ]
            ]
        ],
        'modulos' => [
            'icone' => 'bi-grid',
            'titulo' => 'Módulos do Sistema',
            'permissoes' => [],
            'itens' => [
                'modulo_pdv' => [
                    'label' => 'PDV (Frente de Loja)',
                    'descricao' => 'Disponibiliza o terminal de vendas para operação em tempo real.'
                ],
                'modulo_vendas' => [
                    'label' => 'Vendas',
                    'descricao' => 'Permite registrar pedidos e faturamento direto pelo módulo comercial.'
                ],
                'modulo_pre_vendas' => [
                    'label' => 'Pré-vendas / DAV',
                    'descricao' => 'Habilita a criação de orçamentos e documentos auxiliares de venda.'
                ],
                'modulo_compras' => [
                    'label' => 'Compras',
                    'descricao' => 'Libera o fluxo de requisições, pedidos e recebimento de compras.'
                ],
                'modulo_estoque' => [
                    'label' => 'Gestão de Estoque',
                    'descricao' => 'Controla movimentações, ajustes e saldos dos produtos.'
                ],
                'modulo_ordem_servico' => [
                    'label' => 'Ordem de Serviço',
                    'descricao' => 'Permite abrir, acompanhar e finalizar ordens de serviço.'
                ],
                'modulo_financeiro' => [
                    'label' => 'Financeiro',
                    'descricao' => 'Habilita contas a pagar, contas a receber e fluxo de caixa.'
                ],
                'modulo_centro_custos' => [
                    'label' => 'Centro de Custos',
                    'descricao' => 'Controla a hierarquia de centros de custos e rateios.'
                ],
                'modulo_arquivos_fiscais' => [
                    'label' => 'Arquivos Fiscais',
                    'descricao' => 'Gera e envia XMLs e documentos fiscais para a contabilidade.'
                ],
                'modulo_relatorios' => [
                    'label' => 'Relatórios Gerenciais',
                    'descricao' => 'Disponibiliza dashboards e relatórios operacionais.'
                ]
            ]
        ]
    ];

    /**
     * Lista de módulos padrão para popular tabela modulos.
     */
    private array $modulosPadrao = [
        [
            'nome' => 'PDV (Frente de Loja)',
            'nome_interface' => 'modulo_pdv',
            'descricao' => 'Terminal de vendas em tempo real para operações de frente de loja.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Vendas',
            'nome_interface' => 'modulo_vendas',
            'descricao' => 'Gestão completa de pedidos, orçamento e faturamento.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Pré-vendas / DAV',
            'nome_interface' => 'modulo_pre_vendas',
            'descricao' => 'Emissão de DAVs e orçamentos para posterior conversão em venda.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Compras',
            'nome_interface' => 'modulo_compras',
            'descricao' => 'Controle de requisições, pedidos e recebimento de mercadorias.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Gestão de Estoque',
            'nome_interface' => 'modulo_estoque',
            'descricao' => 'Movimentação, ajustes e controle de saldos de produtos.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Ordem de Serviço',
            'nome_interface' => 'modulo_ordem_servico',
            'descricao' => 'Abertura, acompanhamento e fechamento de ordens de serviço.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Financeiro',
            'nome_interface' => 'modulo_financeiro',
            'descricao' => 'Gestão de contas a pagar, contas a receber e fluxo de caixa.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Centro de Custos',
            'nome_interface' => 'modulo_centro_custos',
            'descricao' => 'Estruturação hierárquica e rateios por centro de custo.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Arquivos Fiscais',
            'nome_interface' => 'modulo_arquivos_fiscais',
            'descricao' => 'Geração e envio de XMLs e documentos fiscais à contabilidade.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Relatórios Gerenciais',
            'nome_interface' => 'modulo_relatorios',
            'descricao' => 'Dashboards e relatórios operacionais para decisões estratégicas.',
            'status' => 'disponivel',
            'ativo' => 'SIM',
        ],
    ];

    private array $gruposAcessoPadrao = [
        [
            'nome' => 'Administradores',
            'slug' => 'admin',
            'descricao' => 'Acesso total ao sistema.',
            'observacoes' => 'Perfis com acesso irrestrito.',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Financeiro',
            'slug' => 'financeiro',
            'descricao' => 'Acesso aos módulos financeiros.',
            'observacoes' => 'Contas a pagar, receber e fluxo de caixa.',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Operacional',
            'slug' => 'operacional',
            'descricao' => 'Acesso aos módulos de vendas, compras e estoque.',
            'observacoes' => 'Equipe de operação.',
            'ativo' => 'SIM',
        ],
        [
            'nome' => 'Fiscal',
            'slug' => 'fiscal',
            'descricao' => 'Acesso a arquivos fiscais e relatórios fiscais.',
            'observacoes' => 'Equipe contábil/fiscal.',
            'ativo' => 'SIM',
        ],
    ];

    /**
     * Exibe listagem de empresas para parametrizar
     */
    public function index(): void
    {
        // Verificar permissão de visualização
        if (!$this->canView('parametros')) {
            $this->response->forbidden('Você não tem permissão para visualizar parâmetros.');
            return;
        }

        // Busca todas as empresas cadastradas no tenant
        $empresas = $this->listarEmpresas();

        $this->view('parametros/index', [
            'empresas' => $empresas
        ]);
    }

    /**
     * Exibe página de parâmetros de uma empresa
     */
    public function edit(): void
    {
        $companyId = (int) $this->request->get('id');

        // Busca empresa
        $empresa = $this->buscarEmpresaPorId($companyId);

        if (!$empresa) {
            $this->redirect('/parametros');
            return;
        }

        // Garante módulos padrão cadastrados
        $this->garantirModulosPadrao();

        // Lista módulos cadastrados
        $modulosDisponiveis = $this->listarModulos();

        // Garante e lista grupos de acesso
        $this->garantirGruposAcessoPadrao();
        $gruposAcesso = $this->listarGruposAcesso($companyId);

        // Carrega parâmetros booleanos existentes
        $parametrosBooleanos = $this->buscarParametrosBooleanos($companyId);

        // Busca todos os parâmetros desta empresa com fallback
        $allParams = $this->listarParametrosEmpresa($companyId);

        // Organiza por categoria
        $parametros = $allParams;

        // Mapeamento de compatibilidade para view
        $aliases = [
            'general' => 'geral',
            'email' => 'email',
            'nfe' => 'nfe',
            'fiscal' => 'fiscal',
            'financeiro' => 'financeiro',
            'security' => 'seguranca'
        ];

        foreach ($parametros as $category => $params) {
            if (isset($aliases[$category])) {
                $parametros[$aliases[$category]] = $params;
            }
        }

        // Busca IPs válidos da empresa
        $ipsValidos = [];
        if ($this->tabelaExiste('valid_ips')) {
            $stmt = $this->db->prepare("
                SELECT id, ip_address, description, is_active, created_at
                FROM valid_ips
                WHERE company_id = ?
                ORDER BY created_at DESC
            ");
            $stmt->execute([$companyId]);
            $ipsValidos = $stmt->fetchAll(PDO::FETCH_ASSOC);
        }

        // Busca informações da assinatura
        $assinatura = null;
        try {
            $tenantId = $this->session->get('tenant_id');
            if ($tenantId) {
                $masterDb = $this->tenantManager->getMasterConnection();

                $stmt = $masterDb->prepare("
                    SELECT t.*, s.plan, s.status, s.amount, s.billing_cycle, s.next_billing_date, s.started_at
                    FROM tenants t
                    LEFT JOIN subscriptions s ON t.id = s.tenant_id
                    WHERE t.id = ?
                ");
                $stmt->execute([$tenantId]);
                $assinatura = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
            }
        } catch (\Throwable $e) {
            error_log('[Parametros] Erro ao carregar assinatura: ' . $e->getMessage());
        }

        $this->view('parametros/edit', [
            'empresa' => $empresa,
            'parametros' => $parametros,
            'parametrosBooleanos' => $parametrosBooleanos,
            'definicoesBooleanas' => $this->parametrosBooleanos,
            'modulos' => $modulosDisponiveis,
            'gruposAcesso' => $gruposAcesso,
            'ipsValidos' => $ipsValidos,
            'assinatura' => $assinatura
        ]);
    }

    /**
     * Salva parâmetros
     */
    public function save(): void
    {
        try {
            $companyId = (int) $this->request->post('company_id');
            $categoria = $this->request->post('categoria');
            $parametros = $this->request->post('parametros', []);

            foreach ($parametros as $name => $value) {
                // Verifica se já existe para esta empresa
                $stmt = $this->db->prepare("
                    SELECT id FROM system_parameters
                    WHERE company_id = ? AND param_key = ?
                ");
                $stmt->execute([$companyId, $name]);
                $exists = $stmt->fetch(PDO::FETCH_ASSOC);

                if ($exists) {
                    // Atualiza
                    $stmt = $this->db->prepare("
                        UPDATE system_parameters
                        SET param_value = ?, updated_at = NOW()
                        WHERE company_id = ? AND param_key = ?
                    ");
                    $stmt->execute([$value, $companyId, $name]);
                } else {
                    // Insere
                    $stmt = $this->db->prepare("
                        INSERT INTO system_parameters (company_id, param_category, param_key, param_value)
                        VALUES (?, ?, ?, ?)
                    ");
                    $stmt->execute([$companyId, $categoria, $name, $value]);
                }
            }

            $this->logActivity('update', 'system_parameters', 0, [
                'categoria' => $categoria,
                'parametros' => $parametros
            ]);

            $this->response->json([
                'success' => true,
                'message' => 'Parâmetros salvos com sucesso!'
            ]);
        } catch (\Exception $e) {
            $this->response->json([
                'success' => false,
                'message' => 'Erro ao salvar parâmetros: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Upload de logo/certificado
     */
    public function upload(): void
    {
        try {
            $companyId = (int) $this->request->post('company_id');
            $tipo = $this->request->post('tipo'); // 'logo' ou 'certificado'

            if (!isset($_FILES['arquivo']) || $_FILES['arquivo']['error'] !== UPLOAD_ERR_OK) {
                throw new \Exception('Nenhum arquivo enviado');
            }

            $arquivo = $_FILES['arquivo'];
            $extensao = pathinfo($arquivo['name'], PATHINFO_EXTENSION);

            // Validações
            if ($tipo === 'logo') {
                $extensoesPermitidas = ['jpg', 'jpeg', 'png', 'gif'];
                $tamanhoMax = 2 * 1024 * 1024; // 2MB
            } else {
                $extensoesPermitidas = ['pfx', 'p12'];
                $tamanhoMax = 5 * 1024 * 1024; // 5MB
            }

            if (!in_array(strtolower($extensao), $extensoesPermitidas)) {
                throw new \Exception('Formato de arquivo não permitido');
            }

            if ($arquivo['size'] > $tamanhoMax) {
                throw new \Exception('Arquivo muito grande');
            }

            // Define diretório
            $uploadDir = \ROOT_PATH . '/storage/uploads/';
            if (!is_dir($uploadDir)) {
                mkdir($uploadDir, 0755, true);
            }

            // Nome único
            $nomeArquivo = uniqid() . '_' . time() . '.' . $extensao;
            $caminhoCompleto = $uploadDir . $nomeArquivo;

            // Move arquivo
            if (!move_uploaded_file($arquivo['tmp_name'], $caminhoCompleto)) {
                throw new \Exception('Erro ao salvar arquivo');
            }

            // Salva no banco para a empresa específica
            $parametroNome = $tipo === 'logo' ? 'logo_empresa' : 'certificado_nfe';
            $categoria = $tipo === 'logo' ? 'general' : 'nfe';

            // Verifica se já existe
            $stmt = $this->db->prepare("
                SELECT id FROM system_parameters
                WHERE company_id = ? AND param_key = ?
            ");
            $stmt->execute([$companyId, $parametroNome]);
            $exists = $stmt->fetch(PDO::FETCH_ASSOC);

            if ($exists) {
                // Atualiza
                $stmt = $this->db->prepare("
                    UPDATE system_parameters
                    SET param_value = ?, updated_at = NOW()
                    WHERE company_id = ? AND param_key = ?
                ");
                $stmt->execute([$nomeArquivo, $companyId, $parametroNome]);
            } else {
                // Insere
                $stmt = $this->db->prepare("
                    INSERT INTO system_parameters (company_id, param_category, param_key, param_value)
                    VALUES (?, ?, ?, ?)
                ");
                $stmt->execute([$companyId, $categoria, $parametroNome, $nomeArquivo]);
            }

            $this->response->json([
                'success' => true,
                'message' => 'Arquivo enviado com sucesso!',
                'filename' => $nomeArquivo,
                'url' => '/storage/uploads/' . $nomeArquivo
            ]);
        } catch (\Exception $e) {
            $this->response->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Adiciona um IP válido
     */
    public function adicionarIpValido(): void
    {
        try {
            $companyId = (int) $this->request->post('company_id');
            $ipAddress = trim($this->request->post('ip_address'));
            $description = trim($this->request->post('description'));

            if (empty($ipAddress) || empty($description)) {
                $this->response->json(['success' => false, 'message' => 'IP e descrição são obrigatórios'], 400);
                return;
            }

            // Validar formato do IP
            if (!$this->validarFormatoIp($ipAddress)) {
                $this->response->json(['success' => false, 'message' => 'Formato de IP inválido'], 400);
                return;
            }

            // Verificar se já existe
            $stmt = $this->db->prepare("
                SELECT id FROM valid_ips
                WHERE company_id = ? AND ip_address = ?
            ");
            $stmt->execute([$companyId, $ipAddress]);
            if ($stmt->fetch(PDO::FETCH_ASSOC)) {
                $this->response->json(['success' => false, 'message' => 'Este IP já está cadastrado'], 400);
                return;
            }

            // Inserir novo IP
            $stmt = $this->db->prepare("
                INSERT INTO valid_ips (company_id, ip_address, description, is_active)
                VALUES (?, ?, ?, 1)
            ");
            $stmt->execute([$companyId, $ipAddress, $description]);

            $this->response->json(['success' => true, 'message' => 'IP adicionado com sucesso!']);
        } catch (\Exception $e) {
            $this->response->json(['success' => false, 'message' => 'Erro ao adicionar IP: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Remove um IP válido
     */
    public function removerIpValido(): void
    {
        try {
            $ipId = (int) $this->request->post('ip_id');
            $companyId = (int) $this->request->post('company_id');

            $stmt = $this->db->prepare("
                DELETE FROM valid_ips
                WHERE id = ? AND company_id = ?
            ");
            $stmt->execute([$ipId, $companyId]);

            if ($stmt->rowCount() > 0) {
                $this->response->json(['success' => true, 'message' => 'IP removido com sucesso!']);
            } else {
                $this->response->json(['success' => false, 'message' => 'IP não encontrado'], 404);
            }
        } catch (\Exception $e) {
            $this->response->json(['success' => false, 'message' => 'Erro ao remover IP: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Ativa/desativa um IP válido
     */
    public function toggleIpValido(): void
    {
        try {
            $ipId = (int) $this->request->post('ip_id');
            $companyId = (int) $this->request->post('company_id');
            $isActive = (int) $this->request->post('is_active');

            $stmt = $this->db->prepare("
                UPDATE valid_ips
                SET is_active = ?, updated_at = NOW()
                WHERE id = ? AND company_id = ?
            ");
            $stmt->execute([$isActive, $ipId, $companyId]);

            if ($stmt->rowCount() > 0) {
                $status = $isActive ? 'ativado' : 'desativado';
                $this->response->json(['success' => true, 'message' => "IP {$status} com sucesso!"]);
            } else {
                $this->response->json(['success' => false, 'message' => 'IP não encontrado'], 404);
            }
        } catch (\Exception $e) {
            $this->response->json(['success' => false, 'message' => 'Erro ao alterar status do IP: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Salva parâmetro booleano (sim/não) na tabela parametros.
     */
    public function salvarBoolean(): void
    {
        try {
            $empresaId = (int) $this->request->post('empresa_id');
            $modulo = $this->request->post('modulo');
            $chave = $this->request->post('chave');
            $valor = $this->request->post('valor') ? 1 : 0;

            if (!$empresaId || empty($modulo) || empty($chave)) {
                $this->response->json([
                    'success' => false,
                    'message' => 'Dados inválidos para salvar parâmetro.'
                ], 400);
                return;
            }

            if (!isset($this->parametrosBooleanos[$modulo]['itens'][$chave])) {
                $this->response->json([
                    'success' => false,
                    'message' => 'Parâmetro informado não é permitido.'
                ], 400);
                return;
            }

            $this->garantirTabelaParametros();

            $stmt = $this->db->prepare("
                INSERT INTO parametros (empresa_id, modulo, chave, valor, descricao, created_at, updated_at)
                VALUES (:empresa_id, :modulo, :chave, :valor, :descricao, NOW(), NOW())
                ON DUPLICATE KEY UPDATE valor = VALUES(valor), updated_at = NOW()
            ");

            $descricao = $this->parametrosBooleanos[$modulo]['itens'][$chave]['label'] ?? null;

            $stmt->execute([
                'empresa_id' => $empresaId,
                'modulo' => $modulo,
                'chave' => $chave,
                'valor' => $valor,
                'descricao' => $descricao,
            ]);

            $this->logActivity('update', 'parametros', $empresaId, [
                'modulo' => $modulo,
                'chave' => $chave,
                'valor' => $valor,
            ]);

            $this->response->json([
                'success' => true,
                'message' => 'Parâmetro atualizado com sucesso.'
            ]);
        } catch (\Exception $e) {
            $this->response->json([
                'success' => false,
                'message' => 'Erro ao salvar parâmetro: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Busca parâmetros booleanos salvos para a empresa.
     */
    private function buscarParametrosBooleanos(int $empresaId): array
    {
        try {
            $this->garantirTabelaParametros();

            $stmt = $this->db->prepare("
                SELECT modulo, chave, valor
                FROM parametros
                WHERE empresa_id = :empresa_id
            ");
            $stmt->execute(['empresa_id' => $empresaId]);
            $registros = $stmt->fetchAll(PDO::FETCH_ASSOC);

            $resultado = [];
            foreach ($registros as $registro) {
                $resultado[$registro['modulo']][$registro['chave']] = (int) $registro['valor'];
            }

            return $resultado;
        } catch (\PDOException $e) {
            if ($e->getCode() === '42S02') {
                error_log('Tabela parametros não encontrada, retornando vazia.');
                return [];
            }
            throw $e;
        }
    }

    /**
     * Lista parâmetros gerais da empresa com fallback entre system_parameters e parametros.
     */
    private function listarParametrosEmpresa(int $companyId): array
    {
        try {
            if ($this->tabelaExiste('system_parameters')) {
                $stmt = $this->db->prepare("
                    SELECT param_category, param_key, param_value
                    FROM system_parameters
                    WHERE company_id = ?
                    ORDER BY param_category, param_key
                ");
                $stmt->execute([$companyId]);
                $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

                $resultado = [];
                foreach ($rows as $row) {
                    $categoria = $row['param_category'] ?? 'geral';
                    $resultado[$categoria][$row['param_key']] = $row['param_value'];
                }
                return $resultado;
            }

            if ($this->tabelaExiste('parametros')) {
                $stmt = $this->db->prepare("
                    SELECT modulo, chave, valor
                    FROM parametros
                    WHERE empresa_id = ?
                    ORDER BY modulo, chave
                ");
                $stmt->execute([$companyId]);
                $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

                $resultado = [];
                foreach ($rows as $row) {
                    $modulo = $row['modulo'] ?? 'geral';
                    $resultado[$modulo][$row['chave']] = (int) $row['valor'];
                }
                return $resultado;
            }
        } catch (\Throwable $e) {
            error_log('[Parametros] Erro ao listar parâmetros: ' . $e->getMessage());
        }

        return [];
    }

    /**
     * Valida formato do IP (IPv4, IPv6 ou CIDR)
     */
    private function validarFormatoIp(string $ip): bool
    {
        // IPv4 simples
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
            return true;
        }

        // IPv6 simples
        if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            return true;
        }

        // CIDR IPv4 (ex: 192.168.1.0/24)
        if (preg_match('/^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/', $ip)) {
            $parts = explode('/', $ip);
            if (
                filter_var($parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) &&
                $parts[1] >= 0 && $parts[1] <= 32
            ) {
                return true;
            }
        }

        // CIDR IPv6 (ex: 2001:db8::/32)
        if (preg_match('/^[0-9a-fA-F:]+::?[0-9a-fA-F:]*\/\d{1,3}$/', $ip)) {
            $parts = explode('/', $ip);
            if (
                filter_var($parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) &&
                $parts[1] >= 0 && $parts[1] <= 128
            ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Garante que a tabela parametros exista (fallback para ambientes sem migration).
     */
    private function garantirTabelaParametros(): void
    {
        static $verificado = false;

        if ($verificado) {
            return;
        }

        $sql = "CREATE TABLE IF NOT EXISTS parametros (
            id INT UNSIGNED NOT NULL AUTO_INCREMENT,
            empresa_id INT UNSIGNED NOT NULL,
            modulo VARCHAR(50) NOT NULL,
            chave VARCHAR(100) NOT NULL,
            valor TINYINT(1) NOT NULL DEFAULT 0,
            descricao VARCHAR(255) NULL,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            UNIQUE KEY uk_parametros_empresa_chave (empresa_id, chave),
            KEY idx_parametros_empresa (empresa_id),
            KEY idx_parametros_modulo (modulo)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";

        $this->db->exec($sql);
        $verificado = true;
    }

    /**
     * Garante que os módulos padrão estejam cadastrados na tabela modulos.
     */
    private function garantirModulosPadrao(): void
    {
        if (!$this->tabelaExiste('modulos')) {
            return;
        }

        try {
            $stmt = $this->db->query('SELECT nome_interface FROM modulos');
            $existentes = $stmt->fetchAll(PDO::FETCH_COLUMN) ?: [];
            // Filtrar valores nulos antes de aplicar strtolower
            $existentes = array_filter($existentes, function($value) {
                return $value !== null && $value !== '';
            });
            $existentesMap = array_flip(array_map('strtolower', $existentes));

            $insert = $this->db->prepare("
                INSERT INTO modulos (nome, nome_interface, descricao, status, ativo, criada_em, atualizada_em)
                VALUES (:nome, :nome_interface, :descricao, :status, :ativo, NOW(), NOW())
            ");

            foreach ($this->modulosPadrao as $modulo) {
                $chave = strtolower($modulo['nome_interface']);
                if (isset($existentesMap[$chave])) {
                    continue;
                }

                $insert->execute([
                    'nome' => $modulo['nome'],
                    'nome_interface' => $modulo['nome_interface'],
                    'descricao' => $modulo['descricao'],
                    'status' => $modulo['status'],
                    'ativo' => $modulo['ativo'],
                ]);

                $existentesMap[$chave] = true;
            }
        } catch (\Throwable $e) {
            error_log('[Parametros] Erro ao popular módulos padrão: ' . $e->getMessage());
        }
    }

    private function garantirGruposAcessoPadrao(): void
    {
        if (!$this->tabelaExiste('grupos_acesso')) {
            return;
        }

        try {
            $colunas = array_flip($this->colunasTabela('grupos_acesso'));
            if (!isset($colunas['nome']) || !isset($colunas['slug'])) {
                return;
            }

            $stmt = $this->db->query('SELECT LOWER(slug) FROM grupos_acesso');
            $existentes = $stmt->fetchAll(PDO::FETCH_COLUMN) ?: [];
            $existentes = array_flip($existentes);

            $insert = $this->db->prepare("
                INSERT INTO grupos_acesso (nome, slug)
                VALUES (:nome, :slug)
            ");

            foreach ($this->gruposAcessoPadrao as $grupo) {
                $slug = strtolower($grupo['slug']);
                if (isset($existentes[$slug])) {
                    continue;
                }

                $insert->execute([
                    'nome' => $grupo['nome'],
                    'slug' => $grupo['slug'],
                ]);
            }
        } catch (\Throwable $e) {
            error_log('[Parametros] Erro ao garantir grupos padrão: ' . $e->getMessage());
        }
    }

    private function listarGruposAcesso(int $companyId): array
    {
        if (!$this->tabelaExiste('grupos_acesso')) {
            return [];
        }

        try {
            $colunasGrupo = $this->colunasTabela('grupos_acesso');
            $select = ['ga.id', 'ga.nome', 'ga.slug'];

            if (in_array('descricao', $colunasGrupo, true)) {
                $select[] = 'ga.descricao';
            } else {
                $select[] = "'' AS descricao";
            }

            if (in_array('observacoes', $colunasGrupo, true)) {
                $select[] = 'ga.observacoes';
            } else {
                $select[] = "'' AS observacoes";
            }

            if (in_array('ativo', $colunasGrupo, true)) {
                $select[] = 'ga.ativo';
            } else {
                $select[] = "'SIM' AS ativo";
            }

            $join = '';
            $selectModulos = "'' AS modulos";
            $params = [];

            $temRelacionamento = $this->tabelaExiste('grupos_acesso_modulos') && $this->tabelaExiste('modulos');
            if ($temRelacionamento) {
                $join = "
                    LEFT JOIN grupos_acesso_modulos gam ON gam.grupo_id = ga.id
                    LEFT JOIN modulos m ON m.id = gam.modulo_id
                ";
                $selectModulos = 'GROUP_CONCAT(m.nome ORDER BY m.nome SEPARATOR \'||\') AS modulos';
            }

            $select[] = $selectModulos;

            $where = '';
            if (in_array('company_id', $colunasGrupo, true)) {
                $where = 'WHERE ga.company_id IS NULL OR ga.company_id = :company_id';
                $params['company_id'] = $companyId;
            }

            $sql = sprintf(
                'SELECT %s FROM grupos_acesso ga %s %s GROUP BY ga.id ORDER BY ga.nome ASC',
                implode(', ', $select),
                $join,
                $where
            );

            $stmt = $this->db->prepare($sql);
            $stmt->execute($params);
            $grupos = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];

            foreach ($grupos as &$grupo) {
                if (!isset($grupo['descricao'])) {
                    $grupo['descricao'] = '';
                }
                if (!isset($grupo['observacoes'])) {
                    $grupo['observacoes'] = '';
                }
                if (!isset($grupo['ativo'])) {
                    $grupo['ativo'] = 'SIM';
                }

                $grupo['modulos'] = !empty($grupo['modulos'])
                    ? explode('||', $grupo['modulos'])
                    : [];
            }

            return $grupos;
        } catch (\Throwable $e) {
            error_log('[Parametros] Erro ao listar grupos de acesso: ' . $e->getMessage());
            return [];
        }
    }

    /**
     * Atualiza status ativo de um módulo.
     */
    public function toggleModulo(): void
    {
        if (!$this->tabelaExiste('modulos')) {
            $this->response->json(['success' => false, 'message' => 'Tabela de módulos não encontrada.'], 404);
            return;
        }

        try {
            $moduloId = (int) $this->request->post('id');
            $ativoEntrada = strtoupper((string) $this->request->post('ativo'));
            $valorAtivo = in_array($ativoEntrada, ['1', 'SIM', 'TRUE', 'ON'], true) ? 'SIM' : 'NAO';

            if ($moduloId <= 0) {
                $this->response->json(['success' => false, 'message' => 'Módulo inválido.'], 400);
                return;
            }

            $stmt = $this->db->prepare("
                UPDATE modulos
                SET ativo = :ativo,
                    atualizada_em = NOW()
                WHERE id = :id
                LIMIT 1
            ");
            $stmt->execute([
                'ativo' => $valorAtivo,
                'id' => $moduloId,
            ]);

            if ($stmt->rowCount() === 0) {
                $this->response->json(['success' => false, 'message' => 'Módulo não encontrado.'], 404);
                return;
            }

            $this->response->json([
                'success' => true,
                'message' => $valorAtivo === 'SIM' ? 'Módulo ativado com sucesso.' : 'Módulo desativado com sucesso.',
            ]);
        } catch (\Throwable $e) {
            $this->response->json([
                'success' => false,
                'message' => 'Erro ao atualizar módulo: ' . $e->getMessage(),
            ], 500);
        }
    }

    private function listarEmpresas(): array
    {
        $tabela = $this->tabelaEmpresas();
        $colunas = $this->colunasEmpresas();

        $select = sprintf(
            "SELECT id,
                %s AS razao_social,
                %s AS nome_fantasia,
                %s AS cnpj,
                %s AS ativo
            FROM %s
            ORDER BY %s",
            $colunas['razao_social'],
            $colunas['nome_fantasia'],
            $colunas['cnpj'],
            $colunas['ativo'],
            $tabela,
            $colunas['ordenacao']
        );

        error_log('[Parametros] Consulta empresas: ' . $select);
        $stmt = $this->db->query($select);
        $empresas = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];

        // Normaliza valores booleanos
        foreach ($empresas as &$empresa) {
            $empresa['razao_social'] = $empresa['razao_social'] ?? '';
            $empresa['nome_fantasia'] = $empresa['nome_fantasia'] ?? '';
            $empresa['cnpj'] = $empresa['cnpj'] ?? '';
            $empresa['ativo'] = $empresa['ativo'] ?? '';
        }

        return $empresas;
    }

    private function buscarEmpresaPorId(int $id): ?array
    {
        // Cache estático para evitar consultas repetidas na mesma requisição
        static $cache = [];

        if (isset($cache[$id])) {
            return $cache[$id];
        }

        $tabela = $this->tabelaEmpresas();
        $colunas = $this->colunasEmpresas();

        $select = sprintf(
            "SELECT id,
                %s AS razao_social,
                %s AS nome_fantasia,
                %s AS cnpj,
                %s AS ativo
            FROM %s
            WHERE id = :id
            LIMIT 1",
            $colunas['razao_social'],
            $colunas['nome_fantasia'],
            $colunas['cnpj'],
            $colunas['ativo'],
            $tabela
        );

        error_log('[Parametros] Buscar empresa por ID: ' . $select . ' => ' . $id);
        $stmt = $this->db->prepare($select);
        $stmt->execute(['id' => $id]);
        $empresa = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;

        if ($empresa) {
            $empresa['razao_social'] = $empresa['razao_social'] ?? '';
            $empresa['nome_fantasia'] = $empresa['nome_fantasia'] ?? '';
            $empresa['cnpj'] = $empresa['cnpj'] ?? '';
            $empresa['ativo'] = $empresa['ativo'] ?? '';
        }

        // Armazena no cache
        $cache[$id] = $empresa;

        return $empresa;
    }

    private function tabelaEmpresas(): string
    {
        static $tabela = null;

        if ($tabela !== null) {
            return $tabela;
        }

        foreach (['empresas', 'companies'] as $possivel) {
            if ($this->tabelaExiste($possivel)) {
                return $tabela = $possivel;
            }
        }

        error_log('[Parametros] Nenhuma tabela de empresas encontrada.');
        throw new \RuntimeException('Tabela de empresas não encontrada.');
    }

    private function colunasEmpresas(): array
    {
        static $map = null;

        if ($map !== null) {
            return $map;
        }

        $tabela = $this->tabelaEmpresas();
        $stmt = $this->db->query("SHOW COLUMNS FROM {$tabela}");
        $colunas = $stmt->fetchAll(PDO::FETCH_COLUMN) ?: [];

        $tem = fn(string $coluna) => in_array($coluna, $colunas, true);

        $map = [
            'razao_social' => $tem('razao_social') ? 'razao_social' : ($tem('name') ? 'name' : "''"),
            'nome_fantasia' => $tem('nome_fantasia') ? 'nome_fantasia' : ($tem('trade_name') ? 'trade_name' : "''"),
            'cnpj' => $tem('cnpj') ? 'cnpj' : ($tem('document') ? 'document' : "''"),
            'ativo' => $tem('ativo') ? 'ativo' : ($tem('is_active') ? 'is_active' : "''"),
            'ordenacao' => $tem('razao_social') ? 'razao_social' : ($tem('name') ? 'name' : 'id')
        ];

        return $map;
    }

    private function listarModulos(): array
    {
        if (!$this->tabelaExiste('modulos')) {
            return [];
        }

        try {
            $stmt = $this->db->query("
                SELECT id, nome, nome_interface, descricao, status, ativo, criada_em, atualizada_em
                FROM modulos
                ORDER BY nome ASC
            ");
            return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        } catch (\Throwable $e) {
            error_log('[Parametros] Erro ao listar módulos: ' . $e->getMessage());
            return [];
        }
    }

    private function colunasTabela(string $tabela): array
    {
        static $cache = [];

        if (isset($cache[$tabela])) {
            return $cache[$tabela];
        }

        try {
            $stmt = $this->db->query("SHOW COLUMNS FROM {$tabela}");
            $cache[$tabela] = $stmt->fetchAll(PDO::FETCH_COLUMN) ?: [];
        } catch (\Throwable $e) {
            $cache[$tabela] = [];
        }

        return $cache[$tabela];
    }

    private function tabelaExiste(string $nome): bool
    {
        $nome = preg_replace('/[^a-zA-Z0-9_]/', '', $nome);
        $stmt = $this->db->query("SHOW TABLES LIKE '{$nome}'");
        return (bool) $stmt->fetchColumn();
    }
}
