# pip3 install pygetwindow pyautogui python-socketio pillow requests websocket-client
# python3 -m pip install pyautogui pygetwindow python-socketio pillow requests websocket-client
# winget install ffmpeg
# Verificar :   python3 -c "import pyautogui, socketio, PIL, requests, websocket; print('✅ Todas as dependências estão instaladas')"
# Testar se a porta 3000 está acessível
# curl -v https://173.249.31.98:3000
# cd /iot
# 1. Se não existir, instalar
# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
# python get-pip.py

# 2. Instalar as dependências do projeto
# Instalar python via homebrew // se nao tiver instalado
# brew install python3
# O pip será instalado automaticamente

# se nao tiver instalado :   /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# ls -la /opt/homebrew/bin/brew

# echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc eval "$(/opt/homebrew/bin/brew shellenv)"

# brew install ffmpeg // instalar no make
# winget install ffmpeg  // instalar no windows

# python maquina.py 1..2..3. quantas forem
# instalar os arquivos acima 3 atraz ai burro
# testar porta  :  curl -v https://173.249.31.98:3000  /  https://www.xkbitcoin.com/iot/  https://www.xkbitcoin.com:3000

# Testar se o caminho /iot/ está funcionando
# curl -v https://www.xkbitcoin.com/iot/

# Testar se o domínio com porta 3000 está funcionando
# curl -v https://www.xkbitcoin.com:3000


# pip3 install pygetwindow pyautogui python-socketio pillow requests websocket-client
# python3 -m pip install pyautogui pygetwindow python-socketio pillow requests websocket-client
# winget install ffmpeg
# Verificar :   python3 -c "import pyautogui, socketio, PIL, requests, websocket; print('✅ Todas as dependências estão instaladas')"
# Testar se a porta 3000 está acessível
# curl -v https://173.249.31.98:3000
# cd /iot
# 1. Se não existir, instalar
# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
# python get-pip.py

# 2. Instalar as dependências do projeto
# Instalar python via homebrew // se nao tiver instalado
# brew install python3
# O pip será instalado automaticamente

# se nao tiver instalado :   /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# ls -la /opt/homebrew/bin/brew

# echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc eval "$(/opt/homebrew/bin/brew shellenv)"

# brew install ffmpeg // instalar no make
# winget install ffmpeg  // instalar no windows

# python maquina.py 1..2..3. quantas forem
# instalar os arquivos acima 3 atraz ai burro
# testar porta  :  curl -v https://173.249.31.98:3000  /  https://www.xkbitcoin.com/iot/  https://www.xkbitcoin.com:3000

# Testar se o caminho /iot/ está funcionando
# curl -v https://www.xkbitcoin.com/iot/

# Testar se o domínio com porta 3000 está funcionando
# curl -v https://www.xkbitcoin.com:3000

#!/usr/bin/env python3
import pyautogui
pyautogui.FAILSAFE = False
import sys
import subprocess
import threading
import socketio
import base64
import io
import time
import platform
import os
import requests
import json
import serial
import traceback

# Tenta importar a biblioteca de teclado
try:
    import keyboard
    TECLADO_DISPONIVEL = True
except ImportError:
    TECLADO_DISPONIVEL = False
    print('⚠️ Biblioteca "keyboard" não instalada. (pip install keyboard)')

# Tenta importar a biblioteca serial
try:
    import serial
    SERIAL_DISPONIVEL = True
except ImportError:
    SERIAL_DISPONIVEL = False
    print('⚠️ Biblioteca "serial" não instalada. (pip install pyserial)')

# ==========================================
# CONFIGURAÇÕES
# ==========================================
if len(sys.argv) < 2:
    print('❌ Use: python3 maquina.py <MAQUINA_ID>')
    sys.exit(1)

MAQUINA_ID = sys.argv[1]
SERVER_URL = 'https://xkbitcoin.com'
FPS = 5
QUALIDADE_JPEG = 30

# Porta Serial do ESP32 
SISTEMA = platform.system()
PORTA_SERIAL = 'COM3' if SISTEMA == 'Windows' else '/dev/ttyUSB0'
BAUDRATE = 115200

# Mapeamento de teclas para comandos
MAPA_TECLAS = {
    'f1': 'btn_carteira',
    'f2': 'btn_ajuda',
    'f3': 'btn_cobrar',
    'f4': 'btn_cartelas',
    'f5': 'btn_ver_cartoes',
    'f6': 'btn_sair',
    'f7': 'btn_turbo',
    'f8': 'btn_aposta',   
    'f9': 'btn_extra',    
    'f10': 'btn_jogar',
    'f11': 'btn_numeros',
    'f12': 'credito_10',
    'f13': 'credito_20',
    'f14': 'credito_50',
    'f15': 'credito_100'
}

# Variáveis globais
processo_audio = None
conectado_com_sucesso = False
ultima_comunicacao = time.time()
threads_ativas = []
conexao_serial = None

print(f'🖥️ Sistema detectado: {SISTEMA}')

# ==========================================
# INICIALIZAÇÃO DA PORTA SERIAL (ESP32)
# ==========================================
if SERIAL_DISPONIVEL:
    try:
        conexao_serial = serial.Serial(PORTA_SERIAL, BAUDRATE, timeout=1)
        print(f"✅ Conectado ao ESP32 na porta {PORTA_SERIAL}!")
        time.sleep(2) # Aguarda o ESP32 reiniciar após conectar
    except Exception as e:
        print(f"⚠️ Não foi possível conectar ao ESP32: {e}")

# ==========================================
# BUSCAR TOKEN DA MÁQUINA
# ==========================================
def obter_token(maquina_id):
    try:
        url_token = f'{SERVER_URL}:3000/api/maquina/token/{maquina_id}'
        response = requests.get(url_token, timeout=10)
        
        if response.status_code == 200:
            return response.json().get('token')
        else:
            print(f'❌ Erro {response.status_code} ao buscar token.')
            return None
    except Exception as e:
        print(f'❌ Erro de requisição ao buscar token: {e}')
        return None

TOKEN = obter_token(MAQUINA_ID)
if not TOKEN:
    print('❌ Token inválido. Abortando.')
    sys.exit(1)

# ==========================================
# COMUNICAÇÃO SERIAL COM ESP32
# ==========================================
def enviar_para_esp32(comando):
    """Envia comando para o ESP32 via Serial (USB)"""
    global conexao_serial
    if conexao_serial and conexao_serial.is_open:
        try:
            conexao_serial.write(f"{comando}\n".encode())
            print(f"📤 Hardware ESP32 acionado: {comando}")
        except Exception as e:
            print(f"❌ Erro ao enviar para ESP32: {e}")

# ==========================================
# SOCKET.IO
# ==========================================
sio = socketio.Client(logger=False, engineio_logger=False)

@sio.event
def connect():
    print(f'✅ Socket conectado! (Máquina {MAQUINA_ID})')
    sio.emit('entrar_maquina', {'maquina_id': MAQUINA_ID}, callback=confirmar_conexao)

def confirmar_conexao(resposta):
    global conectado_com_sucesso
    if resposta and resposta.get('status') == 'success':
        conectado_com_sucesso = True
    else:
        print(f'❌ Servidor rejeitou entrada: {resposta.get("message") if resposta else "Sem resposta"}')

@sio.event
def disconnect():
    print('🔴 Desconectado do servidor.')

# ==========================================
# EVENTOS RECEBIDOS DO SERVIDOR
# ==========================================
@sio.on('executar_clique')
def executar_clique(data):
    try:
        largura, altura = pyautogui.size()
        x_real = int(data['x'] * largura)
        y_real = int(data['y'] * altura)
        pyautogui.click(x_real, y_real)
    except Exception as e:
        print(f'❌ Erro ao clicar: {e}')

@sio.on('executar_js')
def executar_js(data):
    try:
        funcao = data.get('funcao')
        args = data.get('args', [])

        # 1. Tenta identificar se o comando é um dos seus botões da interface Web
        if funcao == 'ClickBtn' and len(args) >= 2:
            comando_recebido = args[1]
            
            # Envia comando físico para o ESP32
            enviar_para_esp32(comando_recebido)
            
            # Envia comando virtual via PyAutoGUI (Fallback)
            tecla_alvo = next((t for t, cmd in MAPA_TECLAS.items() if cmd == comando_recebido), None)
            
            if tecla_alvo:
                print(f'⚡ Comando [{comando_recebido}]. Pressionando {tecla_alvo.upper()} via software.')
                trazer_navegador_para_frente()
                pyautogui.press(tecla_alvo)
                return

        # 2. Injeção JS na barra de endereços
        args_formatados = ", ".join([f"'{a}'" if isinstance(a, str) else str(a) for a in args])
        comando_js = f"javascript:{funcao}({args_formatados});"
        
        trazer_navegador_para_frente()
        if SISTEMA == 'Darwin':
            pyautogui.hotkey('command', 'l')
        else:
            pyautogui.hotkey('ctrl', 'l')
        time.sleep(0.15)
        pyautogui.write(comando_js)
        pyautogui.press('enter')

    except Exception as e:
        print(f'❌ Erro em executar_js: {e}')

@sio.on('inserirCredito')
def inserir_credito(data):
    try:
        maquina_id = data['args'][0]
        valor = data['args'][1]  # Valor em Reais
        
        # Calcula os créditos (R$ × 4)
        creditos = valor * 4
        
        print(f'💰 Inserindo R$ {valor} na máquina {maquina_id} ({creditos} créditos)')
        
        # Envia comando para o ESP32 com o valor em créditos
        enviar_para_esp32(f"credito_{creditos}")
        
        # Pressiona a tecla correspondente ao valor em reais (fallback)
        tecla_credito = {
            10: 'f1',
            20: 'f2',
            50: 'f3',
            100: 'f4'
        }.get(valor)
        
        if tecla_credito:
            trazer_navegador_para_frente()
            pyautogui.press(tecla_credito)
            print(f'💰 Tecla {tecla_credito.upper()} pressionada para R$ {valor}')
            
    except Exception as e:
        print(f'❌ Erro ao inserir crédito: {e}')

# ==========================================
# FUNÇÕES DE CAPTURA
# ==========================================
def trazer_navegador_para_frente():
    try:
        if SISTEMA == 'Darwin':
            script = '''
            tell application "System Events"
                set frontmost to true
                set frontmost of process "Google Chrome" to true
            end tell
            '''
            subprocess.run(['osascript', '-e', script], capture_output=True)
            time.sleep(0.5)
        elif SISTEMA == 'Linux':
            subprocess.run(['wmctrl', '-a', 'Google Chrome'], capture_output=True)
            time.sleep(0.5)
    except:
        pass

def capturar_video():
    global ultima_comunicacao
    print('📸 Stream de vídeo iniciado...')
    trazer_navegador_para_frente()
    while True:
        try:
            screenshot = pyautogui.screenshot().convert('RGB')
            buffer = io.BytesIO()
            screenshot.save(buffer, format='JPEG', quality=QUALIDADE_JPEG)
            
            if sio.connected:
                sio.emit('tela_maquina', {
                    'maquina_id': MAQUINA_ID,
                    'imagem': base64.b64encode(buffer.getvalue()).decode()
                })
                ultima_comunicacao = time.time()
            time.sleep(1 / FPS)
        except:
            time.sleep(1) # Silencia erros contínuos de frame drop

def capturar_audio():
    global processo_audio
    ffmpeg_path = 'ffmpeg'
    try:
        subprocess.run([ffmpeg_path, '-version'], capture_output=True, check=True)
        print('🎤 Stream de áudio iniciado...')
    except:
        print('⚠️ FFmpeg não encontrado. Áudio desativado.')
        return
    
    if SISTEMA == 'Darwin':
        cmd = [ffmpeg_path, '-f', 'avfoundation', '-i', ':0', '-c:a', 'aac', '-b:a', '128k', '-f', 'mp4', '-']
    elif SISTEMA == 'Windows':
        cmd = [ffmpeg_path, '-f', 'dshow', '-i', 'audio="Stereo Mix"', '-c:a', 'aac', '-b:a', '128k', '-f', 'mp4', '-']
    elif SISTEMA == 'Linux':
        cmd = [ffmpeg_path, '-f', 'alsa', '-i', 'default', '-c:a', 'aac', '-b:a', '128k', '-f', 'mp4', '-']
    else:
        return
    
    processo_audio = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
    while True:
        try:
            chunk = processo_audio.stdout.read(4096)
            if not chunk: break
            
            if sio.connected:
                sio.emit('stream_audio_video', {
                    'maquina_id': MAQUINA_ID,
                    'chunk': base64.b64encode(chunk).decode()
                })
        except:
            time.sleep(1)

def capturar_teclas():
    if not TECLADO_DISPONIVEL: return
    print('⌨️ Captura de teclado local ativada.')
    
    def ao_pressionar(e):
        if e.event_type == 'down':
            tecla = e.name.lower()
            if tecla in MAPA_TECLAS and sio.connected:
                comando = MAPA_TECLAS[tecla]
                sio.emit('executar_js', {'funcao': 'ClickBtn', 'args': [MAQUINA_ID, comando]})
                enviar_para_esp32(comando) # Opcional: Garante que teclado físico também acione hardware
                
    keyboard.hook(ao_pressionar)
    keyboard.wait()

def monitorar_conexao():
    while True:
        time.sleep(10)
        if not sio.connected:
            try:
                sio.connect(f'{SERVER_URL}:3000', auth={'token': TOKEN})
                sio.emit('entrar_maquina', {'maquina_id': MAQUINA_ID}, callback=confirmar_conexao)
            except:
                pass

# ==========================================
# INICIAR SISTEMA
# ==========================================
if __name__ == '__main__':
    try:
        sio.connect(f'{SERVER_URL}:3000', auth={'token': TOKEN})
        
        threads = [
            threading.Thread(target=capturar_video, daemon=True),
            threading.Thread(target=capturar_audio, daemon=True),
            threading.Thread(target=capturar_teclas, daemon=True),
            threading.Thread(target=monitorar_conexao, daemon=True)
        ]
        
        for t in threads:
            t.start()
            threads_ativas.append(t)
        
        print(f'✅ Sistema rodando! Pressione Ctrl+C para encerrar.')
        sio.wait()
        
    except (KeyboardInterrupt, SystemExit):
        print("\n👋 Encerrando sistema...")
        if sio.connected: sio.disconnect()
        if conexao_serial and conexao_serial.is_open: conexao_serial.close()
        if processo_audio: processo_audio.terminate()
        sys.exit(0)