const express = require('express');
const http = require('http');
const https = require('https');
const zlib = require('zlib');
const { HttpsProxyAgent } = require('https-proxy-agent');
const cluster = require('cluster');
const os = require('os');
const path = require('path');
const Database = require('better-sqlite3');
const fs = require('fs');
const axios = require('axios');

const PORT = 3000;
const NUM_WORKERS = 1;

// ========== SISTEMA DE API KEYS ==========
const DB_FOLDER_NAME = 'f7imr6zs30ovzazu';
const DB_DIR = path.join(__dirname, DB_FOLDER_NAME);
const DB_PATH = path.join(DB_DIR, 'keys.db');

function validateKey(keyValue) {
    const db = new Database(DB_PATH, { readonly: false });
    try {
        const key = db.prepare('SELECT * FROM api_keys WHERE key_value = ? AND active = 1').get(keyValue);
        if (!key) return null;
        const now = new Date();
        const expires = new Date(key.expires_at);
        if (now >= expires) return null;
        db.prepare('UPDATE api_keys SET total_requests = total_requests + 1, last_used_at = datetime(\'now\', \'localtime\') WHERE key_value = ?').run(keyValue);
        return key;
    } finally {
        db.close();
    }
}

function logRequest(keyValue, placa, success, ip, state = 'UNKNOWN') {
    try {
        const db = new Database(DB_PATH);
        db.prepare('INSERT INTO request_log (key_value, placa, success, ip, state) VALUES (?, ?, ?, ?, ?)').run(keyValue, placa, success ? 1 : 0, ip || '', state);
        db.close();
    } catch (e) {
        console.error('[KEYS] Erro ao logar request:', e.message);
    }
}

// ========== SISTEMA DE CONCORRÊNCIA E FILAS ==========
const MAX_CONCURRENT_REQUESTS = 10;
const CAPTCHA_POOL_SIZE = 5;
const CAPTCHA_TOKEN_TTL = 110000;

function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

let activeRequests = 0;
const requestQueue = [];
const captchaTokenPool = [];
let isRefillingPool = false;

const stats = { totalRequests: 0, activeRequests: 0, queuedRequests: 0, completedRequests: 0, failedRequests: 0, captchaPoolSize: 0 };

// Configurações APIs Externas
const CAPSOLVER_API_KEY = 'CAP-F424C55AC7E0360F95AB48383D9A0FEBD7DD7BB358E39F21F4EA91CEFF923326';
const CAP_URL = 'https://api.capsolver.com';
const RECAPTCHA_SITE_KEY = '6LdyyJgpAAAAAI2M2_rLl0QRQ0osF9q3Hq9ZT0Bo';
const RECAPTCHA_PAGE_URL = 'https://www.b2b.usezapay.com.br/';
const TURNSTILE_SITE_KEY_MG = '0x4AAAAAAAWV7kjZLnydRbx6';
const TURNSTILE_PAGE_URL_MG = 'https://veiculosmg.fazenda.mg.gov.br/buscar-renavam/';

const PROXY_CONFIG = {
    enabled: true,
    host: 'gate.nodemaven.com',
    port: 8080,
    username: 'pkscabelim_gmail_com-country-br-ipv4-true',
    password: 'xcy2s9jsob'
};

function getProxyAgent(sessionId = null) {
    if (!PROXY_CONFIG.enabled) return undefined;
    let username = PROXY_CONFIG.username;
    if (sessionId) username = `pkscabelim_gmail_com-country-br-ipv4-true-session-${sessionId}`;
    const proxyUrl = `http://${username}:${PROXY_CONFIG.password}@${PROXY_CONFIG.host}:${PROXY_CONFIG.port}`;
    return new HttpsProxyAgent(proxyUrl, { keepAlive: true, timeout: 60000, rejectUnauthorized: false });
}

// ========== CAPTCHA POOL ==========
async function getCaptchaToken() {
    while (captchaTokenPool.length > 0) {
        const tokenData = captchaTokenPool.shift();
        if (Date.now() - tokenData.createdAt < CAPTCHA_TOKEN_TTL) {
            stats.captchaPoolSize = captchaTokenPool.length;
            refillCaptchaPool();
            return tokenData.token;
        }
    }
    refillCaptchaPool();
    return await solveRecaptcha();
}

async function refillCaptchaPool() {
    if (isRefillingPool) return;
    isRefillingPool = true;
    try {
        const tokensNeeded = CAPTCHA_POOL_SIZE - captchaTokenPool.length;
        const promises = [];
        for (let i = 0; i < Math.min(tokensNeeded, 3); i++) {
            promises.push(solveRecaptcha().then(token => {
                captchaTokenPool.push({ token, createdAt: Date.now() });
                stats.captchaPoolSize = captchaTokenPool.length;
            }).catch(e => {}));
        }
        await Promise.allSettled(promises);
    } finally { isRefillingPool = false; }
}

async function solveRecaptcha(action = '760_get_debts') {
    try {
        const create = await axios.post(`${CAP_URL}/createTask`, {
            clientKey: CAPSOLVER_API_KEY,
            task: { type: 'ReCaptchaV3EnterpriseTaskProxyLess', websiteURL: RECAPTCHA_PAGE_URL, websiteKey: RECAPTCHA_SITE_KEY, pageAction: action }
        });
        if (create.data.errorId !== 0) throw new Error(create.data.errorDescription);
        const taskId = create.data.taskId;
        for (let i = 0; i < 60; i++) {
            await sleep(2000);
            const res = await axios.post(`${CAP_URL}/getTaskResult`, { clientKey: CAPSOLVER_API_KEY, taskId });
            if (res.data.status === 'ready') return res.data.solution.gRecaptchaResponse;
        }
        throw new Error('Timeout Recaptcha');
    } catch (e) { throw e; }
}

async function solveTurnstile() {
    try {
        const create = await axios.post(`${CAP_URL}/createTask`, {
            clientKey: CAPSOLVER_API_KEY,
            task: { type: 'AntiTurnstileTaskProxyLess', websiteURL: TURNSTILE_PAGE_URL_MG, websiteKey: TURNSTILE_SITE_KEY_MG, metadata: { action: 'consulta-buscar-renavam' } }
        });
        if (create.data.errorId !== 0) throw new Error(create.data.errorDescription);
        const taskId = create.data.taskId;
        for (let i = 0; i < 20; i++) {
            await sleep(3000);
            const res = await axios.post(`${CAP_URL}/getTaskResult`, { clientKey: CAPSOLVER_API_KEY, taskId });
            if (res.data.status === 'ready') return res.data.solution.token;
        }
        throw new Error('Timeout Turnstile');
    } catch (e) { throw e; }
}

// ========== REQUISIÇÕES E CONCORRÊNCIA ==========
async function makeRequest(options, postData, agent, retries = 3) {
    let lastError;
    const protocol = options.port === 80 ? http : https;

    for (let attempt = 1; attempt <= retries; attempt++) {
        try {
            return await new Promise((resolve, reject) => {
                const reqOptions = { ...options, agent, timeout: 60000 };
                const req = protocol.request(reqOptions, (res) => {
                    let chunks = [];
                    res.on('data', (chunk) => chunks.push(chunk));
                    res.on('end', () => {
                        let buffer = Buffer.concat(chunks);
                        let encoding = res.headers['content-encoding'];
                        try {
                            if (encoding === 'gzip') buffer = zlib.gunzipSync(buffer);
                            else if (encoding === 'deflate') buffer = zlib.inflateSync(buffer);
                            else if (encoding === 'br') buffer = zlib.brotliDecompressSync(buffer);
                            // Remover BOM se presente
                            let data = buffer.toString('utf8').replace(/^\uFEFF/, '');
                            try { resolve(JSON.parse(data)); } catch (e) { resolve(data); }
                        } catch (err) { reject(err); }
                    });
                });
                req.on('error', reject);
                if (postData) req.write(postData);
                req.end();
            });
        } catch (err) {
            lastError = err;
            if (attempt < retries) await sleep(2000 * attempt);
            else throw lastError;
        }
    }
}

async function processWithConcurrencyLimit(params, consultFunction) {
    if (activeRequests >= MAX_CONCURRENT_REQUESTS) {
        return new Promise((resolve, reject) => {
            requestQueue.push({ params, consultFunction, resolve, reject });
            stats.queuedRequests = requestQueue.length;
        });
    }
    activeRequests++;
    stats.activeRequests = activeRequests;
    stats.totalRequests++;
    try {
        const result = await consultFunction(...params);
        if (result.success) stats.completedRequests++; else stats.failedRequests++;
        return result;
    } finally {
        activeRequests--;
        stats.activeRequests = activeRequests;
        processQueue();
    }
}

function processQueue() {
    if (requestQueue.length > 0 && activeRequests < MAX_CONCURRENT_REQUESTS) {
        const next = requestQueue.shift();
        stats.queuedRequests = requestQueue.length;
        processWithConcurrencyLimit(next.params, next.consultFunction).then(next.resolve).catch(next.reject);
    }
}

// ========== FUNÇÕES DE CONSULTA ==========

async function createDebtsQuery(placa, email, recaptchaToken) {
    const postData = JSON.stringify({ store: 760, customer: { email }, service: { serviceType: 'debts', licensePlate: placa.toUpperCase(), email } });
    const options = { hostname: 'api.b2b.usezapay.com.br', port: 443, path: '/v1/zapi/ecommerce/debts/', method: 'POST', headers: { ...getDefaultHeaders(), 'use-recaptcha-enterprise': 'true', 'recaptcha-token': recaptchaToken, 'Content-Length': Buffer.byteLength(postData) } };
    return makeRequest(options, postData, getProxyAgent());
}

async function getProtocolDebts(protocol) {
    const postData = JSON.stringify({ test_ecomm: { "device": "desktop" } });
    const options = { hostname: 'api.b2b.usezapay.com.br', port: 443, path: `/v1/zapi/ecommerce/protocol-debts/${protocol}`, method: 'POST', headers: { ...getDefaultHeaders(), 'Content-Length': Buffer.byteLength(postData) } };
    return makeRequest(options, postData, getProxyAgent());
}

async function consultarZapay(placa, email = null) {
    const randomEmail = email || getRandomEmail();
    try {
        const token = await getCaptchaToken();
        const create = await createDebtsQuery(placa, randomEmail, token);
        if (!create || !create.protocol) return { success: false, error: 'Falha na consulta Zapay', response: create };
        const protocol = create.protocol;
        for (let i = 0; i < 30; i++) {
            const debts = await getProtocolDebts(protocol);
            if (debts && debts.status !== 'PROCESSING' && debts.status !== 'PENDING') return { success: true, placa, data: filterResponse(debts) };
            await sleep(2000);
        }
        return { success: false, error: 'Timeout Zapay' };
    } catch (e) { return { success: false, error: e.message }; }
}

async function consultarMG(renavam) {
    try {
        const token = await solveTurnstile();
        const sessionId = Math.random().toString(36).substring(2, 12);
        const options = { hostname: 'veiculosmg.fazenda.mg.gov.br', port: 443, path: `/api/extrato-debito/renavam/${renavam}/`, method: 'GET', headers: { 'Token': token, 'User-Agent': getRandomUserAgent(), 'Referer': TURNSTILE_PAGE_URL_MG, 'Origin': 'https://veiculosmg.fazenda.mg.gov.br' } };
        const response = await makeRequest(options, null, getProxyAgent(sessionId));
        if (response && response.veiculo) return { success: true, placa: response.veiculo.placa, data: response };
        return { success: false, error: 'Veículo não encontrado MG' };
    } catch (e) { return { success: false, error: e.message }; }
}

async function consultarRJ(renavam) {
    try {
        // RJ NÃO USA PROXY no PHP original
        const options = { hostname: '18.231.253.198', port: 80, path: `/rj2.php?renavam=${renavam}`, method: 'GET', headers: { 'User-Agent': getRandomUserAgent() } };
        const response = await makeRequest(options, null, undefined);
        if (response && response.veiculo) return { success: true, placa: response.veiculo.nuPlaca, data: response };
        return { success: false, error: 'Veículo não encontrado RJ' };
    } catch (e) { return { success: false, error: e.message }; }
}

async function consultarMS(placa, renavam) {
    try {
        // MS NÃO USA PROXY no PHP original
        const options = { hostname: '52.15.37.114', port: 80, path: `/dk/api.php?placa=${placa}&renavam=${renavam}`, method: 'GET', headers: { 'User-Agent': getRandomUserAgent() } };
        const response = await makeRequest(options, null, undefined);
        if (response && response.success) {
            return { success: true, placa, data: response };
        }
        return { success: false, error: response.error || 'Veículo não encontrado MS ou erro na API externa' };
    } catch (e) { return { success: false, error: e.message }; }
}

// ========== UTILS ==========
const USER_AGENTS = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'];
function getRandomUserAgent() { return USER_AGENTS[0]; }
function getRandomEmail() { return `user${Math.floor(Math.random()*9999)}@gmail.com`; }
function getDefaultHeaders() { return { 'User-Agent': getRandomUserAgent(), 'Accept': 'application/json', 'Content-Type': 'application/json' }; }
function filterResponse(data) {
    if (!data || typeof data !== 'object') return data;
    const filtered = { ...data };
    delete filtered.kpkey; delete filtered.store; delete filtered.konduto_public_keys;
    return filtered;
}

// ========== MAPEAMENTO BIN (TRADUÇÃO COMPLETA) ==========
const BIN_MAPPINGS = {
    colors: {
        "01": "AMARELO", "02": "AZUL", "03": "BEGE", "04": "BRANCA", "05": "CINZA",
        "06": "DOURADA", "07": "GRENÁ", "08": "LARANJA", "09": "MARROM", "10": "PRATA",
        "11": "PRETA", "12": "ROSA", "13": "ROXA", "14": "VERDE", "15": "VERMELHA", "16": "FANTASIA"
    },
    types: {
        "02": "CICLOMOTOR", "03": "MOTONETA", "04": "MOTOCICLETA", "05": "TRICICLO",
        "06": "AUTOMÓVEL", "07": "MICROÔNIBUS", "08": "ÔNIBUS", "10": "REBOQUE",
        "13": "CAMINHONETE", "14": "CAMINHÃO", "17": "CAMINHÃO TRATOR", "22": "ESPECIAL",
        "23": "COLEÇÃO", "24": "UTILITÁRIO"
    },
    fuels: {
        "01": "ÁLCOOL", "02": "GASOLINA", "03": "DIESEL", "16": "ÁLCOOL/GASOLINA (FLEX)",
        "17": "GASOLINA/GNV", "18": "ÁLCOOL/GNV", "21": "ELÉTRICO"
    },
    brands: {
        // MOTOS
        "2710": { marca: "HONDA", modelo: "HONDA MOTOCICLETA" },
        "2715": { marca: "YAMAHA", modelo: "YAMAHA MOTOCICLETA" },
        "2730": { marca: "SUZUKI", modelo: "SUZUKI MOTOCICLETA" },
        "2740": { marca: "BMW", modelo: "BMW MOTOCICLETA" },
        // CARROS
        "1015": { marca: "VOLKSWAGEN", modelo: "VW - VOLKSWAGEN" },
        "1020": { marca: "FIAT", modelo: "FIAT" },
        "1025": { marca: "CHEVROLET", modelo: "GM - CHEVROLET" },
        "1030": { marca: "FORD", modelo: "FORD" },
        "1045": { marca: "TOYOTA", modelo: "TOYOTA" },
        "1050": { marca: "HYUNDAI", modelo: "HYUNDAI" },
        "1060": { marca: "RENAULT", modelo: "RENAULT" },
        "1070": { marca: "HONDA", modelo: "HONDA" },
        "1080": { marca: "NISSAN", modelo: "NISSAN" },
        "1090": { marca: "JEEP", modelo: "JEEP" }
    }
};

const CITY_MAPPING = {
    "9373": "GOIÂNIA",
    "6001": "RIO DE JANEIRO",
    "4123": "BELO HORIZONTE",
    "9701": "BRASÍLIA"
};

const MODEL_CODES = {
    "563359": "HONDA CB 300R",
    "563300": "HONDA XRE 300",
    "563301": "HONDA XRE 300",
    "563350": "HONDA CB 300R",
    "563310": "HONDA NX-4 FALCON",
    "101501": "VW GOL",
    "102001": "FIAT UNO",
    "102501": "GM ONIX"
};

function translateBIN(body) {
    if (!body) return {};
    const typeKey = String(body.vehicle_type || "").padStart(2, '0');
    const fuelKey = String(body.fuel || "").padStart(2, '0');
    const colorKey = String(body.vehicle_color || "").padStart(2, '0');
    const brandKey = String(body.brand_model || "");
    const lineKey = String(body.line || "").substring(0, 6);
    
    const brandInfo = BIN_MAPPINGS.brands[brandKey] || { marca: "OUTROS", modelo: "N/A" };
    
    // Prioridade 1: Código de Modelo Específico (BIN Line)
    let modeloFinal = MODEL_CODES[lineKey] || brandInfo.modelo;
    
    // Prioridade 2: Heurística de cilindrada (caso não tenha o código de linha)
    if (modeloFinal === "HONDA MOTOCICLETA" || modeloFinal === "N/A") {
        if (brandKey === "2710") {
            if (body.engine_capacity === "291") modeloFinal = "HONDA CB 300R";
            else if (body.engine_capacity === "149") modeloFinal = "HONDA CG 150";
            else if (body.engine_capacity === "162") modeloFinal = "HONDA CG 160";
        }
    }

    return {
        cor: BIN_MAPPINGS.colors[colorKey] || body.vehicle_color || "N/A",
        tipo: BIN_MAPPINGS.types[typeKey] || body.vehicle_type || "N/A",
        combustivel: BIN_MAPPINGS.fuels[fuelKey] || body.fuel || "N/A",
        marca_modelo_cod: body.brand_model,
        line_cod: body.line,
        marca: brandInfo.marca,
        modelo: modeloFinal,
        ano_fabricacao: body.manufacture_year,
        ano_modelo: body.model_year,
        municipio: CITY_MAPPING[body.city] || body.city || "N/A",
        municipio_cod: body.city,
        uf: body.state_plate,
        chassi: body.chassis,
        placa_mercosul: body.new_plate_model || body.plate
    };
}

async function consultarDH(placa) {
    const apikey = 'b26b41804898eaee94c04a1795fc532ce5c30a6ea0cf9e1c0c525465774eabee';
    const url = `https://api-dh.ciphers.systems/api/v2/vehicle/plate/?plate=${placa}&apikey=${apikey}`;
    try {
        const response = await axios.get(url, { timeout: 10000 });
        if (response.data && response.data.statusCode === 200) {
            return {
                success: true,
                raw: response.data.body,
                translated: translateBIN(response.data.body)
            };
        }
        return { success: false, error: 'DH API Error' };
    } catch (e) {
        return { success: false, error: e.message };
    }
}

// ========== EXPRESS AND CLUSTER ==========
if (cluster.isPrimary) {
    for (let i = 0; i < NUM_WORKERS; i++) cluster.fork();
    cluster.on('exit', () => cluster.fork());
} else {
    const app = express();
    app.use(express.json());

    app.get('/consultar/:key/:placa', async (req, res) => {
        const { key, placa } = req.params;
        if (!validateKey(key)) return res.status(403).json({ success: false, error: 'Key inválida' });
        
        // 1. Busca débitos (Zapay)
        const result = await processWithConcurrencyLimit([placa], consultarZapay);
        
        // 2. Busca dados técnicos (DH API) para complementar
        const dhResult = await consultarDH(placa);
        
        if (dhResult.success) {
            // Se Zapay falhou mas temos dados técnicos, pelo menos retornamos o sucesso parcial
            if (!result.success) {
                result.success = true;
                result.placa = placa;
                result.data = { vehicle: {} };
            }
            
            // Mescla dados traduzidos para o objeto de veículo
            if (result.data && result.data.vehicle) {
                Object.assign(result.data.vehicle, dhResult.translated);
                result.data.vehicle.marca_modelo_cod = dhResult.raw.brand_model;
            }
            
            // Adiciona o bloco bruto para depuração se necessário (opcional)
            result.extra_details = dhResult.translated;
        }

        logRequest(key, placa, result.success, req.ip, 'ZAPAY');
        res.json(result);
    });

    app.get('/consultar/mg/:key/:renavam', async (req, res) => {
        const { key, renavam } = req.params;
        if (!validateKey(key)) return res.status(403).json({ success: false, error: 'Key inválida' });
        if (!renavam || renavam.length < 9) return res.status(400).json({ success: false, error: 'Renavam inválido ou não fornecido.' });
        const result = await processWithConcurrencyLimit([renavam], consultarMG);
        logRequest(key, renavam, result.success, req.ip, 'MG');
        res.json(result);
    });

    app.get('/consultar/rj/:key/:renavam', async (req, res) => {
        const { key, renavam } = req.params;
        if (!validateKey(key)) return res.status(403).json({ success: false, error: 'Key inválida' });
        if (!renavam || renavam.length < 9) return res.status(400).json({ success: false, error: 'Renavam inválido ou não fornecido.' });
        const result = await processWithConcurrencyLimit([renavam], consultarRJ);
        logRequest(key, renavam, result.success, req.ip, 'RJ');
        res.json(result);
    });

    app.get('/consultar/ms/:key/:placa/:renavam', async (req, res) => {
        const { key, placa, renavam } = req.params;
        if (!validateKey(key)) return res.status(403).json({ success: false, error: 'Key inválida' });
        if (!placa || !renavam) return res.status(400).json({ success: false, error: 'Placa e Renavam são obrigatórios.' });
        const result = await processWithConcurrencyLimit([placa, renavam], consultarMS);
        logRequest(key, placa, result.success, req.ip, 'MS');
        res.json(result);
    });

    app.get('/', (req, res) => res.json({ status: 'ok', endpoints: ['/consultar/:key/:placa', '/consultar/mg/:key/:renavam', '/consultar/rj/:key/:renavam', '/consultar/ms/:key/:placa/:renavam'] }));

    app.listen(PORT, () => {
        console.log(`Worker ${cluster.worker.id} na porta ${PORT}`);
        refillCaptchaPool();
    });
}
