"""
app_detector.py
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cobre a camada:
  ✅ Camada 4 — Tipo de cada aplicativo

Detecta automaticamente se o app é:
  • Win32 nativo     → UI Automation direta
  • WPF / UWP        → UI Automation + padrões UIA
  • Electron         → Screenshot + Visão Computacional
  • Java Swing / AWT → Java Access Bridge (JAB)
  • Web Browser      → Playwright / CDP
  • Jogo / OpenGL    → Visão Computacional pura
  • Desconhecido     → Tenta UI Automation + fallback
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"""

import os
import json
import ctypes
import winreg
from pathlib import Path
from typing import Optional
from dataclasses import dataclass, field
from loguru import logger


# ══════════════════════════════════════════════════════
# TIPOS DE APP E ABORDAGENS
# ══════════════════════════════════════════════════════

@dataclass
class TipoApp:
    """Resultado da detecção de tipo de app."""
    tipo:              str           # WIN32, WPF, UWP, ELECTRON, JAVA, BROWSER, GAME, UNKNOWN
    abordagem:         str           # UI_AUTOMATION, SCREENSHOT_VISION, JAB, PLAYWRIGHT, MIXED
    confianca:         float         # 0.0 a 1.0
    detalhes:          str           # descrição humana
    fallback:          str           # abordagem de fallback
    pode_usar_uia:     bool          # pode tentar UI Automation?
    requer_visao:      bool          # precisa de screenshot/visão?
    requer_playwright: bool          # precisa de Playwright?
    dicas:             list = field(default_factory=list)


# ══════════════════════════════════════════════════════
# ASSINATURAS DE IDENTIFICAÇÃO
# ══════════════════════════════════════════════════════

# Classes de janela conhecidas
CLASSES_WIN32 = [
    "Notepad", "WordPadClass", "MSPaintApp", "CabinetWClass",
    "#32770",   # diálogos genéricos Win32
    "ConsoleWindowClass", "XLMAIN", "OpusApp",
    "ThunderRT6FormDC", "ThunderRT6Main",
]

CLASSES_WPF = [
    "HwndWrapper", "HwndSource",
]

CLASSES_UWP = [
    "ApplicationFrameWindow", "Windows.UI.Core.CoreWindow",
    "WINUI3",
]

CLASSES_ELECTRON = [
    "Chrome_WidgetWin_1",  # Electron usa Chromium internamente
    "CefBrowserWindow",
    "NativeWindow",
]

CLASSES_JAVA = [
    "SunAwtFrame", "SunAwtDialog", "SunAwtWindow",
    "javax.swing.JFrame",
]

CLASSES_BROWSER = [
    "Chrome_WidgetWin_1",   # Chrome e Edge usam mesma classe
    "MozillaWindowClass",   # Firefox
    "IEFrame",              # IE / IE mode
    "OperaWindowClass",
]

CLASSES_GAME = [
    "UnityWndClass",        # Unity
    "UE4Game",              # Unreal Engine
    "SDL_app",              # SDL
    "GLFW30",               # OpenGL / GLFW
    "LWJGL",                # Java games
]

# Apps Electron conhecidos (pelo nome do processo)
PROCESSOS_ELECTRON = [
    "code.exe",        # VS Code
    "discord.exe",
    "slack.exe",
    "notion.exe",
    "spotify.exe",
    "obsidian.exe",
    "figma.exe",
    "cursor.exe",
    "insomnia.exe",
    "postman.exe",
    "1password.exe",
    "teams.exe",       # Microsoft Teams (versão nova)
    "whatsapp.exe",
]

# Browsers conhecidos
PROCESSOS_BROWSER = [
    "chrome.exe",
    "msedge.exe",
    "firefox.exe",
    "opera.exe",
    "brave.exe",
    "vivaldi.exe",
    "iexplore.exe",
]

# Apps Java conhecidos
PROCESSOS_JAVA = [
    "javaw.exe",
    "java.exe",
    "eclipse.exe",
    "intellij.exe",
    "idea64.exe",
    "netbeans64.exe",
]


# ══════════════════════════════════════════════════════
# DETECTOR PRINCIPAL
# ══════════════════════════════════════════════════════

class DetectorApp:
    """Detecta o tipo de aplicativo e define a melhor abordagem de automação."""

    def detectar(self, nome_janela: str = "",
                 classe_janela: str = "",
                 nome_processo: str = "",
                 exe_path: str = "") -> TipoApp:
        """
        Detecta o tipo do app usando múltiplas estratégias em cascata.
        """
        nome_proc_lower   = nome_processo.lower()
        classe_lower      = classe_janela.lower()
        exe_lower         = exe_path.lower()

        logger.debug(
            f"Detectando tipo — janela='{nome_janela}' "
            f"classe='{classe_janela}' proc='{nome_processo}'"
        )

        # ── 1. Jogo / OpenGL
        if self._e_jogo(classe_janela, nome_janela, exe_lower):
            return TipoApp(
                tipo="GAME",
                abordagem="SCREENSHOT_VISION",
                confianca=0.95,
                detalhes="Jogo ou app OpenGL/DirectX — sem suporte a UI Automation",
                fallback="SCREENSHOT_VISION",
                pode_usar_uia=False,
                requer_visao=True,
                requer_playwright=False,
                dicas=[
                    "Usar screenshot + Grok Vision para identificar elementos",
                    "Coordenadas podem variar por resolução — sempre recalcular"
                ]
            )

        # ── 2. Browser
        if self._e_browser(classe_janela, nome_proc_lower, nome_janela):
            return TipoApp(
                tipo="BROWSER",
                abordagem="PLAYWRIGHT",
                confianca=0.98,
                detalhes="Navegador web — usar Playwright/CDP para máxima precisão",
                fallback="UI_AUTOMATION",
                pode_usar_uia=True,
                requer_visao=False,
                requer_playwright=True,
                dicas=[
                    "Playwright tem acesso ao DOM — muito mais preciso que UI Automation",
                    "Usar CDP (Chrome DevTools Protocol) para Chrome e Edge",
                    "Para Firefox usar GeckoDriver",
                    "UI Automation pode funcionar para a barra de endereço e abas"
                ]
            )

        # ── 3. Electron
        if self._e_electron(classe_janela, nome_proc_lower, exe_path):
            return TipoApp(
                tipo="ELECTRON",
                abordagem="MIXED",
                confianca=0.92,
                detalhes="App Electron (Chromium embutido) — UI Automation limitada, prefira visão",
                fallback="SCREENSHOT_VISION",
                pode_usar_uia=False,
                requer_visao=True,
                requer_playwright=True,
                dicas=[
                    "Electron expõe acessibilidade parcial — testar primeiro",
                    "Playwright via CDP funciona se o app permitir DevTools",
                    "Screenshot + Grok Vision é o fallback mais confiável",
                    "VS Code e Discord têm menus acessíveis via UI Automation básica"
                ]
            )

        # ── 4. Java
        if self._e_java(classe_janela, nome_proc_lower):
            return TipoApp(
                tipo="JAVA",
                abordagem="JAB",
                confianca=0.90,
                detalhes="App Java (Swing/AWT) — usar Java Access Bridge",
                fallback="SCREENSHOT_VISION",
                pode_usar_uia=False,
                requer_visao=False,
                requer_playwright=False,
                dicas=[
                    "Ativar Java Access Bridge: jabswitch -enable",
                    "Requer reinicialização após ativar JAB",
                    "pywinauto suporta JAB com backend='jabwrapper'",
                    "Se JAB falhar → screenshot + Grok Vision"
                ]
            )

        # ── 5. UWP (Windows 10/11 apps modernos)
        if self._e_uwp(classe_janela, nome_janela):
            return TipoApp(
                tipo="UWP",
                abordagem="UI_AUTOMATION",
                confianca=0.88,
                detalhes="App UWP (Windows Store) — UI Automation com padrões modernos",
                fallback="SCREENSHOT_VISION",
                pode_usar_uia=True,
                requer_visao=False,
                requer_playwright=False,
                dicas=[
                    "UWP usa padrões UIA modernos — InvokePattern, TogglePattern",
                    "Elementos podem ter nomes diferentes de apps Win32",
                    "Usar AutomationId quando disponível (mais estável que nome)",
                    "Calculadora, Fotos, Configurações são exemplos UWP"
                ]
            )

        # ── 6. WPF
        if self._e_wpf(classe_janela):
            return TipoApp(
                tipo="WPF",
                abordagem="UI_AUTOMATION",
                confianca=0.90,
                detalhes="App WPF (.NET) — UI Automation completa",
                fallback="SCREENSHOT_VISION",
                pode_usar_uia=True,
                requer_visao=False,
                requer_playwright=False,
                dicas=[
                    "WPF tem excelente suporte a UI Automation",
                    "Usar AutomationId para elementos — mais estável que nome",
                    "Visual Studio, apps .NET modernos são exemplos WPF"
                ]
            )

        # ── 7. Win32 nativo (padrão para maioria dos apps)
        if self._e_win32(classe_janela, nome_janela):
            return TipoApp(
                tipo="WIN32",
                abordagem="UI_AUTOMATION",
                confianca=0.95,
                detalhes="App Win32 nativo — UI Automation direta, máxima precisão",
                fallback="SCREENSHOT_VISION",
                pode_usar_uia=True,
                requer_visao=False,
                requer_playwright=False,
                dicas=[
                    "UI Automation funciona perfeitamente em apps Win32",
                    "Notepad, Explorer, Paint, Word, Excel são exemplos",
                    "Usar ButtonControl, EditControl, MenuControl diretamente"
                ]
            )

        # ── 8. Desconhecido — tentar UI Automation com fallback
        return TipoApp(
            tipo="UNKNOWN",
            abordagem="MIXED",
            confianca=0.50,
            detalhes=f"Tipo não identificado — tentando UI Automation com fallback visual",
            fallback="SCREENSHOT_VISION",
            pode_usar_uia=True,
            requer_visao=True,
            requer_playwright=False,
            dicas=[
                "Tentar UI Automation primeiro",
                "Se falhar → capturar screenshot e usar Grok Vision",
                "Reportar tipo desconhecido para melhorar o detector"
            ]
        )

    # ── Métodos de verificação internos

    def _e_jogo(self, classe: str, nome: str, exe: str) -> bool:
        for c in CLASSES_GAME:
            if c.lower() in classe.lower():
                return True
        termos_jogo = ["game", "unity", "unreal", "steam", "launcher"]
        return any(t in exe.lower() for t in termos_jogo)

    def _e_browser(self, classe: str, processo: str, nome: str) -> bool:
        for p in PROCESSOS_BROWSER:
            if p in processo:
                return True
        return False

    def _e_electron(self, classe: str, processo: str, exe: str) -> bool:
        # Electron usa a classe Chrome_WidgetWin_1
        if "Chrome_WidgetWin_1" in classe:
            # Mas browsers também usam — excluir browsers
            for b in PROCESSOS_BROWSER:
                if b in processo:
                    return False
            return True
        for p in PROCESSOS_ELECTRON:
            if p in processo:
                return True
        # Verificar se o .exe tem resources/electron.asar
        if exe:
            pasta = Path(exe).parent
            if (pasta / "resources" / "app.asar").exists():
                return True
            if (pasta / "resources" / "electron.asar").exists():
                return True
        return False

    def _e_java(self, classe: str, processo: str) -> bool:
        for c in CLASSES_JAVA:
            if c.lower() in classe.lower():
                return True
        for p in PROCESSOS_JAVA:
            if p in processo:
                return True
        return False

    def _e_uwp(self, classe: str, nome: str) -> bool:
        for c in CLASSES_UWP:
            if c.lower() in classe.lower():
                return True
        return False

    def _e_wpf(self, classe: str) -> bool:
        for c in CLASSES_WPF:
            if c.lower() in classe.lower():
                return True
        return False

    def _e_win32(self, classe: str, nome: str) -> bool:
        for c in CLASSES_WIN32:
            if c.lower() in classe.lower():
                return True
        # Heurística: se tem nome e classe reconhecíveis → provavelmente Win32
        return bool(classe and nome)


# ══════════════════════════════════════════════════════
# VERIFICADOR DE JAVA ACCESS BRIDGE
# ══════════════════════════════════════════════════════

def jab_esta_ativado() -> bool:
    """Verifica se o Java Access Bridge está ativo no sistema."""
    try:
        chave = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\JavaSoft\Java Runtime Environment"
        )
        jre_versao, _ = winreg.QueryValueEx(chave, "CurrentVersion")
        jre_path_chave = winreg.OpenKey(chave, jre_versao)
        jre_path, _ = winreg.QueryValueEx(jre_path_chave, "JavaHome")
        jab_dll = Path(jre_path) / "bin" / "WindowsAccessBridge-64.dll"
        return jab_dll.exists()
    except:
        return False


def ativar_jab():
    """Tenta ativar o Java Access Bridge via jabswitch."""
    try:
        import subprocess
        resultado = subprocess.run(
            ["jabswitch", "-enable"],
            capture_output=True, text=True, timeout=10
        )
        if resultado.returncode == 0:
            logger.success("Java Access Bridge ativado com sucesso")
            return True
        else:
            logger.warning(f"jabswitch retornou: {resultado.stderr}")
            return False
    except Exception as e:
        logger.error(f"Erro ao ativar JAB: {e}")
        return False


# ══════════════════════════════════════════════════════
# MAPEAR TIPO DE TODAS AS JANELAS ABERTAS
# ══════════════════════════════════════════════════════

def mapear_tipos_apps_abertos() -> list:
    """
    Para cada janela aberta, detecta e retorna o tipo e a melhor abordagem.
    """
    import uiautomation as auto
    import psutil

    detector  = DetectorApp()
    resultado = []

    # Mapa de PID → processo
    proc_map = {}
    for p in psutil.process_iter(["pid", "name", "exe"]):
        try:
            proc_map[p.info["pid"]] = {
                "nome": p.info["name"] or "",
                "exe":  p.info["exe"]  or ""
            }
        except:
            continue

    for janela in auto.GetRootControl().GetChildren():
        try:
            if not janela.Name:
                continue

            pid     = janela.ProcessId
            proc    = proc_map.get(pid, {})
            detecao = detector.detectar(
                nome_janela   = janela.Name,
                classe_janela = janela.ClassName or "",
                nome_processo = proc.get("nome", ""),
                exe_path      = proc.get("exe",  "")
            )

            resultado.append({
                "janela":     janela.Name,
                "classe":     janela.ClassName or "",
                "processo":   proc.get("nome", ""),
                "tipo":       detecao.tipo,
                "abordagem":  detecao.abordagem,
                "confianca":  detecao.confianca,
                "detalhes":   detecao.detalhes,
                "pode_uia":   detecao.pode_usar_uia,
                "requer_visao": detecao.requer_visao,
                "dicas":      detecao.dicas
            })

        except Exception:
            continue

    logger.info(f"Tipos de apps mapeados: {len(resultado)}")
    return resultado


# Instância global do detector
detector = DetectorApp()


# Teste direto
if __name__ == "__main__":
    tipos = mapear_tipos_apps_abertos()
    for t in tipos:
        print(f"[{t['tipo']:10}] {t['janela'][:40]:40} → {t['abordagem']}")
