/**
 * Admin Panel - API Client & Utils
 * @version 2.0.0
 */

const API_BASE = 'api/admin.php';

// ================================
// Estado Global
// ================================
window.AdminState = {
    token: localStorage.getItem('admin_token'),
    currentUser: null,
    sites: [],
    currentSiteId: null,
    charts: {
        sites: null,
        events: null
    }
};

// ================================
// API Client
// ================================
const Api = {
    /**
     * Fazer requisição à API
     */
    async request(action, method = 'GET', body = null, showSpinner = false) {
        if (showSpinner) UI.showLoading();
        
        try {
            const options = {
                method,
                headers: {
                    'Content-Type': 'application/json'
                }
            };
            
            if (AdminState.token) {
                options.headers['Authorization'] = `Bearer ${AdminState.token}`;
            }
            
            if (body && method !== 'GET') {
                options.body = JSON.stringify(body);
            }
            
            const response = await fetch(`${API_BASE}?action=${action}`, options);
            const data = await response.json();

            // Tratar 401 de forma diferente para login vs outras rotas
            if (response.status === 401) {
                // Para login, retornar os dados (contém mensagem de erro real)
                if (action === 'login') {
                    return data;
                }
                // Para outras rotas, significa sessão expirada
                Auth.logout();
                throw new Error('Session expired');
            }

            return data;
        } finally {
            if (showSpinner) UI.hideLoading();
        }
    },

    /**
     * GET request
     */
    async get(action, showSpinner = false) {
        return this.request(action, 'GET', null, showSpinner);
    },

    /**
     * POST request
     */
    async post(action, data, showSpinner = false) {
        return this.request(action, 'POST', data, showSpinner);
    },

    /**
     * PUT request
     */
    async put(action, data, showSpinner = false) {
        return this.request(action, 'PUT', data, showSpinner);
    },

    /**
     * DELETE request
     */
    async delete(action, showSpinner = false) {
        return this.request(action, 'DELETE', null, showSpinner);
    }
};

// ================================
// UI Utilities
// ================================
const UI = {
    /**
     * Mostrar loading overlay
     */
    showLoading(text = 'Carregando...') {
        const overlay = document.getElementById('loading-overlay');
        const textEl = document.getElementById('loading-text');
        if (textEl) textEl.textContent = text;
        if (overlay) overlay.classList.add('show');
    },

    /**
     * Esconder loading overlay
     */
    hideLoading() {
        const overlay = document.getElementById('loading-overlay');
        if (overlay) overlay.classList.remove('show');
    },

    /**
     * Mostrar toast notification
     */
    toast(message, type = 'info', duration = 3000) {
        let container = document.getElementById('toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'toast-container';
            container.className = 'toast-container';
            document.body.appendChild(container);
        }
        
        const toast = document.createElement('div');
        toast.className = `toast ${type}`;
        
        const icons = {
            success: 'fa-check-circle',
            error: 'fa-times-circle',
            warning: 'fa-exclamation-triangle',
            info: 'fa-info-circle'
        };
        
        toast.innerHTML = `
            <i class="fas ${icons[type] || icons.info}"></i>
            <span>${message}</span>
        `;
        
        container.appendChild(toast);
        
        setTimeout(() => {
            toast.style.animation = 'slideIn 0.3s ease reverse';
            setTimeout(() => toast.remove(), 300);
        }, duration);
    },

    /**
     * Confirmar ação
     */
    confirm(message, onConfirm, onCancel = null) {
        if (window.confirm(message)) {
            if (onConfirm) onConfirm();
        } else {
            if (onCancel) onCancel();
        }
    },

    /**
     * Prompt com validação
     */
    prompt(message, expectedValue, onSuccess) {
        const result = window.prompt(message);
        if (result === expectedValue) {
            onSuccess();
        } else if (result !== null) {
            UI.toast('Operação cancelada. Valor incorreto.', 'warning');
        }
    },

    /**
     * Copiar texto para clipboard
     */
    async copyToClipboard(text) {
        try {
            await navigator.clipboard.writeText(text);
            UI.toast('Copiado!', 'success', 1500);
            return true;
        } catch (err) {
            // Fallback
            const textarea = document.createElement('textarea');
            textarea.value = text;
            document.body.appendChild(textarea);
            textarea.select();
            document.execCommand('copy');
            document.body.removeChild(textarea);
            UI.toast('Copiado!', 'success', 1500);
            return true;
        }
    },

    /**
     * Adicionar classe loading a um botão
     */
    btnLoading(btn, loading = true) {
        if (loading) {
            btn.classList.add('loading');
            btn.disabled = true;
        } else {
            btn.classList.remove('loading');
            btn.disabled = false;
        }
    },

    /**
     * Mostrar/esconder elemento
     */
    show(selector) {
        const el = typeof selector === 'string' ? document.querySelector(selector) : selector;
        if (el) el.classList.remove('hidden');
    },

    hide(selector) {
        const el = typeof selector === 'string' ? document.querySelector(selector) : selector;
        if (el) el.classList.add('hidden');
    },

    /**
     * Atualizar texto de um elemento
     */
    setText(selector, text) {
        const el = document.querySelector(selector);
        if (el) el.textContent = text;
    },

    /**
     * Atualizar HTML de um elemento
     */
    setHtml(selector, html) {
        const el = document.querySelector(selector);
        if (el) el.innerHTML = html;
    },

    /**
     * Obter valor de input
     */
    getValue(selector) {
        const el = document.querySelector(selector);
        return el ? el.value : '';
    },

    /**
     * Definir valor de input
     */
    setValue(selector, value) {
        const el = document.querySelector(selector);
        if (el) el.value = value;
    }
};

// ================================
// Formatters
// ================================
const Format = {
    /**
     * Formatar valor em BRL
     */
    brl(value) {
        return new Intl.NumberFormat('pt-BR', {
            style: 'currency',
            currency: 'BRL'
        }).format(value || 0);
    },

    /**
     * Formatar número
     */
    number(value) {
        return new Intl.NumberFormat('pt-BR').format(value || 0);
    },

    /**
     * Formatar data
     */
    date(dateStr) {
        if (!dateStr) return '-';
        return new Date(dateStr).toLocaleDateString('pt-BR');
    },

    /**
     * Formatar data e hora
     */
    datetime(dateStr) {
        if (!dateStr) return '-';
        return new Date(dateStr).toLocaleString('pt-BR');
    },

    /**
     * Formatar hora
     */
    time(dateStr) {
        if (!dateStr) return '-';
        return new Date(dateStr).toLocaleTimeString('pt-BR', {
            hour: '2-digit',
            minute: '2-digit'
        });
    },

    /**
     * Formatar tipo de evento para exibição
     */
    eventType(type) {
        const types = {
            'page_view': '👁️ Visualização',
            'visit': '👁️ Visualização',
            'index_view': '🏠 Página Inicial',
            'consulta': '🔍 Consulta',
            'consulta_placa': '🚗 Consulta Placa',
            'consulta_debitos': '🔍 Consulta Débitos',
            'consulta_iniciada': '🔎 Consulta Iniciada',
            'consulta_realizada': '✅ Consulta Realizada',
            'consulta_erro': '❌ Erro Consulta',
            'cnpj_consulta': '🏢 CNPJ',
            'pix_gerado': '💰 PIX Gerado',
            'pix_iniciado': '💰 PIX Gerado',
            'pix_generated': '💰 PIX Gerado',
            'pix_generation_success': '✅ PIX Sucesso',
            'pix_copiado': '📋 PIX Copiado',
            'pix_codigo_copiado': '📋 PIX Copiado',
            'pix_modal_aberto': '📱 Modal PIX Aberto',
            'pix_modal_fechado': '❌ Modal PIX Fechado',
            'debitos_view': '📄 Página Débitos',
            'debito_selecionado': '📋 Débito Selecionado',
            'pagamento_iniciado': '💳 Pagamento Iniciado',
            'payment_confirmed': '✅ Pagamento Confirmado',
            'erro': '❌ Erro',
            'session_start': '🚀 Sessão Iniciada'
        };
        return types[type] || type;
    },

    /**
     * Obter classe de badge para tipo de evento
     */
    eventBadgeClass(type) {
        const classes = {
            'page_view': 'bg-info',
            'visit': 'bg-info',
            'index_view': 'bg-info',
            'consulta': 'bg-primary',
            'consulta_placa': 'bg-primary',
            'consulta_debitos': 'bg-primary',
            'consulta_iniciada': 'bg-secondary',
            'consulta_realizada': 'bg-success',
            'consulta_erro': 'bg-danger',
            'cnpj_consulta': 'bg-dark',
            'pix_gerado': 'bg-success',
            'pix_iniciado': 'bg-success',
            'pix_generated': 'bg-success',
            'pix_generation_success': 'bg-success',
            'pix_copiado': 'bg-warning',
            'pix_codigo_copiado': 'bg-warning',
            'pix_modal_aberto': 'bg-info',
            'pix_modal_fechado': 'bg-secondary',
            'debitos_view': 'bg-info',
            'debito_selecionado': 'bg-purple',
            'pagamento_iniciado': 'bg-warning',
            'payment_confirmed': 'bg-success',
            'erro': 'bg-danger',
            'session_start': 'bg-secondary'
        };
        return classes[type] || 'bg-secondary';
    },

    /**
     * Mascarar API Key
     */
    maskApiKey(key) {
        if (!key || key.length < 12) return key;
        return key.substring(0, 8) + '...' + key.substring(key.length - 4);
    },

    /**
     * Formatar localização
     */
    location(city, region) {
        if (!city) return '-';
        return region ? `${city}, ${region}` : city;
    }
};

// ================================
// Helpers
// ================================
const Helpers = {
    /**
     * Gerar senha aleatória
     */
    generatePassword(length = 12) {
        const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%';
        let password = '';
        for (let i = 0; i < length; i++) {
            password += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return password;
    },

    /**
     * Debounce function
     */
    debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    },

    /**
     * Throttle function
     */
    throttle(func, limit) {
        let inThrottle;
        return function(...args) {
            if (!inThrottle) {
                func.apply(this, args);
                inThrottle = true;
                setTimeout(() => inThrottle = false, limit);
            }
        };
    },

    /**
     * Verificar se é super admin
     */
    isSuperAdmin() {
        return AdminState.currentUser?.is_super_admin === true;
    },

    /**
     * Obter ID do site do usuário
     */
    getUserSiteId() {
        return AdminState.currentUser?.site_id || null;
    },

    /**
     * Query string builder
     */
    buildQuery(params) {
        return Object.entries(params)
            .filter(([_, v]) => v !== null && v !== undefined && v !== '')
            .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
            .join('&');
    }
};

// ================================
// Modal Lazy Loader
// ================================
const Modal = {
    _cache: {},
    _container: null,

    /**
     * Inicializa container de modais
     */
    init() {
        if (!this._container) {
            this._container = document.getElementById('modals-container');
            if (!this._container) {
                this._container = document.createElement('div');
                this._container.id = 'modals-container';
                document.body.appendChild(this._container);
            }
        }
    },

    /**
     * Carrega e exibe um modal
     */
    async load(name) {
        this.init();

        // Verificar se já está no DOM
        let modal = document.getElementById(`modal-${name}`);
        if (modal) {
            return new bootstrap.Modal(modal);
        }

        // Verificar cache
        if (this._cache[name]) {
            this._container.insertAdjacentHTML('beforeend', this._cache[name]);
            modal = document.getElementById(`modal-${name}`);
            return new bootstrap.Modal(modal);
        }

        // Carregar do servidor
        try {
            const response = await fetch(`assets/modals/${name}.html`);
            if (!response.ok) throw new Error('Modal not found');

            const html = await response.text();
            this._cache[name] = html;
            this._container.insertAdjacentHTML('beforeend', html);

            modal = document.getElementById(`modal-${name}`);
            if (modal) {
                return new bootstrap.Modal(modal);
            }
        } catch (error) {
            // Silenciar em produção
        }
        return null;
    },

    /**
     * Fecha e remove modal do DOM
     */
    close(name) {
        const modal = document.getElementById(`modal-${name}`);
        if (modal) {
            const bsModal = bootstrap.Modal.getInstance(modal);
            if (bsModal) bsModal.hide();
        }
    }
};

// Expor globalmente para compatibilidade
window.Api = Api;
window.UI = UI;
window.Format = Format;
window.Helpers = Helpers;
window.Modal = Modal;

// Funções de compatibilidade com código legado
window.apiRequest = (action, method, body, showSpinner) => Api.request(action, method, body, showSpinner);
window.showLoading = UI.showLoading;
window.hideLoading = UI.hideLoading;
window.formatBRL = Format.brl;
window.formatEventType = Format.eventType;
window.getBadgeClass = Format.eventBadgeClass;
window.copyToClipboard = (id) => {
    const el = document.getElementById(id);
    if (el) UI.copyToClipboard(el.value);
};
/**
 * Admin Panel - Authentication Module
 * @version 2.0.0
 */

const Auth = {
    _isLoading: false,

    /**
     * Fazer login
     */
    async login(username, password) {
        if (this._isLoading) return;
        this._isLoading = true;

        const btn = document.querySelector('#login-form .btn-login');
        const errorDiv = document.getElementById('login-error');

        UI.btnLoading(btn, true);
        UI.hide(errorDiv);

        try {
            const data = await Api.post('login', { username, password });

            if (data.status === 'success' && data.data) {
                AdminState.token = data.data.token;
                localStorage.setItem('admin_token', data.data.token);
                UI.toast('Login realizado com sucesso!', 'success');
                await this.showDashboard();
            } else {
                errorDiv.textContent = data.message || 'Credenciais inválidas';
                UI.show(errorDiv);
            }
        } catch (error) {
            errorDiv.textContent = 'Erro ao conectar com o servidor';
            UI.show(errorDiv);
        } finally {
            UI.btnLoading(btn, false);
            this._isLoading = false;
        }
    },

    /**
     * Fazer logout
     */
    logout() {
        AdminState.token = null;
        AdminState.currentUser = null;
        localStorage.removeItem('admin_token');
        
        UI.hide('#dashboard-page');
        UI.show('#login-page');
        
        // Limpar formulário
        document.getElementById('login-form')?.reset();
        UI.hide('#login-error');
    },

    /**
     * Carregar informações do usuário atual
     */
    async loadCurrentUser() {
        try {
            const data = await Api.get('me');

            if (data.status === 'success') {
                AdminState.currentUser = data.data;
                this.updateUIForUser();
                return data.data;
            }
        } catch (error) {
            // Silenciar em produção
        }
        return null;
    },

    /**
     * Atualizar UI baseado no tipo de usuário
     */
    updateUIForUser() {
        const user = AdminState.currentUser;
        if (!user) return;
        
        const isSuperAdmin = user.is_super_admin;
        
        // Mostrar/esconder elementos admin-only
        document.querySelectorAll('.admin-only').forEach(el => {
            el.style.display = isSuperAdmin ? '' : 'none';
        });
        
        // Mostrar/esconder elementos site-user-only
        document.querySelectorAll('.site-user-only').forEach(el => {
            el.style.display = !isSuperAdmin && user.site_id ? '' : 'none';
        });
        
        // Se for usuário de site, mostrar nome do site
        if (!isSuperAdmin && user.site_name) {
            UI.setText('#user-site-name', user.site_name);
        }
    },

    /**
     * Verificar se está autenticado
     */
    isAuthenticated() {
        return !!AdminState.token;
    },

    /**
     * Obter token atual
     */
    getToken() {
        return AdminState.token || localStorage.getItem('admin_token');
    },

    /**
     * Obter usuário atual
     */
    getCurrentUser() {
        return AdminState.currentUser;
    },

    /**
     * Mostrar dashboard após login
     * Delega para Dashboard.show() para evitar duplicação de lógica
     */
    async showDashboard() {
        // Usar Dashboard.show() que já tem toda a lógica correta
        if (typeof Dashboard !== 'undefined' && Dashboard.show) {
            await Dashboard.show();
        }
    },

    /**
     * Verificar autenticação no carregamento
     */
    async checkAuth() {
        if (this.isAuthenticated()) {
            // Esconder login imediatamente para evitar flash
            document.getElementById('login-page')?.classList.add('hidden');

            try {
                const user = await this.loadCurrentUser();
                if (user) {
                    await this.showDashboard();
                    return;
                }
            } catch (error) {
                // Token inválido
            }
            // Token expirado - fazer logout
            this.logout();
        }
    },

    /**
     * Inicializar eventos de autenticação
     */
    init() {
        // Login form
        document.getElementById('login-form')?.addEventListener('submit', (e) => {
            e.preventDefault();
            const username = UI.getValue('#login-username');
            const password = UI.getValue('#login-password');
            this.login(username, password);
        });
        
        // Verificar autenticação existente
        this.checkAuth();
    }
};

// Expor globalmente
window.Auth = Auth;

// Funções de compatibilidade
window.logout = () => Auth.logout();
window.loadCurrentUser = () => Auth.loadCurrentUser();

// Variável global para compatibilidade
window.currentUserInfo = null;
Object.defineProperty(window, 'currentUserInfo', {
    get: () => AdminState.currentUser
});
/**
 * Dashboard Module
 * Gerencia o painel principal, estatísticas e gráficos
 */
const Dashboard = {
    // Referências dos gráficos
    sitesChart: null,
    eventsChart: null,

    // Cache e controle de loading
    _loading: false,
    _lastLoad: 0,
    _cacheTime: 30000, // 30 segundos de cache
    _abortController: null,

    /**
     * Exibe o dashboard e carrega dados
     */
    async show() {
        UI.showLoading('Carregando painel...');

        document.getElementById('login-page').classList.add('hidden');
        document.getElementById('dashboard-page').classList.remove('hidden');

        // Carregar usuário logado (se ainda não foi carregado)
        if (!AdminState.currentUser) {
            await Auth.loadCurrentUser();
        }

        // Inicializar gráficos primeiro (vazios)
        this.initCharts();

        try {
            // Carregar dados em paralelo
            await Promise.all([this.load(), Sites.load()]);

            // Popular seletores após sites carregados
            Users.populateSiteSelectors();

            // Carregar API Tokens para seletores
            if (typeof ApiTokens !== 'undefined' && ApiTokens.loadForSelectors) {
                ApiTokens.loadForSelectors();
            }
        } catch (error) {
            // Silenciar em produção
        } finally {
            UI.hideLoading();
        }
    },

    /**
     * Carrega dados do dashboard
     * OTIMIZADO: Evita chamadas duplicadas com cache e AbortController
     */
    async load(force = false) {
        const now = Date.now();

        // Usar cache se não forçado e dados recentes
        if (!force && (now - this._lastLoad) < this._cacheTime) {
            return;
        }

        // Cancelar requisição anterior se existir
        if (this._abortController) {
            this._abortController.abort();
        }

        // Evitar chamadas duplicadas
        if (this._loading && !force) {
            return;
        }

        this._loading = true;
        this._abortController = new AbortController();

        try {
            const data = await Api.get('dashboard');

            if (data.status === 'success') {
                this._lastLoad = Date.now();
                this.renderStats(data.data.summary);
                this.updateCharts(data.data);
                this.renderRecentEvents(data.data.recent_events || []);
            }
        } catch (error) {
            if (error.name !== 'AbortError') {
                // Silenciar erros em produção
            }
        } finally {
            this._loading = false;
            this._abortController = null;
        }
    },
    
    /**
     * Renderiza estatísticas do dashboard
     */
    renderStats(summary) {
        document.getElementById('stat-sites').textContent = summary.total_sites;
        document.getElementById('stat-visits-unique').textContent = summary.unique_visitors_today || 0;
        document.getElementById('stat-visits-total').textContent = `${summary.events_today || 0} acessos totais`;
        document.getElementById('stat-pix').textContent = summary.pix_today;
        document.getElementById('stat-pix-value').textContent = Format.brl(summary.pix_value_today);
        document.getElementById('stat-consultas').textContent = summary.consultas_today || 0;
        
        // Badge de eventos na sidebar
        document.getElementById('events-count').textContent = summary.events_today || 0;
        
        // Última atualização
        document.getElementById('last-update').textContent = new Date().toLocaleTimeString('pt-BR');
    },
    
    /**
     * Renderiza eventos recentes na tabela
     */
    renderRecentEvents(events) {
        const tbody = document.getElementById('recent-events-body');
        
        if (!events.length) {
            tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Nenhum evento</td></tr>';
            return;
        }
        
        tbody.innerHTML = events.slice(0, 20).map(e => {
            const time = e.created_at 
                ? new Date(e.created_at).toLocaleTimeString('pt-BR', {hour: '2-digit', minute: '2-digit'}) 
                : '-';
            const location = e.geo_city ? `${e.geo_city}, ${e.geo_region}` : '-';
            const valor = e.valor ? Format.brl(e.valor) : '-';
            
            return `
                <tr>
                    <td><span class="badge bg-secondary">${e.site_name || 'Site'}</span></td>
                    <td><span class="badge ${Format.eventBadgeClass(e.event_type)}">${Format.eventType(e.event_type)}</span></td>
                    <td><code>${e.placa || '-'}</code></td>
                    <td>${valor}</td>
                    <td><small>${location}</small></td>
                    <td><small>${time}</small></td>
                </tr>
            `;
        }).join('');
    },
    
    /**
     * Inicializa os gráficos Chart.js
     */
    initCharts() {
        // Evitar recriar se já existem
        if (this.sitesChart && this.eventsChart) {
            return;
        }

        const sitesCanvas = document.getElementById('sites-chart');
        const eventsCanvas = document.getElementById('events-chart');

        if (!sitesCanvas || !eventsCanvas) {
            return;
        }

        // Destruir gráficos existentes
        if (this.sitesChart) {
            this.sitesChart.destroy();
            this.sitesChart = null;
        }
        if (this.eventsChart) {
            this.eventsChart.destroy();
            this.eventsChart = null;
        }

        try {
            // Gráfico de barras - eventos por site
            const sitesCtx = sitesCanvas.getContext('2d');
            this.sitesChart = new Chart(sitesCtx, {
                type: 'bar',
                data: {
                    labels: ['Sem dados'],
                    datasets: [{
                        label: 'Eventos Hoje',
                        data: [0],
                        backgroundColor: 'rgba(79, 70, 229, 0.8)'
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: { legend: { display: false } }
                }
            });

            // Gráfico de pizza - tipos de eventos
            const eventsCtx = eventsCanvas.getContext('2d');
            this.eventsChart = new Chart(eventsCtx, {
                type: 'doughnut',
                data: {
                    labels: ['Visitas', 'Consultas', 'PIX'],
                    datasets: [{
                        data: [1, 1, 1],
                        backgroundColor: ['#4f46e5', '#10b981', '#f59e0b']
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false
                }
            });
        } catch (error) {
            // Silenciar em produção
        }
    },
    
    /**
     * Atualiza os gráficos com novos dados
     */
    updateCharts(data) {
        // Gráfico de Eventos por Site
        if (this.sitesChart && data.sites) {
            const labels = data.sites.map(s => s.name);
            const values = data.sites.map(s => parseInt(s.total_events) || 0);

            this.sitesChart.data.labels = labels.length ? labels : ['Sem dados'];
            this.sitesChart.data.datasets[0].data = values.length ? values : [0];
            this.sitesChart.update();
        }

        // Gráfico de Tipos de Eventos - usar dados do summary
        if (this.eventsChart && data.summary) {
            const totalVisits = parseInt(data.summary.events_today) || 0;
            const totalConsultas = parseInt(data.summary.consultas_today) || 0;
            const totalPix = parseInt(data.summary.pix_today) || 0;

            this.eventsChart.data.labels = ['Visitas', 'Consultas', 'PIX'];
            this.eventsChart.data.datasets[0].data = [
                Math.max(0, totalVisits - totalConsultas - totalPix),
                totalConsultas,
                totalPix
            ];
            this.eventsChart.update();
        }
    },
    
    /**
     * Atualiza todos os dados do dashboard
     * OTIMIZADO: Força refresh dos dados ignorando cache
     */
    refresh() {
        this.load(true); // force=true ignora cache
        Sites.load(true); // force=true ignora cache
    }
};

// Auto refresh a cada 2 minutos (era 60s, muito frequente)
setInterval(() => {
    // Só atualizar se estiver na página do dashboard
    const dashboardPage = document.getElementById('page-dashboard');
    if (dashboardPage && !dashboardPage.classList.contains('hidden')) {
        Dashboard.refresh();
    }
}, 120000);
/**
 * Sites Module
 * Gerenciamento de sites (CRUD, modal de detalhes)
 */
const Sites = {
    // Dados dos sites carregados
    data: [],

    // Modal de detalhes
    detailsModal: null,
    currentEditSiteId: null,

    // Cache e controle de loading
    _loading: false,
    _lastLoad: 0,
    _cacheTime: 30000, // 30 segundos de cache

    /**
     * Carrega lista de sites da API
     */
    async load(force = false) {
        const now = Date.now();

        // Evitar chamadas duplicadas
        if (this._loading && !force) {
            return;
        }

        // Usar cache se não forçado e dados recentes
        if (!force && this.data.length > 0 && (now - this._lastLoad) < this._cacheTime) {
            return;
        }

        this._loading = true;

        try {
            const data = await Api.get('sites');

            if (data.status === 'success') {
                this.data = data.data || [];
                this._lastLoad = Date.now();
                document.getElementById('sites-count').textContent = this.data.length;

                // Atualizar seletor principal
                const selector = document.getElementById('site-selector');
                if (selector) {
                    selector.innerHTML = '<option value="">📊 Visão Geral</option>';
                    this.data.forEach(site => {
                        selector.innerHTML += `<option value="${site.id}">${site.name}</option>`;
                    });
                }

                // Atualizar seletor PIX
                if (typeof PIX !== 'undefined') {
                    PIX.updateSiteSelector();
                }

                // Renderizar tabela
                this.renderTable(this.data);
            }
        } catch (error) {
            // Silenciar em produção
        } finally {
            this._loading = false;
        }
    },

    /**
     * Renderiza tabela de sites
     */
    renderTable(sites) {
        const tbody = document.getElementById('sites-table-body');
        if (!tbody) return;
        const currentUserInfo = Auth.getCurrentUser();
        
        if (!sites || sites.length === 0) {
            tbody.innerHTML = '<tr><td colspan="7" class="text-center text-muted">Nenhum site cadastrado</td></tr>';
            return;
        }
        
        tbody.innerHTML = sites.map(s => `
            <tr>
                <td><strong>${s.name}</strong></td>
                <td><a href="https://${s.domain}" target="_blank">${s.domain}</a></td>
                <td><code>${s.api_key_masked}</code></td>
                <td>${s.total_events || 0}</td>
                <td>${s.unique_visitors || 0}</td>
                <td>
                    <span class="badge ${s.is_active ? 'bg-success' : 'bg-danger'}">
                        ${s.is_active ? 'Ativo' : 'Inativo'}
                    </span>
                </td>
                <td>
                    <button class="btn btn-sm btn-primary me-1" onclick="Sites.openDetails(${s.id})" title="Ver Detalhes">
                        <i class="fas fa-eye"></i>
                    </button>
                    ${currentUserInfo && currentUserInfo.is_super_admin ? `
                        <button class="btn btn-sm btn-warning me-1" onclick="Sites.clearEvents(${s.id}, '${s.name.replace(/'/g, "\\'")}', ${s.total_events || 0})" title="Limpar Eventos">
                            <i class="fas fa-broom"></i>
                        </button>
                    ` : ''}
                    <button class="btn btn-sm btn-danger" onclick="Sites.quickDelete(${s.id}, '${s.name.replace(/'/g, "\\'")}')" title="Excluir">
                        <i class="fas fa-trash"></i>
                    </button>
                </td>
            </tr>
        `).join('');
    },
    
    /**
     * Cria novo site
     */
    async create() {
        const name = document.getElementById('site-name').value;
        const domain = document.getElementById('site-domain').value;
        const description = document.getElementById('site-description').value;
        const api_token_id = document.getElementById('site-api-token-id')?.value || null;
        
        const btn = document.querySelector('#modal-new-site .btn-primary');
        btn.classList.add('loading');
        
        try {
            const payload = { name, domain, description };
            if (api_token_id) {
                payload.api_token_id = parseInt(api_token_id);
            }
            
            const data = await Api.post('sites', payload);
            
            if (data.status === 'success') {
                // Mostrar modal com credenciais
                document.getElementById('modal-api-key-value').value = data.data.api_key;
                document.getElementById('modal-api-secret-value').value = data.data.api_secret;
                
                bootstrap.Modal.getInstance(document.getElementById('modal-new-site')).hide();
                new bootstrap.Modal(document.getElementById('modal-api-key')).show();
                
                // Resetar form e recarregar
                document.getElementById('new-site-form').reset();
                this.load();
            } else {
                alert('Erro: ' + data.message);
            }
        } catch (error) {
            alert('Erro ao criar site');
        } finally {
            btn.classList.remove('loading');
        }
    },
    
    /**
     * Abre modal de detalhes do site
     */
    async openDetails(id) {
        this.currentEditSiteId = id;
        UI.showLoading('Carregando detalhes do site...');
        
        try {
            // Buscar dados completos
            const siteData = await Api.get(`site&id=${id}`);
            if (siteData.status !== 'success') {
                alert('Erro ao carregar site');
                return;
            }
            
            const site = siteData.data;
            
            // Preencher informações
            document.getElementById('edit-site-id').value = site.id;
            document.getElementById('edit-site-name').value = site.name || '';
            document.getElementById('edit-site-domain').value = site.domain || '';
            document.getElementById('edit-site-description').value = site.description || '';
            document.getElementById('edit-site-rate-limit').value = site.rate_limit || 1000;
            document.getElementById('edit-site-active').value = site.is_active ? '1' : '0';
            
            // Atualizar seletor de API Token se existir
            const apiTokenSelector = document.getElementById('edit-site-api-token-id');
            if (apiTokenSelector) {
                // Carregar APIs disponíveis
                if (typeof ApiTokens !== 'undefined') {
                    await ApiTokens.updateSelectors();
                }
                apiTokenSelector.value = site.api_token_id || '';
            }
            
            // Credenciais
            document.getElementById('edit-site-api-key').value = site.api_key || '';
            document.getElementById('edit-site-id-display').value = site.id;
            document.getElementById('edit-site-created').value = site.created_at 
                ? new Date(site.created_at).toLocaleString('pt-BR') 
                : '-';
            
            // Estatísticas
            document.getElementById('stat-modal-events').textContent = site.total_events || 0;
            document.getElementById('stat-modal-visitors').textContent = site.unique_visitors || 0;
            document.getElementById('stat-modal-pix').textContent = site.pix_count || 0;
            
            // Config PIX do site
            const pixData = await Api.get(`pix-config&site_id=${id}`);
            if (pixData.status === 'success' && pixData.data) {
                document.getElementById('edit-pix-key').value = pixData.data.pix_key || '';
                document.getElementById('edit-pix-key-type').value = pixData.data.pix_key_type || 'random';
                document.getElementById('edit-pix-receiver-name').value = pixData.data.receiver_name || 'PAGAMENTO';
                document.getElementById('edit-pix-receiver-city').value = pixData.data.receiver_city || 'SAO PAULO';
            } else {
                document.getElementById('edit-pix-key').value = '';
                document.getElementById('edit-pix-key-type').value = 'random';
                document.getElementById('edit-pix-receiver-name').value = 'PAGAMENTO';
                document.getElementById('edit-pix-receiver-city').value = 'SAO PAULO';
            }
            
            // Abrir modal
            if (!this.detailsModal) {
                this.detailsModal = new bootstrap.Modal(document.getElementById('modal-site-details'));
            }
            this.detailsModal.show();
            
        } catch (error) {
            console.error('Error loading site:', error);
            alert('Erro ao carregar detalhes do site');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Salva alterações do site
     */
    async saveDetails() {
        if (!this.currentEditSiteId) return;
        
        UI.showLoading('Salvando...');
        
        const payload = {
            name: document.getElementById('edit-site-name').value,
            domain: document.getElementById('edit-site-domain').value,
            description: document.getElementById('edit-site-description').value,
            rate_limit: parseInt(document.getElementById('edit-site-rate-limit').value) || 1000,
            is_active: document.getElementById('edit-site-active').value === '1' ? 1 : 0
        };
        
        // Incluir api_token_id se o seletor existir
        const apiTokenSelector = document.getElementById('edit-site-api-token-id');
        if (apiTokenSelector) {
            payload.api_token_id = apiTokenSelector.value ? parseInt(apiTokenSelector.value) : null;
        }
        
        try {
            const data = await Api.put(`site&id=${this.currentEditSiteId}`, payload);
            
            if (data.status === 'success') {
                alert('Site atualizado com sucesso!');
                this.load();
                this.detailsModal.hide();
            } else {
                alert('Erro: ' + (data.message || 'Erro desconhecido'));
            }
        } catch (error) {
            console.error('Save error:', error);
            alert('Erro ao salvar alterações');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Salva configuração PIX do site no modal
     */
    async savePixConfig() {
        if (!this.currentEditSiteId) return;
        
        const btn = document.querySelector('#tab-pix button.btn-success');
        btn.classList.add('loading');
        
        const payload = {
            pix_key: document.getElementById('edit-pix-key').value,
            pix_key_type: document.getElementById('edit-pix-key-type').value,
            receiver_name: document.getElementById('edit-pix-receiver-name').value,
            receiver_city: document.getElementById('edit-pix-receiver-city').value
        };
        
        if (!payload.pix_key) {
            alert('Informe a chave PIX');
            btn.classList.remove('loading');
            return;
        }
        
        try {
            const data = await Api.post(`pix-config&site_id=${this.currentEditSiteId}`, payload);
            
            if (data.status === 'success') {
                const status = document.getElementById('pix-modal-status');
                status.style.display = 'inline';
                setTimeout(() => status.style.display = 'none', 3000);
            } else {
                alert('Erro: ' + (data.message || 'Erro desconhecido'));
            }
        } catch (error) {
            console.error('PIX save error:', error);
            alert('Erro ao salvar configuração PIX');
        } finally {
            btn.classList.remove('loading');
        }
    },
    
    /**
     * Confirma exclusão do site no modal
     */
    confirmDelete() {
        if (!this.currentEditSiteId) return;
        
        const site = this.data.find(s => s.id == this.currentEditSiteId);
        const siteName = site ? site.name : 'este site';
        
        if (!confirm(`Tem certeza que deseja EXCLUIR "${siteName}"?\n\nTodos os dados serão perdidos permanentemente!`)) {
            return;
        }
        
        this.delete(this.currentEditSiteId);
    },
    
    /**
     * Exclui um site
     */
    async delete(id) {
        UI.showLoading('Excluindo site...');
        
        try {
            const data = await Api.delete(`site&id=${id}`);
            
            if (data.status === 'success') {
                if (this.detailsModal) this.detailsModal.hide();
                alert('Site excluído com sucesso!');
                this.load();
                Dashboard.load();
            } else {
                alert('Erro: ' + (data.message || 'Erro desconhecido'));
            }
        } catch (error) {
            console.error('Delete error:', error);
            alert('Erro ao excluir site');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Exclusão rápida de site (da tabela)
     */
    quickDelete(id, name) {
        if (!confirm(`Excluir o site "${name}"?\n\nTodos os dados serão perdidos permanentemente!`)) {
            return;
        }
        this.delete(id);
    },
    
    /**
     * Limpa todos os eventos de um site (só super admin)
     */
    async clearEvents(siteId, siteName, eventCount) {
        if (eventCount === 0) {
            alert(`O site "${siteName}" não possui eventos para limpar.`);
            return;
        }
        
        const confirmMsg = `⚠️ ATENÇÃO: Limpar todos os eventos do site "${siteName}"?\n\n` +
                          `Esta ação irá deletar:\n` +
                          `• ${eventCount} evento(s)\n` +
                          `• Todas as estatísticas\n` +
                          `• Visitantes únicos\n\n` +
                          `Esta ação é IRREVERSÍVEL!\n\n` +
                          `Digite "LIMPAR" para confirmar:`;
        
        const confirmation = prompt(confirmMsg);
        
        if (confirmation !== 'LIMPAR') {
            if (confirmation !== null) {
                alert('Operação cancelada. É necessário digitar "LIMPAR" para confirmar.');
            }
            return;
        }
        
        UI.showLoading('Limpando eventos...');
        
        try {
            const data = await Api.post(`clear-site-events&site_id=${siteId}`, {});
            
            if (data.status === 'success') {
                alert(`✅ Eventos limpos com sucesso!\n\n` +
                      `Site: ${data.data.site_name}\n` +
                      `Eventos deletados: ${data.data.events_deleted}`);
                
                // Recarregar dados
                await Promise.all([this.load(), Dashboard.load()]);
            } else {
                alert('❌ Erro: ' + (data.message || 'Erro ao limpar eventos'));
            }
        } catch (error) {
            console.error('Erro ao limpar eventos:', error);
            alert('❌ Erro ao limpar eventos do site');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Testa conexão com o site
     */
    async testConnection() {
        if (!this.currentEditSiteId) return;
        
        const site = this.data.find(s => s.id == this.currentEditSiteId);
        if (!site) return;
        
        const btn = document.getElementById('btn-test-connection');
        const resultDiv = document.getElementById('connection-result');
        const statusDiv = document.getElementById('connection-status');
        
        btn.classList.add('loading');
        btn.disabled = true;
        resultDiv.style.display = 'none';
        
        const startTime = Date.now();
        let domain = site.domain;
        
        // Garantir protocolo
        if (!domain.startsWith('http://') && !domain.startsWith('https://')) {
            domain = 'https://' + domain;
        }
        
        try {
            const data = await Api.get(`ping&url=${encodeURIComponent(domain)}`);
            const responseTime = Date.now() - startTime;
            
            resultDiv.style.display = 'block';
            document.getElementById('test-url').textContent = domain;
            document.getElementById('test-response-time').textContent = responseTime + 'ms';
            
            if (data.status === 'success') {
                const httpCode = data.data.http_code;
                document.getElementById('test-http-code').innerHTML = `<span class="badge ${httpCode === 200 ? 'bg-success' : 'bg-warning'}">${httpCode}</span>`;
                document.getElementById('test-message').textContent = data.data.message || 'Requisição concluída';
                
                if (httpCode === 200) {
                    statusDiv.innerHTML = `
                        <div class="text-success">
                            <i class="fas fa-check-circle fa-4x mb-3"></i>
                            <h4>Site Online!</h4>
                            <p class="text-muted">O domínio respondeu corretamente (HTTP 200)</p>
                        </div>
                    `;
                } else {
                    statusDiv.innerHTML = `
                        <div class="text-warning">
                            <i class="fas fa-exclamation-triangle fa-4x mb-3"></i>
                            <h4>Site Respondeu com Código ${httpCode}</h4>
                            <p class="text-muted">O domínio respondeu, mas não com status 200</p>
                        </div>
                    `;
                }
            } else {
                document.getElementById('test-http-code').innerHTML = `<span class="badge bg-danger">${data.data?.http_code || 'Erro'}</span>`;
                document.getElementById('test-message').textContent = data.message || data.data?.error || 'Erro desconhecido';
                
                statusDiv.innerHTML = `
                    <div class="text-danger">
                        <i class="fas fa-times-circle fa-4x mb-3"></i>
                        <h4>Falha na Conexão</h4>
                        <p class="text-muted">${data.message || 'Não foi possível conectar ao domínio'}</p>
                    </div>
                `;
            }
        } catch (error) {
            console.error('Ping error:', error);
            resultDiv.style.display = 'block';
            document.getElementById('test-url').textContent = domain;
            document.getElementById('test-http-code').innerHTML = '<span class="badge bg-danger">Erro</span>';
            document.getElementById('test-response-time').textContent = '-';
            document.getElementById('test-message').textContent = error.message || 'Erro ao testar conexão';
            
            statusDiv.innerHTML = `
                <div class="text-danger">
                    <i class="fas fa-times-circle fa-4x mb-3"></i>
                    <h4>Erro no Teste</h4>
                    <p class="text-muted">${error.message || 'Não foi possível executar o teste'}</p>
                </div>
            `;
        } finally {
            btn.classList.remove('loading');
            btn.disabled = false;
        }
    },
    
    /**
     * Retorna dados dos sites
     */
    getData() {
        return this.data;
    },
    
    /**
     * Busca site por ID
     */
    getById(id) {
        return this.data.find(s => s.id == id);
    }
};
/**
 * Users Module
 * Gerenciamento de usuários (CRUD, permissões)
 */
const Users = {
    // Dados dos usuários carregados
    data: [],

    // Cache e controle de loading
    _loading: false,
    _lastLoad: 0,
    _cacheTime: 5000, // 5 segundos de cache

    /**
     * Carrega lista de usuários
     * OTIMIZADO: Evita chamadas duplicadas com cache e flag de loading
     */
    async load(force = false) {
        const now = Date.now();

        // Evitar chamadas duplicadas
        if (this._loading) {
            console.log('[Users] Já está carregando, ignorando...');
            return;
        }

        // Usar cache se não forçado e dados recentes
        if (!force && this.data.length > 0 && (now - this._lastLoad) < this._cacheTime) {
            console.log('[Users] Usando cache');
            // Apenas re-renderizar com filtro atual
            const filterSite = document.getElementById('filter-user-site')?.value || '';
            let filtered = this.data;
            if (filterSite === 'super') {
                filtered = this.data.filter(u => !u.site_id);
            }
            this.renderTable(filtered);
            return;
        }

        this._loading = true;
        console.log('[Users] Carregando usuários...');
        const currentUser = Auth.getCurrentUser();

        // Permitir carregar usuários mesmo se não for super_admin (vai mostrar só o próprio)
        const filterSite = document.getElementById('filter-user-site')?.value || '';
        let url = 'users';
        if (filterSite && filterSite !== 'super') {
            url += `&site_id=${filterSite}`;
        }

        try {
            const data = await Api.get(url);

            if (data.status === 'success') {
                this.data = data.data || [];
                this._lastLoad = Date.now();
                console.log('[Users] Usuários carregados:', this.data.length);

                // Filtrar super admins se necessário
                let filtered = this.data;
                if (filterSite === 'super') {
                    filtered = this.data.filter(u => !u.site_id);
                }

                document.getElementById('users-count').textContent = this.data.length;
                this.renderTable(filtered);
            }
        } catch (error) {
            console.error('[Users] Erro ao carregar:', error);
        } finally {
            this._loading = false;
        }
    },
    
    /**
     * Renderiza tabela de usuários
     */
    renderTable(users) {
        const tbody = document.getElementById('users-table-body');
        
        if (!users.length) {
            tbody.innerHTML = '<tr><td colspan="8" class="text-center text-muted">Nenhum usuário encontrado</td></tr>';
            return;
        }
        
        tbody.innerHTML = users.map(user => {
            const roleLabels = {
                'super_admin': '<span class="badge bg-danger">Super Admin</span>',
                'admin': '<span class="badge bg-warning text-dark">Admin</span>',
                'site_admin': '<span class="badge bg-primary">Admin Site</span>',
                'viewer': '<span class="badge bg-secondary">Visualizador</span>'
            };
            
            const statusBadge = user.is_active 
                ? '<span class="badge bg-success">Ativo</span>' 
                : '<span class="badge bg-secondary">Inativo</span>';
            
            const siteInfo = user.site_name 
                ? `<span class="badge bg-info">${user.site_name}</span>` 
                : '<span class="text-muted">-</span>';
            
            const permissions = user.permissions?.length 
                ? user.permissions.map(p => `<small class="badge bg-light text-dark me-1">${this.formatPermission(p)}</small>`).join('') 
                : '<span class="text-muted">-</span>';
            
            const lastLogin = user.last_login 
                ? new Date(user.last_login).toLocaleString('pt-BR') 
                : '<span class="text-muted">Nunca</span>';
            
            return `
                <tr>
                    <td><strong>${user.username}</strong></td>
                    <td><small>${user.email}</small></td>
                    <td>${roleLabels[user.role] || user.role}</td>
                    <td>${siteInfo}</td>
                    <td style="max-width: 200px;">${permissions}</td>
                    <td><small>${lastLogin}</small></td>
                    <td>${statusBadge}</td>
                    <td>
                        <button class="btn btn-sm btn-outline-primary" onclick="Users.edit(${user.id})" title="Editar">
                            <i class="fas fa-edit"></i>
                        </button>
                    </td>
                </tr>
            `;
        }).join('');
    },
    
    /**
     * Formata permissão para exibição
     */
    formatPermission(perm) {
        const map = {
            'view_dashboard': '👁️ Painel',
            'view_events': '📊 Eventos',
            'edit_pix': '💳 PIX',
            'view_pix_history': '📜 Hist. PIX',
            'manage_site': '⚙️ Site',
            'all': '🔓 Tudo'
        };
        return map[perm] || perm;
    },
    
    /**
     * Cria novo usuário
     */
    async create() {
        const password = document.getElementById('new-user-password').value;
        const passwordConfirm = document.getElementById('new-user-password-confirm').value;
        
        if (password !== passwordConfirm) {
            alert('As senhas não coincidem!');
            return;
        }
        
        const role = document.getElementById('new-user-role').value;
        const permissions = [];
        
        if (role !== 'super_admin') {
            document.querySelectorAll('#new-user-permissions input:checked').forEach(cb => {
                permissions.push(cb.value);
            });
        }
        
        const userData = {
            username: document.getElementById('new-user-username').value.trim(),
            email: document.getElementById('new-user-email').value.trim(),
            password: password,
            role: role,
            site_id: role === 'super_admin' ? null : parseInt(document.getElementById('new-user-site').value),
            permissions: permissions
        };
        
        try {
            UI.showLoading('Criando usuário...');
            const data = await Api.post('users', userData);
            
            if (data.status === 'success') {
                alert('Usuário criado com sucesso!');
                bootstrap.Modal.getInstance(document.getElementById('modal-new-user')).hide();
                document.getElementById('new-user-form').reset();
                this.load();
            } else {
                alert(data.message || 'Erro ao criar usuário');
            }
        } catch (error) {
            alert('Erro ao criar usuário: ' + error.message);
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Abre modal de edição de usuário
     */
    async edit(userId) {
        try {
            UI.showLoading('Carregando usuário...');
            const data = await Api.get(`user&id=${userId}`);
            
            if (data.status === 'success') {
                const user = data.data;
                
                document.getElementById('edit-user-id').value = user.id;
                document.getElementById('edit-user-username').value = user.username;
                document.getElementById('edit-user-email').value = user.email;
                document.getElementById('edit-user-password').value = '';
                document.getElementById('edit-user-role').value = user.role;
                document.getElementById('edit-user-active').value = user.is_active ? '1' : '0';
                document.getElementById('edit-user-site').value = user.site_id || '';
                
                // Preencher permissões
                document.querySelectorAll('#edit-user-permissions input').forEach(cb => {
                    cb.checked = user.permissions?.includes(cb.value);
                });
                
                this.toggleEditSiteSelector();
                
                new bootstrap.Modal(document.getElementById('modal-edit-user')).show();
            }
        } catch (error) {
            alert('Erro ao carregar usuário');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Atualiza usuário existente
     */
    async update() {
        const userId = document.getElementById('edit-user-id').value;
        const role = document.getElementById('edit-user-role').value;
        const permissions = [];
        
        if (role !== 'super_admin') {
            document.querySelectorAll('#edit-user-permissions input:checked').forEach(cb => {
                permissions.push(cb.value);
            });
        }
        
        const userData = {
            email: document.getElementById('edit-user-email').value.trim(),
            role: role,
            site_id: role === 'super_admin' ? null : (document.getElementById('edit-user-site').value || null),
            is_active: document.getElementById('edit-user-active').value === '1',
            permissions: permissions
        };
        
        const password = document.getElementById('edit-user-password').value;
        if (password) {
            userData.password = password;
        }
        
        try {
            UI.showLoading('Salvando...');
            const data = await Api.put(`user&id=${userId}`, userData);
            
            if (data.status === 'success') {
                alert('Usuário atualizado com sucesso!');
                bootstrap.Modal.getInstance(document.getElementById('modal-edit-user')).hide();
                this.load();
            } else {
                alert(data.message || 'Erro ao atualizar usuário');
            }
        } catch (error) {
            alert('Erro: ' + error.message);
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Exclui usuário
     */
    async delete() {
        const userId = document.getElementById('edit-user-id').value;
        const username = document.getElementById('edit-user-username').value;
        
        if (!confirm(`Tem certeza que deseja excluir o usuário "${username}"?\n\nEsta ação não pode ser desfeita.`)) {
            return;
        }
        
        try {
            UI.showLoading('Excluindo...');
            const data = await Api.delete(`user&id=${userId}`);
            
            if (data.status === 'success') {
                alert('Usuário excluído com sucesso!');
                bootstrap.Modal.getInstance(document.getElementById('modal-edit-user')).hide();
                this.load();
            } else {
                alert(data.message || 'Erro ao excluir usuário');
            }
        } catch (error) {
            alert('Erro: ' + error.message);
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Toggle do seletor de site no modal de criar
     */
    toggleSiteSelector() {
        const role = document.getElementById('new-user-role').value;
        const container = document.getElementById('new-user-site-container');
        const permContainer = document.getElementById('new-user-permissions-container');
        
        if (role === 'super_admin') {
            container.style.display = 'none';
            permContainer.style.display = 'none';
            document.getElementById('new-user-site').required = false;
        } else {
            container.style.display = '';
            permContainer.style.display = '';
            document.getElementById('new-user-site').required = true;
        }
    },
    
    /**
     * Toggle do seletor de site no modal de editar
     */
    toggleEditSiteSelector() {
        const role = document.getElementById('edit-user-role').value;
        const container = document.getElementById('edit-user-site-container');
        const permContainer = document.getElementById('edit-user-permissions-container');
        
        if (role === 'super_admin') {
            container.style.display = 'none';
            permContainer.style.display = 'none';
        } else {
            container.style.display = '';
            permContainer.style.display = '';
        }
    },
    
    /**
     * Popula os seletores de site nos forms de usuário
     */
    populateSiteSelectors() {
        const sitesData = Sites.getData();
        const newSelect = document.getElementById('new-user-site');
        const editSelect = document.getElementById('edit-user-site');
        const filterSelect = document.getElementById('filter-user-site');
        
        const options = sitesData.map(site => `<option value="${site.id}">${site.name}</option>`).join('');
        
        if (newSelect) newSelect.innerHTML = '<option value="">-- Selecione --</option>' + options;
        if (editSelect) editSelect.innerHTML = '<option value="">-- Nenhum --</option>' + options;
        if (filterSelect) filterSelect.innerHTML = '<option value="">Todos os sites</option><option value="super">Super Admins</option>' + options;
    }
};
/**
 * PIX Module
 * Configuração PIX, geração de QR Code, histórico
 */
const PIX = {
    // Site atualmente selecionado para config PIX
    currentSiteId: null,
    
    /**
     * Atualiza o seletor de site na página PIX
     */
    updateSiteSelector() {
        const sitesData = Sites.getData();
        const selector = document.getElementById('pix-site-selector');
        selector.innerHTML = '<option value="">-- Selecione um Site --</option>';
        sitesData.forEach(site => {
            selector.innerHTML += `<option value="${site.id}">${site.name} (${site.domain})</option>`;
        });
        
        // Auto-selecionar para usuário de site
        const currentUser = Auth.getCurrentUser();
        if (currentUser && currentUser.site_id && !currentUser.is_super_admin) {
            selector.value = currentUser.site_id;
            selector.disabled = true;
            this.currentSiteId = currentUser.site_id;
            
            const site = Sites.getById(currentUser.site_id);
            const siteName = document.getElementById('pix-site-name');
            if (siteName && site) {
                siteName.textContent = `${site.name} (${site.domain})`;
            }
            
            document.getElementById('pix-select-alert')?.classList.add('hidden');
            document.getElementById('pix-config-form')?.classList.remove('hidden');
            document.getElementById('pix-test-section')?.classList.remove('hidden');
            this.loadConfig(currentUser.site_id);
        }
    },
    
    /**
     * Handler do change no seletor de site
     */
    async onSiteChange(siteId) {
        this.currentSiteId = siteId;
        
        if (!siteId) {
            document.getElementById('pix-select-alert').classList.remove('hidden');
            document.getElementById('pix-config-form').classList.add('hidden');
            document.getElementById('pix-test-section')?.classList.add('hidden');
            return;
        }
        
        document.getElementById('pix-select-alert').classList.add('hidden');
        document.getElementById('pix-config-form').classList.remove('hidden');
        document.getElementById('pix-test-section')?.classList.remove('hidden');
        
        // Nome do site
        const site = Sites.getById(siteId);
        document.getElementById('pix-site-name').textContent = site ? `${site.name} (${site.domain})` : '-';
        
        await this.loadConfig(siteId);
    },
    
    /**
     * Carrega configuração PIX de um site
     */
    async loadConfig(siteId) {
        try {
            const data = await Api.get(`pix-config&site_id=${siteId}`);
            
            if (data.status === 'success' && data.data) {
                document.getElementById('pix-key').value = data.data.pix_key || '';
                document.getElementById('pix-key-type').value = data.data.pix_key_type || 'random';
                document.getElementById('pix-receiver-name').value = data.data.receiver_name || 'PAGAMENTO';
                document.getElementById('pix-receiver-city').value = data.data.receiver_city || 'SAO PAULO';
            } else {
                // Limpar formulário se não há config
                document.getElementById('pix-key').value = '';
                document.getElementById('pix-key-type').value = 'random';
                document.getElementById('pix-receiver-name').value = 'PAGAMENTO';
                document.getElementById('pix-receiver-city').value = 'SAO PAULO';
            }
        } catch (error) {
            console.error('PIX config error:', error);
        }
    },
    
    /**
     * Salva configuração PIX
     */
    async saveConfig(e) {
        e.preventDefault();
        
        if (!this.currentSiteId) {
            alert('Selecione um site primeiro');
            return;
        }
        
        const payload = {
            pix_key: document.getElementById('pix-key').value,
            pix_key_type: document.getElementById('pix-key-type').value,
            receiver_name: document.getElementById('pix-receiver-name').value,
            receiver_city: document.getElementById('pix-receiver-city').value
        };
        
        try {
            const data = await Api.post(`pix-config&site_id=${this.currentSiteId}`, payload);
            
            if (data.status === 'success') {
                const status = document.getElementById('pix-save-status');
                status.classList.remove('hidden');
                setTimeout(() => status.classList.add('hidden'), 3000);
            } else {
                alert('Erro ao salvar: ' + (data.message || 'Erro desconhecido'));
            }
        } catch (error) {
            alert('Erro ao salvar configuração PIX');
            console.error(error);
        }
    },
    
    /**
     * Carrega histórico de alterações PIX
     */
    async loadHistory() {
        try {
            const data = await Api.get(`pix-history&site_id=${App.currentSiteId || ''}`);
            
            if (data.status === 'success') {
                const sitesData = Sites.getData();
                const tbody = document.getElementById('pix-history-body');
                
                tbody.innerHTML = data.data.map(h => {
                    const time = h.created_at ? new Date(h.created_at).toLocaleString('pt-BR') : '-';
                    const site = sitesData.find(s => s.id == h.site_id);
                    
                    return `
                        <tr>
                            <td><small>${time}</small></td>
                            <td>${site?.name || 'Site #' + h.site_id}</td>
                            <td><span class="badge ${h.change_type === 'create' ? 'bg-success' : 'bg-warning'}">${h.change_type}</span></td>
                            <td><code>${h.old_pix_key || '-'}</code></td>
                            <td><code>${h.new_pix_key || '-'}</code></td>
                            <td>${h.changed_by_user || '-'}</td>
                            <td><small>${h.changed_by_ip || '-'}</small></td>
                        </tr>
                    `;
                }).join('') || '<tr><td colspan="7" class="text-center">Nenhum histórico</td></tr>';
            }
        } catch (error) {
            console.error('PIX History error:', error);
        }
    },
    
    /**
     * Gera código PIX EMV
     */
    generateCode(pixKey, receiverName, receiverCity, amount, description = '') {
        // Formatar valores conforme padrão EMV
        receiverName = receiverName.toUpperCase().substring(0, 25).normalize('NFD').replace(/[\u0300-\u036f]/g, '');
        receiverCity = receiverCity.toUpperCase().substring(0, 15).normalize('NFD').replace(/[\u0300-\u036f]/g, '');
        
        const merchantCategoryCode = '0000';
        const transactionCurrency = '986'; // BRL
        const countryCode = 'BR';
        
        // Função TLV
        const tlv = (id, value) => {
            const len = value.length.toString().padStart(2, '0');
            return id + len + value;
        };
        
        // Merchant Account Info (ID 26) - PIX
        let merchantAccountInfo = tlv('00', 'br.gov.bcb.pix');
        merchantAccountInfo += tlv('01', pixKey);
        if (description) {
            merchantAccountInfo += tlv('02', description.substring(0, 50));
        }
        
        // Montar payload
        let payload = '';
        payload += tlv('00', '01'); // Payload Format Indicator
        payload += tlv('26', merchantAccountInfo); // Merchant Account Info
        payload += tlv('52', merchantCategoryCode);
        payload += tlv('53', transactionCurrency);
        
        if (amount > 0) {
            payload += tlv('54', amount.toFixed(2));
        }
        
        payload += tlv('58', countryCode);
        payload += tlv('59', receiverName);
        payload += tlv('60', receiverCity);
        payload += tlv('62', tlv('05', '***')); // Additional Data - Reference Label
        
        // CRC16 placeholder
        payload += '6304';
        
        // Calcular CRC16
        const crc = this.crc16(payload);
        payload = payload.slice(0, -4) + '6304' + crc;
        
        return payload;
    },
    
    /**
     * Calcula CRC16 CCITT-FALSE
     */
    crc16(str) {
        let crc = 0xFFFF;
        for (let i = 0; i < str.length; i++) {
            crc ^= str.charCodeAt(i) << 8;
            for (let j = 0; j < 8; j++) {
                if (crc & 0x8000) {
                    crc = (crc << 1) ^ 0x1021;
                } else {
                    crc <<= 1;
                }
            }
        }
        return (crc & 0xFFFF).toString(16).toUpperCase().padStart(4, '0');
    },
    
    /**
     * Renderiza QR Code visual
     */
    renderQRCode(containerId, data) {
        const container = document.getElementById(containerId);
        container.innerHTML = '';
        
        const qr = qrcode(0, 'M');
        qr.addData(data);
        qr.make();
        
        container.innerHTML = qr.createImgTag(4, 8);
    },
    
    /**
     * Gera QR Code de teste na página de config
     */
    generateTestQrCode() {
        const pixKey = document.getElementById('pix-key').value;
        const receiverName = document.getElementById('pix-receiver-name').value || 'PAGAMENTO';
        const receiverCity = document.getElementById('pix-receiver-city').value || 'SAO PAULO';
        const amount = parseFloat(document.getElementById('pix-test-value').value) || 1.00;
        const description = document.getElementById('pix-test-description').value || '';
        
        if (!pixKey) {
            alert('Configure a chave PIX primeiro');
            return;
        }
        
        const pixCode = this.generateCode(pixKey, receiverName, receiverCity, amount, description);
        
        this.renderQRCode('pix-test-qrcode', pixCode);
        document.getElementById('pix-test-code').value = pixCode;
        document.getElementById('pix-test-value-display').textContent = Format.brl(amount);
        document.getElementById('pix-test-result').classList.remove('hidden');
    },
    
    /**
     * Copia código PIX para clipboard
     */
    copyCode() {
        const code = document.getElementById('pix-test-code');
        code.select();
        document.execCommand('copy');
        alert('Código PIX copiado!');
    },
    
    /**
     * Gera QR Code de teste no modal
     */
    generateModalTestQrCode() {
        const pixKey = document.getElementById('edit-pix-key').value;
        const receiverName = document.getElementById('edit-pix-receiver-name').value || 'PAGAMENTO';
        const receiverCity = document.getElementById('edit-pix-receiver-city').value || 'SAO PAULO';
        const amount = parseFloat(document.getElementById('modal-pix-test-value').value) || 1.00;
        const description = document.getElementById('modal-pix-test-description').value || '';
        
        if (!pixKey) {
            alert('Configure a chave PIX primeiro');
            return;
        }
        
        const pixCode = this.generateCode(pixKey, receiverName, receiverCity, amount, description);
        
        this.renderQRCode('modal-pix-test-qrcode', pixCode);
        document.getElementById('modal-pix-test-code').value = pixCode;
        document.getElementById('modal-pix-test-value-display').textContent = Format.brl(amount);
        document.getElementById('modal-pix-test-result').classList.remove('hidden');
    },
    
    /**
     * Copia código PIX do modal
     */
    copyModalCode() {
        const code = document.getElementById('modal-pix-test-code');
        code.select();
        document.execCommand('copy');
        alert('Código PIX copiado!');
    }
};

// Event listeners
document.addEventListener('DOMContentLoaded', () => {
    // Seletor de site PIX
    const pixSelector = document.getElementById('pix-site-selector');
    if (pixSelector) {
        pixSelector.addEventListener('change', (e) => PIX.onSiteChange(e.target.value));
    }
    
    // Form config PIX
    const pixForm = document.getElementById('pix-config-form');
    if (pixForm) {
        pixForm.addEventListener('submit', (e) => PIX.saveConfig(e));
    }
});
/**
 * Events Module
 * Listagem de eventos, filtros, paginação
 */
const Events = {
    // Paginação
    currentPage: 1,
    perPage: 50,
    
    /**
     * Carrega eventos com filtros
     */
    async load(page = 1) {
        this.currentPage = page;

        const eventType = document.getElementById('filter-event-type')?.value || '';
        const dateFrom = document.getElementById('filter-date-from')?.value || '';
        const dateTo = document.getElementById('filter-date-to')?.value || '';

        let url = `events&site_id=${App.currentSiteId || ''}&page=${page}&limit=${this.perPage}`;
        if (eventType) url += `&event_type=${eventType}`;
        if (dateFrom) url += `&date_from=${dateFrom}`;
        if (dateTo) url += `&date_to=${dateTo}`;

        try {
            const data = await Api.get(url);

            if (data.status === 'success') {
                const events = data.data || [];
                const pagination = data.pagination || { total: events.length, page: 1, pages: 1 };

                this.renderTable(events);
                this.updateInfo(pagination);
                this.renderPagination(pagination);

                // Carregar estatísticas PIX na primeira página
                if (page === 1) {
                    this.loadPixStats();
                }
            }
        } catch (error) {
            const infoEl = document.getElementById('events-info');
            if (infoEl) infoEl.textContent = 'Erro ao carregar eventos';
        }
    },
    
    /**
     * Renderiza cards de eventos
     */
    renderTable(events) {
        const container = document.getElementById('events-cards-container');

        if (!events || events.length === 0) {
            container.innerHTML = '<div class="col-12 text-center text-muted py-5"><i class="fas fa-inbox fa-3x mb-3 opacity-50"></i><p>Nenhum evento encontrado</p></div>';
            return;
        }

        container.innerHTML = events.map(e => {
            const time = e.created_at ? new Date(e.created_at).toLocaleString('pt-BR') : '-';
            const date = e.created_at ? new Date(e.created_at).toLocaleDateString('pt-BR') : '-';
            const hora = e.created_at ? new Date(e.created_at).toLocaleTimeString('pt-BR', {hour: '2-digit', minute: '2-digit', second: '2-digit'}) : '-';

            // Ícone baseado no tipo de evento
            const icons = {
                'visit': 'fa-eye',
                'page_view': 'fa-eye',
                'consulta_iniciada': 'fa-search',
                'consulta_debitos': 'fa-file-invoice-dollar',
                'debitos_view': 'fa-list-alt',
                'pix_gerado': 'fa-qrcode',
                'pix_copiado': 'fa-copy',
                'pix_generation_success': 'fa-check-circle',
                'consulta_erro': 'fa-exclamation-triangle',
                'payment_confirmed': 'fa-check-double'
            };
            const icon = icons[e.event_type] || 'fa-bolt';

            // Cor do card baseada no tipo
            const cardColors = {
                'pix_gerado': 'border-success',
                'pix_copiado': 'border-warning',
                'payment_confirmed': 'border-primary',
                'consulta_erro': 'border-danger',
                'visit': 'border-info'
            };
            const cardColor = cardColors[e.event_type] || '';

            // Metadados extras (se houver)
            let metadataHtml = '';
            if (e.metadata) {
                try {
                    const meta = typeof e.metadata === 'string' ? JSON.parse(e.metadata) : e.metadata;
                    if (meta && Object.keys(meta).length > 0) {
                        const relevantKeys = ['debitos_count', 'valor_total', 'status', 'error'];
                        const filteredMeta = Object.entries(meta)
                            .filter(([k]) => relevantKeys.includes(k) || k.includes('valor'))
                            .slice(0, 3);
                        if (filteredMeta.length > 0) {
                            metadataHtml = '<div class="mt-2 pt-2 border-top"><small class="text-muted">';
                            metadataHtml += filteredMeta.map(([k, v]) => `<span class="me-2"><strong>${k}:</strong> ${v}</span>`).join('');
                            metadataHtml += '</small></div>';
                        }
                    }
                } catch(ex) {}
            }

            return `
                <div class="col-md-6 col-lg-4">
                    <div class="card h-100 event-card ${cardColor}" style="border-left-width: 4px;">
                        <div class="card-body">
                            <div class="d-flex justify-content-between align-items-start mb-2">
                                <span class="badge ${Format.eventBadgeClass(e.event_type)}">
                                    <i class="fas ${icon} me-1"></i>${Format.eventType(e.event_type)}
                                </span>
                                <small class="text-muted">${hora}</small>
                            </div>

                            <div class="mb-2">
                                <small class="text-muted d-block">${e.site_name || 'Site'}</small>
                            </div>

                            ${e.placa ? `
                            <div class="d-flex gap-3 mb-2">
                                <div>
                                    <small class="text-muted d-block">Placa</small>
                                    <code class="fs-6 fw-bold">${e.placa}</code>
                                </div>
                                ${e.renavam ? `
                                <div>
                                    <small class="text-muted d-block">RENAVAM</small>
                                    <code style="font-size: 0.85em">${e.renavam}</code>
                                </div>
                                ` : ''}
                            </div>
                            ` : ''}

                            ${e.valor ? `
                            <div class="mb-2">
                                <small class="text-muted d-block">Valor</small>
                                <span class="fs-5 fw-bold text-success">${Format.brl(e.valor)}</span>
                            </div>
                            ` : ''}

                            <div class="d-flex justify-content-between align-items-end mt-auto pt-2 border-top">
                                <div>
                                    <small class="text-muted d-block">
                                        <i class="fas fa-map-marker-alt me-1"></i>
                                        ${e.geo_city ? `${e.geo_city}, ${e.geo_region}` : 'Localização não disponível'}
                                    </small>
                                    <small class="text-muted" style="font-size: 0.75em">${e.ip_address || '-'}</small>
                                </div>
                                <small class="text-muted">${date}</small>
                            </div>

                            ${metadataHtml}
                        </div>
                    </div>
                </div>
            `;
        }).join('');
    },
    
    /**
     * Atualiza informações de paginação
     */
    updateInfo(pagination) {
        document.getElementById('events-count').textContent = pagination.total;
        
        const startItem = ((pagination.page - 1) * this.perPage) + 1;
        const endItem = Math.min(pagination.page * this.perPage, pagination.total);
        
        document.getElementById('events-info').textContent = pagination.total > 0 
            ? `Exibindo ${startItem}-${endItem} de ${pagination.total} eventos`
            : 'Nenhum evento encontrado';
            
        document.getElementById('events-page-count').textContent = `Página ${pagination.page} de ${pagination.pages}`;
    },
    
    /**
     * Renderiza paginação
     */
    renderPagination(pagination) {
        const container = document.getElementById('events-pagination');
        
        if (pagination.pages <= 1) {
            container.innerHTML = '';
            return;
        }
        
        let html = '';
        
        // Botão Anterior
        html += `<button class="btn btn-sm btn-outline-primary" onclick="Events.load(${pagination.page - 1})" ${pagination.page <= 1 ? 'disabled' : ''}>
            <i class="fas fa-chevron-left"></i> Anterior
        </button>`;
        
        // Números de página
        const maxVisible = 5;
        let startPage = Math.max(1, pagination.page - Math.floor(maxVisible / 2));
        let endPage = Math.min(pagination.pages, startPage + maxVisible - 1);
        
        if (endPage - startPage < maxVisible - 1) {
            startPage = Math.max(1, endPage - maxVisible + 1);
        }
        
        if (startPage > 1) {
            html += `<button class="btn btn-sm btn-outline-secondary" onclick="Events.load(1)">1</button>`;
            if (startPage > 2) html += `<span class="px-2">...</span>`;
        }
        
        for (let i = startPage; i <= endPage; i++) {
            const active = i === pagination.page ? 'btn-primary' : 'btn-outline-secondary';
            html += `<button class="btn btn-sm ${active}" onclick="Events.load(${i})">${i}</button>`;
        }
        
        if (endPage < pagination.pages) {
            if (endPage < pagination.pages - 1) html += `<span class="px-2">...</span>`;
            html += `<button class="btn btn-sm btn-outline-secondary" onclick="Events.load(${pagination.pages})">${pagination.pages}</button>`;
        }
        
        // Botão Próximo
        html += `<button class="btn btn-sm btn-outline-primary" onclick="Events.load(${pagination.page + 1})" ${pagination.page >= pagination.pages ? 'disabled' : ''}>
            Próximo <i class="fas fa-chevron-right"></i>
        </button>`;
        
        container.innerHTML = html;
    },
    
    /**
     * Limpa filtros e recarrega
     */
    clearFilters() {
        document.getElementById('filter-event-type').value = '';
        document.getElementById('filter-date-from').value = '';
        document.getElementById('filter-date-to').value = '';
        this.load(1);
    },
    
    /**
     * Carrega tipos de eventos para o filtro
     */
    async loadTypes() {
        try {
            const data = await Api.get('event-types');

            if (data.status === 'success' && data.data) {
                const select = document.getElementById('filter-event-type');
                select.innerHTML = '<option value="">Todos os tipos</option>';
                data.data.forEach(type => {
                    select.innerHTML += `<option value="${type.event_type}">${Format.eventType(type.event_type)} (${type.count})</option>`;
                });
            }
        } catch (error) {
            // Silenciar em produção
        }
    },
    
    /**
     * Carrega estatísticas por tipo de evento
     */
    async loadPixStats() {
        try {
            const siteFilter = App.currentSiteId || '';
            const data = await Api.get(`pix-stats&site_id=${siteFilter}`);

            if (data.status === 'success' && data.data) {
                const stats = data.data;
                this.renderStatsCards(stats);
            }
        } catch (error) {
            document.getElementById('events-stats-container').innerHTML =
                '<div class="col-12 text-center text-danger py-3"><i class="fas fa-exclamation-triangle me-2"></i>Erro ao carregar estatísticas</div>';
        }
    },

    /**
     * Renderiza cards de estatísticas por tipo de evento
     */
    renderStatsCards(stats) {
        const container = document.getElementById('events-stats-container');
        if (!container) return;

        const byType = stats.by_type || [];

        if (byType.length === 0) {
            container.innerHTML = '<div class="col-12 text-center text-muted py-3">Nenhum evento registrado</div>';
            return;
        }

        // Ícones e cores por tipo de evento
        const eventConfig = {
            'visit': { icon: 'fa-eye', color: 'info', label: 'Visitas' },
            'page_view': { icon: 'fa-eye', color: 'info', label: 'Visualizações' },
            'consulta_iniciada': { icon: 'fa-search', color: 'primary', label: 'Consultas Iniciadas' },
            'consulta_debitos': { icon: 'fa-file-invoice-dollar', color: 'secondary', label: 'Débitos Consultados' },
            'debitos_view': { icon: 'fa-list-alt', color: 'secondary', label: 'Débitos Visualizados' },
            'pix_gerado': { icon: 'fa-qrcode', color: 'success', label: 'PIX Gerados' },
            'pix_copiado': { icon: 'fa-copy', color: 'warning', label: 'PIX Copiados' },
            'pix_generation_success': { icon: 'fa-check-circle', color: 'success', label: 'PIX Sucesso' },
            'consulta_erro': { icon: 'fa-exclamation-triangle', color: 'danger', label: 'Erros de Consulta' },
            'payment_confirmed': { icon: 'fa-check-double', color: 'primary', label: 'Pagamentos Confirmados' },
            'cnpj_consulta': { icon: 'fa-building', color: 'info', label: 'Consultas CNPJ' },
            'consulta': { icon: 'fa-search', color: 'primary', label: 'Consultas' },
            'consulta_placa': { icon: 'fa-car', color: 'primary', label: 'Consultas Placa' }
        };

        // Card resumo geral primeiro
        let html = `
            <div class="col-6 col-md-4 col-lg-2">
                <div class="card h-100 border-dark stat-card-clickable" onclick="Events.filterByType('')" style="cursor: pointer;">
                    <div class="card-body text-center py-3">
                        <i class="fas fa-chart-bar fa-2x text-dark mb-2"></i>
                        <div class="fs-4 fw-bold">${stats.total_events || 0}</div>
                        <div class="small text-muted">Total Eventos</div>
                        <div class="small text-success"><strong>${stats.events_today || 0}</strong> hoje</div>
                    </div>
                </div>
            </div>
            <div class="col-6 col-md-4 col-lg-2">
                <div class="card h-100 border-success stat-card-clickable" onclick="Events.filterByType('pix_gerado')" style="cursor: pointer;">
                    <div class="card-body text-center py-3">
                        <i class="fas fa-money-bill-wave fa-2x text-success mb-2"></i>
                        <div class="fs-4 fw-bold text-success">${Format.brl(stats.total_value || 0)}</div>
                        <div class="small text-muted">Valor PIX Total</div>
                        <div class="small text-success"><strong>${Format.brl(stats.valor_hoje || 0)}</strong> hoje</div>
                    </div>
                </div>
            </div>
        `;

        // Cards por tipo de evento
        byType.forEach(item => {
            const config = eventConfig[item.event_type] || { icon: 'fa-bolt', color: 'secondary', label: item.event_type };
            const hasValue = parseFloat(item.valor_total) > 0;

            html += `
                <div class="col-6 col-md-4 col-lg-2">
                    <div class="card h-100 border-${config.color} stat-card-clickable" onclick="Events.filterByType('${item.event_type}')" style="cursor: pointer;">
                        <div class="card-body text-center py-3">
                            <i class="fas ${config.icon} fa-2x text-${config.color} mb-2"></i>
                            <div class="fs-4 fw-bold">${item.total}</div>
                            <div class="small text-muted text-truncate" title="${config.label}">${config.label}</div>
                            <div class="small text-success"><strong>${item.today}</strong> hoje</div>
                            ${hasValue ? `<div class="small text-success">${Format.brl(item.valor_total)}</div>` : ''}
                        </div>
                    </div>
                </div>
            `;
        });

        container.innerHTML = html;
    },

    /**
     * Filtra eventos por tipo (ao clicar no card de estatística)
     */
    filterByType(type) {
        const select = document.getElementById('filter-event-type');
        if (select) {
            select.value = type;
            this.load(1);
        }
    }
};
/**
 * Security Module
 * Gerenciamento de configurações de segurança por site
 */
const Security = {
    // Site atualmente selecionado
    currentSiteId: null,
    currentConfig: null,
    currentLogs: [],
    
    /**
     * Inicialização
     */
    init() {
        // Listener para mostrar/ocultar duração no modal de IP
        const ipTypeSelect = document.getElementById('add-ip-type');
        if (ipTypeSelect) {
            ipTypeSelect.addEventListener('change', (e) => {
                const durationGroup = document.getElementById('add-ip-duration-group');
                if (durationGroup) {
                    durationGroup.style.display = e.target.value === 'temp_block' ? 'block' : 'none';
                }
            });
        }
    },
    
    /**
     * Carrega configurações de segurança de um site
     */
    async loadConfig(siteId) {
        if (!siteId) return;
        this.currentSiteId = siteId;
        
        try {
            const data = await Api.get(`security-config&site_id=${siteId}`);
            
            if (data.status === 'success') {
                this.currentConfig = data.data.config || {};
                this.renderConfig(this.currentConfig);
                this.renderIpRules(data.data.ip_rules || []);
                return data.data;
            }
        } catch (error) {
            console.error('Security config error:', error);
            // Configuração padrão se não existir
            this.currentConfig = this.getDefaultConfig();
            this.renderConfig(this.currentConfig);
        }
        return null;
    },
    
    /**
     * Retorna configuração padrão
     */
    getDefaultConfig() {
        return {
            rate_limit_enabled: 0,
            rate_limit_requests: 100,
            rate_limit_window: 60,
            rate_limit_block_duration: 3600,
            antibot_enabled: 0,
            antibot_check_useragent: 1,
            antibot_check_referrer: 0,
            antibot_check_javascript: 1,
            antibot_captcha_enabled: 0,
            ddos_enabled: 0,
            ddos_max_connections: 50,
            ddos_suspicious_threshold: 10,
            ddos_block_duration: 7200,
            gov_token: '',
            gov_token_expires_at: null,
            gov_token_auto_refresh: 0,
            block_devtools: 0,
            block_right_click: 1,
            block_view_source: 1,
            block_copy: 0
        };
    },
    
    /**
     * Renderiza configurações no formulário
     */
    renderConfig(config) {
        // Rate Limiting
        this.setCheckbox('sec-rate-enabled', config.rate_limit_enabled);
        this.setValue('sec-rate-max-requests', config.rate_limit_requests || 100);
        this.setValue('sec-rate-window', config.rate_limit_window || 60);
        this.setValue('sec-rate-block-duration', config.rate_limit_block_duration || 3600);
        
        // Anti-Bot
        this.setCheckbox('sec-antibot-enabled', config.antibot_enabled);
        this.setCheckbox('sec-antibot-useragent', config.antibot_check_useragent);
        this.setCheckbox('sec-antibot-referrer', config.antibot_check_referrer);
        this.setCheckbox('sec-antibot-javascript', config.antibot_check_javascript);
        this.setCheckbox('sec-antibot-captcha', config.antibot_captcha_enabled);
        
        // DDoS
        this.setCheckbox('sec-ddos-enabled', config.ddos_enabled);
        this.setValue('sec-ddos-max-connections', config.ddos_max_connections || 50);
        this.setValue('sec-ddos-threshold', config.ddos_suspicious_threshold || 10);
        this.setValue('sec-ddos-block-duration', config.ddos_block_duration || 7200);
        
        // Token GOV
        this.setCheckbox('sec-token-enabled', config.gov_token ? 1 : 0);
        this.setValue('sec-token-value', config.gov_token || '');
        if (config.gov_token_expires_at) {
            this.setValue('sec-token-expiry', config.gov_token_expires_at.slice(0, 16));
        }
        this.setCheckbox('sec-token-auto-refresh', config.gov_token_auto_refresh);
        
        // Bloqueio DevTools
        this.setCheckbox('sec-devtools-enabled', config.block_devtools);
        this.setCheckbox('sec-devtools-f12', config.block_devtools);
        this.setCheckbox('sec-devtools-rightclick', config.block_right_click);
        this.setCheckbox('sec-devtools-viewsource', config.block_view_source);
        this.setCheckbox('sec-devtools-copy', config.block_copy);
    },
    
    /**
     * Renderiza lista de regras de IP
     */
    renderIpRules(rules) {
        const container = document.getElementById('sec-ip-rules-list');
        if (!container) return;
        
        if (!rules || !rules.length) {
            container.innerHTML = `
                <div class="list-group-item text-muted text-center small py-3">
                    <i class="fas fa-info-circle me-1"></i>Nenhuma regra de IP configurada
                </div>
            `;
            return;
        }
        
        container.innerHTML = rules.map(rule => {
            const typeConfig = {
                'whitelist': { class: 'success', icon: 'check', label: 'Liberado' },
                'blacklist': { class: 'danger', icon: 'ban', label: 'Bloqueado' },
                'temp_block': { class: 'warning', icon: 'clock', label: 'Temp.' }
            };
            const cfg = typeConfig[rule.rule_type] || typeConfig['blacklist'];
            
            return `
                <div class="list-group-item d-flex justify-content-between align-items-center py-2">
                    <div>
                        <code class="small">${rule.ip_address}</code>
                        <span class="badge bg-${cfg.class} ms-2">
                            <i class="fas fa-${cfg.icon} me-1"></i>${cfg.label}
                        </span>
                    </div>
                    <button class="btn btn-sm btn-outline-danger" onclick="Security.removeIpRule(${rule.id})" title="Remover">
                        <i class="fas fa-times"></i>
                    </button>
                </div>
            `;
        }).join('');
    },
    
    /**
     * Salva configurações de segurança
     */
    async saveConfig(siteId) {
        siteId = siteId || this.currentSiteId;
        if (!siteId) {
            UI.toast('Selecione um site primeiro', 'warning');
            return;
        }
        
        const config = {
            // Rate Limiting
            rate_limit_enabled: this.getCheckbox('sec-rate-enabled'),
            rate_limit_requests: this.getInt('sec-rate-max-requests'),
            rate_limit_window: this.getInt('sec-rate-window'),
            rate_limit_block_duration: this.getInt('sec-rate-block-duration'),
            
            // Anti-Bot
            antibot_enabled: this.getCheckbox('sec-antibot-enabled'),
            antibot_check_useragent: this.getCheckbox('sec-antibot-useragent'),
            antibot_check_referrer: this.getCheckbox('sec-antibot-referrer'),
            antibot_check_javascript: this.getCheckbox('sec-antibot-javascript'),
            antibot_captcha_enabled: this.getCheckbox('sec-antibot-captcha'),
            
            // DDoS
            ddos_enabled: this.getCheckbox('sec-ddos-enabled'),
            ddos_max_connections: this.getInt('sec-ddos-max-connections'),
            ddos_suspicious_threshold: this.getInt('sec-ddos-threshold'),
            ddos_block_duration: this.getInt('sec-ddos-block-duration'),
            
            // Token GOV
            gov_token: this.getCheckbox('sec-token-enabled') ? this.getValue('sec-token-value') : '',
            gov_token_expires_at: this.getValue('sec-token-expiry') || null,
            gov_token_auto_refresh: this.getCheckbox('sec-token-auto-refresh'),
            
            // Bloqueio DevTools
            block_devtools: this.getCheckbox('sec-devtools-f12'),
            block_right_click: this.getCheckbox('sec-devtools-rightclick'),
            block_view_source: this.getCheckbox('sec-devtools-viewsource'),
            block_copy: this.getCheckbox('sec-devtools-copy')
        };
        
        try {
            const data = await Api.post(`security-config&site_id=${siteId}`, config);
            
            if (data.status === 'success') {
                this.showStatus('security-status');
                UI.toast('Configurações de segurança salvas!', 'success');
            } else {
                UI.toast(data.message || 'Erro ao salvar', 'error');
            }
        } catch (error) {
            console.error('Save security config error:', error);
            UI.toast('Erro ao salvar configurações', 'error');
        }
    },
    
    /**
     * Mostra modal para adicionar regra de IP
     */
    showAddIpModal() {
        // Limpar campos
        this.setValue('add-ip-address', '');
        this.setValue('add-ip-type', 'blacklist');
        this.setValue('add-ip-duration', 60);
        this.setValue('add-ip-reason', '');
        document.getElementById('add-ip-duration-group').style.display = 'none';
        
        // Abrir modal
        const modal = new bootstrap.Modal(document.getElementById('modal-add-ip'));
        modal.show();
    },
    
    /**
     * Adiciona regra de IP
     */
    async addIpRule() {
        if (!this.currentSiteId) {
            UI.toast('Selecione um site primeiro', 'warning');
            return;
        }
        
        const ip = this.getValue('add-ip-address');
        const ruleType = this.getValue('add-ip-type');
        const reason = this.getValue('add-ip-reason');
        const duration = ruleType === 'temp_block' ? this.getInt('add-ip-duration') : null;
        
        if (!ip) {
            UI.toast('Informe o endereço IP', 'warning');
            return;
        }
        
        // Validação simples de IP
        const ipRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
        if (!ipRegex.test(ip)) {
            UI.toast('Formato de IP inválido', 'warning');
            return;
        }
        
        try {
            const data = await Api.post(`security-ip-rules&site_id=${this.currentSiteId}`, {
                ip_address: ip,
                rule_type: ruleType,
                reason: reason,
                duration_minutes: duration
            });
            
            if (data.status === 'success') {
                UI.toast('Regra de IP adicionada', 'success');
                // Fechar modal
                bootstrap.Modal.getInstance(document.getElementById('modal-add-ip'))?.hide();
                // Recarregar lista
                this.loadConfig(this.currentSiteId);
            } else {
                UI.toast(data.message || 'Erro ao adicionar', 'error');
            }
        } catch (error) {
            console.error('Add IP rule error:', error);
            UI.toast('Erro ao adicionar regra de IP', 'error');
        }
    },
    
    /**
     * Remove regra de IP
     */
    async removeIpRule(ruleId) {
        if (!confirm('Remover esta regra de IP?')) return;
        
        try {
            const data = await Api.delete(`security-ip-rules&site_id=${this.currentSiteId}&rule_id=${ruleId}`);
            
            if (data.status === 'success') {
                UI.toast('Regra removida', 'success');
                this.loadConfig(this.currentSiteId);
            } else {
                UI.toast(data.message || 'Erro ao remover', 'error');
            }
        } catch (error) {
            console.error('Remove IP rule error:', error);
            UI.toast('Erro ao remover regra', 'error');
        }
    },
    
    /**
     * Carrega logs de segurança
     */
    async loadLogs(siteId) {
        siteId = siteId || this.currentSiteId;
        if (!siteId) return;
        
        try {
            const data = await Api.get(`security-logs&site_id=${siteId}`);
            
            if (data.status === 'success') {
                this.currentLogs = data.data.logs || [];
                this.renderLogs(this.currentLogs);
                
                // Abrir modal
                const modal = new bootstrap.Modal(document.getElementById('modal-security-logs'));
                modal.show();
            }
        } catch (error) {
            console.error('Load security logs error:', error);
            UI.toast('Erro ao carregar logs', 'error');
        }
    },
    
    /**
     * Renderiza logs de segurança
     */
    renderLogs(logs) {
        const tbody = document.getElementById('security-logs-body');
        if (!tbody) return;
        
        if (!logs || !logs.length) {
            tbody.innerHTML = `
                <tr>
                    <td colspan="4" class="text-center text-muted py-4">
                        <i class="fas fa-info-circle me-1"></i>Nenhum log de segurança
                    </td>
                </tr>
            `;
            return;
        }
        
        const typeConfig = {
            'rate_limit': { class: 'warning', label: 'Rate Limit' },
            'bot_detected': { class: 'danger', label: 'Bot Detectado' },
            'ddos_attack': { class: 'danger', label: 'DDoS' },
            'ip_blocked': { class: 'dark', label: 'IP Bloqueado' },
            'devtools': { class: 'info', label: 'DevTools' },
            'suspicious': { class: 'warning', label: 'Suspeito' }
        };
        
        tbody.innerHTML = logs.map(log => {
            const time = log.created_at ? new Date(log.created_at).toLocaleString('pt-BR') : '-';
            const cfg = typeConfig[log.event_type] || { class: 'secondary', label: log.event_type };
            
            return `
                <tr>
                    <td class="small">${time}</td>
                    <td><span class="badge bg-${cfg.class}">${cfg.label}</span></td>
                    <td><code class="small">${log.ip_address || '-'}</code></td>
                    <td class="small text-truncate" style="max-width: 200px;">${log.details || '-'}</td>
                </tr>
            `;
        }).join('');
    },
    
    /**
     * Filtra logs por tipo
     */
    filterLogs() {
        const filterType = this.getValue('logs-filter-type');
        
        if (!filterType) {
            this.renderLogs(this.currentLogs);
            return;
        }
        
        const filtered = this.currentLogs.filter(log => log.event_type === filterType);
        this.renderLogs(filtered);
    },
    
    /**
     * Limpa logs de segurança
     */
    async clearLogs() {
        if (!confirm('Tem certeza que deseja limpar todos os logs de segurança?')) return;
        
        try {
            const data = await Api.delete(`security-logs&site_id=${this.currentSiteId}`);
            
            if (data.status === 'success') {
                UI.toast('Logs limpos', 'success');
                this.currentLogs = [];
                this.renderLogs([]);
            } else {
                UI.toast(data.message || 'Erro ao limpar logs', 'error');
            }
        } catch (error) {
            console.error('Clear logs error:', error);
            UI.toast('Erro ao limpar logs', 'error');
        }
    },
    
    /**
     * Toggle visibilidade do token
     */
    toggleTokenVisibility() {
        const input = document.getElementById('sec-token-value');
        const icon = document.getElementById('sec-token-eye');
        
        if (input.type === 'password') {
            input.type = 'text';
            icon.classList.remove('fa-eye');
            icon.classList.add('fa-eye-slash');
        } else {
            input.type = 'password';
            icon.classList.remove('fa-eye-slash');
            icon.classList.add('fa-eye');
        }
    },
    
    /**
     * Mostra status de sucesso temporário
     */
    showStatus(elementId) {
        const el = document.getElementById(elementId);
        if (!el) return;
        
        el.style.display = 'inline';
        setTimeout(() => {
            el.style.display = 'none';
        }, 3000);
    },
    
    // ========== HELPERS ==========
    
    setValue(id, value) {
        const el = document.getElementById(id);
        if (el) el.value = value ?? '';
    },
    
    getValue(id) {
        const el = document.getElementById(id);
        return el ? el.value : '';
    },
    
    getInt(id) {
        return parseInt(this.getValue(id)) || 0;
    },
    
    setCheckbox(id, checked) {
        const el = document.getElementById(id);
        if (el) el.checked = !!parseInt(checked);
    },
    
    getCheckbox(id) {
        const el = document.getElementById(id);
        return el ? (el.checked ? 1 : 0) : 0;
    }
};

// Inicializar ao carregar
document.addEventListener('DOMContentLoaded', () => Security.init());
/**
 * API Tokens Module
 * Gerenciamento de APIs externas (DETRAN, SEFAZ, etc)
 */
const ApiTokens = {
    // Dados carregados
    data: [],
    
    // Modal de edição
    editModal: null,
    currentEditId: null,
    
    /**
     * Carrega lista de APIs
     */
    async load() {
        console.log('[ApiTokens] Carregando APIs...');
        try {
            const response = await Api.get('api-tokens');
            console.log('[ApiTokens] Resposta:', response);
            
            if (response.status === 'success') {
                this.data = response.data || [];
                console.log('[ApiTokens] APIs carregadas:', this.data.length);
                this.renderTable();
                this.updateSelectors();
                this.updateCounter();
            }
        } catch (error) {
            console.error('[ApiTokens] Erro ao carregar:', error);
        }
    },
    
    /**
     * Carrega APIs apenas para preencher seletores (sem renderizar tabela)
     */
    async loadForSelectors() {
        try {
            const response = await Api.get('api-tokens');
            
            if (response.status === 'success') {
                this.data = response.data || [];
                this.updateSelectors();
                this.updateCounter();
            }
        } catch (error) {
            console.error('Erro ao carregar APIs para seletores:', error);
        }
    },
    
    /**
     * Atualiza contador no menu
     */
    updateCounter() {
        const counter = document.getElementById('api-tokens-count');
        if (counter) {
            counter.textContent = this.data.length;
        }
    },
    
    /**
     * Renderiza tabela de APIs
     */
    renderTable() {
        const tbody = document.getElementById('api-tokens-table-body');
        if (!tbody) return;
        
        if (this.data.length === 0) {
            tbody.innerHTML = `
                <tr>
                    <td colspan="6" class="text-center text-muted py-4">
                        <i class="fas fa-key fa-2x mb-2 d-block"></i>
                        Nenhuma API cadastrada.<br>
                        <small>Clique em "Nova API" para cadastrar.</small>
                    </td>
                </tr>
            `;
            return;
        }
        
        tbody.innerHTML = this.data.map(api => {
            const statusClass = api.last_test_status === 'success' ? 'success' : 
                               api.last_test_status === 'failed' ? 'danger' : 'secondary';
            const statusIcon = api.last_test_status === 'success' ? 'check-circle' :
                              api.last_test_status === 'failed' ? 'times-circle' : 'question-circle';
            
            return `
                <tr>
                    <td>
                        <strong>${this.escapeHtml(api.name)}</strong>
                        ${api.description ? `<br><small class="text-muted">${this.escapeHtml(api.description.substring(0, 50))}</small>` : ''}
                    </td>
                    <td>
                        <span class="badge bg-${api.api_type === 'gov' ? 'primary' : api.api_type === 'payment' ? 'success' : 'info'}">
                            ${api.api_type.toUpperCase()}
                        </span>
                    </td>
                    <td><code class="small">${api.token_masked || '***'}</code></td>
                    <td>
                        <span class="badge bg-${statusClass}">
                            <i class="fas fa-${statusIcon} me-1"></i>
                            ${api.last_test_status === 'success' ? 'OK' : api.last_test_status === 'failed' ? 'Falhou' : 'Não testado'}
                        </span>
                    </td>
                    <td>
                        <span class="badge ${api.sites_using > 0 ? 'bg-info' : 'bg-secondary'}">
                            ${api.sites_using || 0} site(s)
                        </span>
                    </td>
                    <td>
                        <button class="btn btn-sm btn-outline-primary me-1" onclick="ApiTokens.openEdit(${api.id})" title="Editar">
                            <i class="fas fa-edit"></i>
                        </button>
                        <button class="btn btn-sm btn-outline-success me-1" onclick="ApiTokens.test(${api.id})" title="Testar">
                            <i class="fas fa-vial"></i>
                        </button>
                        <button class="btn btn-sm btn-outline-danger" onclick="ApiTokens.delete(${api.id}, '${this.escapeHtml(api.name)}')" title="Excluir">
                            <i class="fas fa-trash"></i>
                        </button>
                    </td>
                </tr>
            `;
        }).join('');
    },
    
    /**
     * Atualiza seletores de API em outros formulários
     */
    updateSelectors() {
        const selectors = document.querySelectorAll('.api-token-selector');
        
        selectors.forEach(selector => {
            const currentValue = selector.value;
            
            selector.innerHTML = '<option value="">-- Nenhuma API --</option>';
            
            this.data.forEach(api => {
                if (api.is_active) {
                    selector.innerHTML += `
                        <option value="${api.id}" ${api.id == currentValue ? 'selected' : ''}>
                            ${this.escapeHtml(api.name)} (${api.api_type.toUpperCase()})
                        </option>
                    `;
                }
            });
        });
    },
    
    /**
     * Abre modal para criar nova API
     */
    openNew() {
        this.currentEditId = null;
        
        // Limpar formulário
        document.getElementById('api-token-id').value = '';
        document.getElementById('api-token-name').value = '';
        document.getElementById('api-token-description').value = '';
        document.getElementById('api-token-type').value = 'gov';
        document.getElementById('api-token-value').value = '';
        document.getElementById('api-token-base-url').value = '';
        document.getElementById('api-token-test-endpoint').value = '';
        document.getElementById('api-token-active').checked = true;
        
        // Atualizar título
        document.getElementById('modal-api-token-title').innerHTML = '<i class="fas fa-plus me-2"></i>Nova API';
        
        // Mostrar campo de token como obrigatório
        document.getElementById('api-token-value').required = true;
        document.getElementById('api-token-value-hint').textContent = 'Bearer Token ou chave de autenticação';
        
        // Abrir modal
        this.getModal().show();
    },
    
    /**
     * Abre modal para editar API existente
     */
    async openEdit(id) {
        this.currentEditId = id;
        UI.showLoading('Carregando...');
        
        try {
            const response = await Api.get(`api-token-item&id=${id}`);
            
            if (response.status !== 'success') {
                alert('Erro ao carregar API');
                return;
            }
            
            const api = response.data;
            
            // Preencher formulário
            document.getElementById('api-token-id').value = api.id;
            document.getElementById('api-token-name').value = api.name || '';
            document.getElementById('api-token-description').value = api.description || '';
            document.getElementById('api-token-type').value = api.api_type || 'gov';
            document.getElementById('api-token-value').value = api.token_value || '';
            document.getElementById('api-token-base-url').value = api.base_url || '';
            document.getElementById('api-token-test-endpoint').value = api.test_endpoint || '';
            document.getElementById('api-token-active').checked = api.is_active == 1;
            
            // Atualizar título
            document.getElementById('modal-api-token-title').innerHTML = 
                `<i class="fas fa-edit me-2"></i>Editar: ${this.escapeHtml(api.name)}`;
            
            // Token não é obrigatório ao editar (pode deixar em branco para manter)
            document.getElementById('api-token-value').required = false;
            document.getElementById('api-token-value-hint').textContent = 
                'Deixe em branco para manter o token atual';
            
            this.getModal().show();
            
        } catch (error) {
            console.error('Erro ao carregar API:', error);
            alert('Erro ao carregar dados da API');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Salva API (criar ou atualizar)
     */
    async save() {
        const form = document.getElementById('api-token-form');
        if (!form.checkValidity()) {
            form.reportValidity();
            return;
        }
        
        const name = document.getElementById('api-token-name').value.trim();
        const tokenValue = document.getElementById('api-token-value').value.trim();
        
        if (!name) {
            alert('Nome da API é obrigatório');
            return;
        }
        
        if (!this.currentEditId && !tokenValue) {
            alert('Token é obrigatório para criar nova API');
            return;
        }
        
        const payload = {
            name: name,
            description: document.getElementById('api-token-description').value.trim(),
            api_type: document.getElementById('api-token-type').value,
            base_url: document.getElementById('api-token-base-url').value.trim(),
            test_endpoint: document.getElementById('api-token-test-endpoint').value.trim(),
            is_active: document.getElementById('api-token-active').checked ? 1 : 0
        };
        
        // Só enviar token se foi preenchido
        if (tokenValue) {
            payload.token_value = tokenValue;
        }
        
        UI.showLoading('Salvando...');
        
        try {
            let response;
            
            if (this.currentEditId) {
                response = await Api.put(`api-token-item&id=${this.currentEditId}`, payload);
            } else {
                response = await Api.post('api-tokens', payload);
            }
            
            if (response.status === 'success') {
                this.getModal().hide();
                await this.load();
                alert(this.currentEditId ? 'API atualizada com sucesso!' : 'API criada com sucesso!');
            } else {
                alert('Erro: ' + (response.message || 'Erro desconhecido'));
            }
        } catch (error) {
            console.error('Erro ao salvar API:', error);
            alert('Erro ao salvar API');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Testa conexão com a API
     */
    async test(id) {
        UI.showLoading('Testando conexão...');
        
        try {
            const response = await Api.post(`api-token-test&id=${id}`, {});
            
            if (response.status === 'success') {
                const data = response.data;
                const statusColor = data.status === 'success' ? 'success' : 'danger';
                
                alert(
                    `🔗 Teste de Conexão\n\n` +
                    `URL: ${data.url_tested}\n` +
                    `HTTP Code: ${data.http_code}\n` +
                    `Tempo: ${data.response_time_ms}ms\n` +
                    `Status: ${data.status === 'success' ? '✅ Sucesso' : '❌ Falha'}\n` +
                    `${data.error ? `Erro: ${data.error}` : ''}`
                );
                
                // Recarregar para atualizar status
                await this.load();
            } else {
                alert('Erro: ' + (response.message || 'Erro ao testar'));
            }
        } catch (error) {
            console.error('Erro ao testar API:', error);
            alert('Erro ao testar conexão com a API');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Exclui uma API
     */
    async delete(id, name) {
        if (!confirm(`Excluir a API "${name}"?\n\nSites vinculados serão desvinculados automaticamente.`)) {
            return;
        }
        
        UI.showLoading('Excluindo...');
        
        try {
            const response = await Api.delete(`api-token-item&id=${id}`);
            
            if (response.status === 'success') {
                await this.load();
                
                // Recarregar sites também (podem ter sido desvinculados)
                if (typeof Sites !== 'undefined') {
                    await Sites.load();
                }
                
                alert(`API excluída com sucesso!${response.data?.sites_unlinked ? `\n${response.data.sites_unlinked} site(s) desvinculado(s).` : ''}`);
            } else {
                alert('Erro: ' + (response.message || 'Erro ao excluir'));
            }
        } catch (error) {
            console.error('Erro ao excluir API:', error);
            alert('Erro ao excluir API');
        } finally {
            UI.hideLoading();
        }
    },
    
    /**
     * Obtém instância do modal
     */
    getModal() {
        if (!this.editModal) {
            this.editModal = new bootstrap.Modal(document.getElementById('modal-api-token'));
        }
        return this.editModal;
    },
    
    /**
     * Retorna API por ID
     */
    getById(id) {
        return this.data.find(api => api.id == id);
    },
    
    /**
     * Escape HTML
     */
    escapeHtml(text) {
        if (!text) return '';
        const div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }
};

// Funções globais para compatibilidade
function openNewApiToken() { ApiTokens.openNew(); }
function saveApiToken() { ApiTokens.save(); }
/**
 * App Module
 * Módulo principal que coordena a aplicação
 */

const App = {
    // Site atualmente selecionado (filtro global)
    currentSiteId: null,

    /**
     * Inicializa a aplicação
     */
    async init() {
        this.setupEventListeners();
    },

    /**
     * Configura event listeners globais
     */
    setupEventListeners() {
        // Navegação da sidebar
        const menuLinks = document.querySelectorAll('.sidebar-menu a[data-page]');

        menuLinks.forEach(link => {
            link.addEventListener('click', (e) => {
                this.handleNavigation(e, link);
            });
        });
        
        // Seletor de site global
        document.getElementById('site-selector').addEventListener('change', (e) => {
            this.currentSiteId = e.target.value || null;
            
            const siteName = this.currentSiteId 
                ? Sites.getById(this.currentSiteId)?.name 
                : 'Visão geral';
                
            document.getElementById('current-site').textContent = siteName;
            Dashboard.load();
        });
        
        // Form de API Token
        const apiTokenForm = document.getElementById('api-token-form');
        if (apiTokenForm) {
            apiTokenForm.addEventListener('submit', (e) => this.handleApiTokenSubmit(e));
        }
        
        // Atualizar seletores de API quando modal de novo site for aberto
        const modalNewSite = document.getElementById('modal-new-site');
        if (modalNewSite) {
            modalNewSite.addEventListener('show.bs.modal', () => {
                if (typeof ApiTokens !== 'undefined') {
                    ApiTokens.updateSelectors();
                }
            });
        }
    },
    
    /**
     * Handler de navegação
     */
    handleNavigation(e, link) {
        e.preventDefault();
        const page = link.dataset.page;

        // Atualizar estado ativo
        document.querySelectorAll('.sidebar-menu a').forEach(a => a.classList.remove('active'));
        link.classList.add('active');

        // Mostrar página
        const pageElement = document.getElementById(`page-${page}`);

        document.querySelectorAll('.page-content').forEach(p => p.classList.add('hidden'));
        if (pageElement) {
            pageElement.classList.remove('hidden');
        }

        // Atualizar título
        document.getElementById('page-title').innerHTML = link.innerHTML;

        // Carregar dados da página
        this.loadPageData(page);
    },
    
    /**
     * Carrega dados de acordo com a página
     */
    loadPageData(page) {
        const currentUser = Auth.getCurrentUser();
        
        switch(page) {
            case 'sites':
                Sites.load();
                break;
                
            case 'events':
                Events.loadTypes();
                Events.load(1);
                break;
                
            case 'pix':
                PIX.updateSiteSelector();
                if (currentUser && currentUser.site_id && !currentUser.is_super_admin) {
                    PIX.loadConfig(currentUser.site_id);
                }
                break;
                
            case 'pix-history':
                PIX.loadHistory();
                break;
                
            case 'api-token':
                this.loadApiToken();
                break;
            
            case 'api-tokens':
                ApiTokens.load();
                break;
                
            case 'users':
                Users.populateSiteSelectors();
                Users.load();
                break;

            case 'security':
                if (typeof Security !== 'undefined') {
                    // Se usuário tem site específico, carregar config
                    if (currentUser && currentUser.site_id && !currentUser.is_super_admin) {
                        Security.loadConfig(currentUser.site_id);
                    }
                }
                break;
        }
    },
    
    /**
     * Carrega token da API (Detran)
     */
    async loadApiToken() {
        try {
            const data = await Api.get('api-token');

            if (data.status === 'success' && data.data) {
                document.getElementById('api-token-value').value = data.data.token || '';
                document.getElementById('api-token-updated').textContent = data.data.updated_at
                    ? `${new Date(data.data.updated_at).toLocaleString('pt-BR')} por ${data.data.updated_by || 'Sistema'}`
                    : 'Nunca configurado';
            } else {
                document.getElementById('api-token-value').value = '';
                document.getElementById('api-token-updated').textContent = 'Nunca configurado';
            }
        } catch (error) {
            // Silenciar em produção
        }
    },
    
    /**
     * Toggle de visibilidade do token
     */
    toggleTokenVisibility() {
        const input = document.getElementById('api-token-value');
        const icon = document.getElementById('token-eye-icon');
        
        if (input.type === 'password') {
            input.type = 'text';
            icon.classList.remove('fa-eye');
            icon.classList.add('fa-eye-slash');
        } else {
            input.type = 'password';
            icon.classList.remove('fa-eye-slash');
            icon.classList.add('fa-eye');
        }
    },
    
    /**
     * Handler de submit do form de API Token
     */
    async handleApiTokenSubmit(e) {
        e.preventDefault();
        
        const token = document.getElementById('api-token-value').value.trim();
        
        if (!token) {
            alert('Por favor, insira um token válido');
            return;
        }
        
        try {
            const data = await Api.post('api-token', { token });
            
            if (data.status === 'success') {
                const status = document.getElementById('api-token-save-status');
                status.classList.remove('hidden');
                setTimeout(() => status.classList.add('hidden'), 3000);
                this.loadApiToken();
            } else {
                alert('Erro ao salvar token: ' + (data.message || 'Erro desconhecido'));
            }
        } catch (error) {
            console.error('Save token error:', error);
            alert('Erro ao salvar token');
        }
    },
    
    /**
     * Copia texto para clipboard
     */
    copyToClipboard(elementId) {
        const input = document.getElementById(elementId);
        input.select();
        document.execCommand('copy');
        alert('Copiado!');
    },
    
    /**
     * Atualiza todos os dados
     */
    refreshData() {
        Dashboard.load();
        Sites.load();
    }
};

// Funções globais para compatibilidade com HTML inline
// Estas funções delegam para os módulos apropriados

// Sites
function createSite() { Sites.create(); }
function openSiteDetails(id) { Sites.openDetails(id); }
function saveSiteDetails() { Sites.saveDetails(); }
function saveSitePixConfig() { Sites.savePixConfig(); }
function confirmDeleteSite() { Sites.confirmDelete(); }
function deleteSite(id) { Sites.delete(id); }
function quickDeleteSite(id, name) { Sites.quickDelete(id, name); }
function clearSiteEvents(siteId, siteName, eventCount) { Sites.clearEvents(siteId, siteName, eventCount); }
function testSiteConnection() { Sites.testConnection(); }

// Users
function createUser() { Users.create(); }
function editUser(userId) { Users.edit(userId); }
function updateUser() { Users.update(); }
function deleteUser() { Users.delete(); }
function toggleSiteSelector() { Users.toggleSiteSelector(); }
function toggleEditSiteSelector() { Users.toggleEditSiteSelector(); }
function generatePassword(inputId) {
    const password = Helpers.generatePassword(12);
    const input = document.getElementById(inputId);
    if (input) {
        input.value = password;
        input.type = 'text';
        setTimeout(() => { input.type = 'password'; }, 3000);
    }
}
function populateUserSiteSelectors() { Users.populateSiteSelectors(); }
function loadUsers() { Users.load(); }
function filterUsersBySite() { Users.load(); }

// PIX
function generateTestPixQrCode() { PIX.generateTestQrCode(); }
function copyPixCode() { PIX.copyCode(); }
function generateModalTestPixQrCode() { PIX.generateModalTestQrCode(); }
function copyModalPixCode() { PIX.copyModalCode(); }
function loadPixHistory() { PIX.loadHistory(); }

// Events
function loadEvents(page) { Events.load(page); }
function clearEventFilters() { Events.clearFilters(); }
function loadEventTypes() { Events.loadTypes(); }

// Dashboard
function refreshData() { App.refreshData(); }

// Auth
function login() { Auth.login(); }
function logout() { Auth.logout(); }

// App
function toggleTokenVisibility() { App.toggleTokenVisibility(); }
function copyToClipboard(elementId) { App.copyToClipboard(elementId); }

// Security
function loadSecurityConfig(siteId) { Security.loadConfig(siteId); }
function saveSecurityConfig(siteId) { Security.saveConfig(siteId); }
function showAddIpModal() { Security.showAddIpModal(); }
function addIpRule() { Security.addIpRule(); }
function removeIpRule(ruleId) { Security.removeIpRule(ruleId); }
function loadSecurityLogs(siteId) { Security.loadLogs(siteId); }
function filterSecurityLogs() { Security.filterLogs(); }
function clearSecurityLogs() { Security.clearLogs(); }
function toggleSecurityTokenVisibility() { Security.toggleTokenVisibility(); }

// API Tokens
function openNewApiToken() { ApiTokens.openNew(); }
function openEditApiToken(id) { ApiTokens.openEdit(id); }
function saveApiToken() { ApiTokens.save(); }
function testApiToken(id) { ApiTokens.test(id); }
function deleteApiToken(id, name) { ApiTokens.delete(id, name); }
function loadApiTokens() { ApiTokens.load(); }

// Inicializar app quando DOM estiver pronto
document.addEventListener('DOMContentLoaded', () => {
    // Inicializar autenticação primeiro
    if (typeof Auth !== 'undefined' && Auth.init) {
        Auth.init();
    } else {
        // Fallback: registrar evento de login diretamente
        document.getElementById('login-form')?.addEventListener('submit', (e) => {
            e.preventDefault();
            const username = document.getElementById('login-username')?.value;
            const password = document.getElementById('login-password')?.value;
            Auth.login(username, password);
        });
    }

    App.init();
});
