"""
security_manager.py
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cobre a camada:
  ✅ Camada 6 — Permissões e contexto de segurança

Gerencia:
  • Verificação e elevação para administrador (UAC)
  • Detecção de antivírus e possíveis bloqueios
  • Políticas de grupo (Group Policy) que podem restringir automação
  • Verificação de integridade de processo (apps protegidos)
  • Acesso a apps rodando como admin a partir de processo normal
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"""

import os
import sys
import ctypes
import winreg
import psutil
import subprocess
from typing import Optional
from dataclasses import dataclass, field
from loguru import logger


# ══════════════════════════════════════════════════════
# ESTRUTURA DE RESULTADO DE SEGURANÇA
# ══════════════════════════════════════════════════════

@dataclass
class StatusSeguranca:
    """Resultado completo da verificação de segurança."""
    e_admin:                bool
    uac_ativo:              bool
    pode_elevar:            bool
    antivirus_detectado:    list
    politicas_restritivas:  list
    apps_protegidos:        list
    nivel_integridade:      str   # Low, Medium, High, System
    recomendacoes:          list = field(default_factory=list)
    alertas:                list = field(default_factory=list)


# ══════════════════════════════════════════════════════
# VERIFICAÇÃO DE ADMINISTRADOR
# ══════════════════════════════════════════════════════

def e_administrador() -> bool:
    """Verifica se o processo atual está rodando como administrador."""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
    except Exception:
        return False


def obter_nivel_integridade() -> str:
    """
    Retorna o nível de integridade do processo atual.
    Valores: Low, Medium, High (admin), System
    """
    try:
        import ctypes.wintypes

        TOKEN_QUERY            = 0x0008
        TokenIntegrityLevel    = 25

        token = ctypes.wintypes.HANDLE()
        proc  = ctypes.windll.kernel32.GetCurrentProcess()

        if not ctypes.windll.advapi32.OpenProcessToken(
            proc, TOKEN_QUERY, ctypes.byref(token)
        ):
            return "Desconhecido"

        # Obter tamanho necessário
        tam = ctypes.wintypes.DWORD()
        ctypes.windll.advapi32.GetTokenInformation(
            token, TokenIntegrityLevel, None, 0, ctypes.byref(tam)
        )

        buf = (ctypes.c_byte * tam.value)()
        if not ctypes.windll.advapi32.GetTokenInformation(
            token, TokenIntegrityLevel, buf, tam, ctypes.byref(tam)
        ):
            return "Desconhecido"

        # O nível fica nos últimos 2 bytes do SID
        nivel_rid = ctypes.c_ulong.from_buffer(buf, tam.value - 4).value

        niveis = {
            0x1000: "Low",
            0x2000: "Medium",
            0x3000: "High",
            0x4000: "System",
        }
        return niveis.get(nivel_rid, f"Desconhecido ({nivel_rid:#x})")

    except Exception as e:
        logger.debug(f"Erro ao obter nível de integridade: {e}")
        return "Desconhecido"


def elevar_para_admin(reiniciar: bool = True) -> bool:
    """
    Solicita elevação de privilégios via UAC.
    Se reiniciar=True, reinicia o processo com permissões de admin.
    """
    if e_administrador():
        logger.info("Processo já está rodando como administrador")
        return True

    try:
        logger.info("Solicitando elevação de privilégios (UAC)...")
        resultado = ctypes.windll.shell32.ShellExecuteW(
            None,
            "runas",          # Solicita "Run as administrator"
            sys.executable,
            " ".join(sys.argv),
            None,
            1
        )

        if resultado > 32:
            logger.success("Elevação solicitada com sucesso")
            if reiniciar:
                sys.exit(0)  # Encerrar processo atual (novo foi criado elevado)
            return True
        else:
            logger.warning(f"Elevação recusada ou falhou (código: {resultado})")
            return False

    except Exception as e:
        logger.error(f"Erro ao elevar privilégios: {e}")
        return False


# ══════════════════════════════════════════════════════
# VERIFICAÇÃO DO UAC
# ══════════════════════════════════════════════════════

def uac_esta_ativo() -> bool:
    """Verifica se o UAC (Controle de Conta de Usuário) está habilitado."""
    try:
        chave = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
        )
        valor, _ = winreg.QueryValueEx(chave, "EnableLUA")
        return bool(valor)
    except Exception:
        return True  # Assumir ativo por segurança


def obter_nivel_uac() -> str:
    """
    Retorna o nível de notificação do UAC configurado.
    0 = Nunca notificar, 1 = Notificar apps, 2 = Sempre notificar
    """
    try:
        chave  = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
        )
        valor, _ = winreg.QueryValueEx(chave, "ConsentPromptBehaviorAdmin")
        niveis = {
            0: "Nunca notificar (desativado)",
            1: "Notificar apenas para apps",
            2: "Notificar sempre (padrão)",
            5: "Notificar apenas para apps (padrão recomendado)",
        }
        return niveis.get(valor, f"Nível {valor}")
    except Exception:
        return "Padrão"


# ══════════════════════════════════════════════════════
# DETECÇÃO DE ANTIVÍRUS
# ══════════════════════════════════════════════════════

def detectar_antivirus() -> list:
    """
    Detecta antivírus instalados via WMI e registro.
    Antivírus podem bloquear automação — importante saber quais estão ativos.
    """
    encontrados = []

    # Via WMI (método mais confiável)
    try:
        resultado = subprocess.run(
            [
                "powershell", "-Command",
                "Get-CimInstance -Namespace root/SecurityCenter2 "
                "-ClassName AntivirusProduct | "
                "Select-Object displayName, productState | ConvertTo-Json"
            ],
            capture_output=True, text=True, timeout=10
        )

        if resultado.returncode == 0 and resultado.stdout.strip():
            import json
            dados = json.loads(resultado.stdout)
            if isinstance(dados, dict):
                dados = [dados]
            for av in dados:
                encontrados.append({
                    "nome":   av.get("displayName", "Desconhecido"),
                    "estado": av.get("productState", 0),
                    "ativo":  True
                })
    except Exception as e:
        logger.debug(f"WMI antivírus: {e}")

    # Fallback: verificar processos comuns de antivírus
    if not encontrados:
        av_processos = {
            "MsMpEng.exe":    "Windows Defender",
            "avguard.exe":    "Avira",
            "avgnt.exe":      "Avira",
            "avp.exe":        "Kaspersky",
            "ekrn.exe":       "ESET",
            "bdagent.exe":    "Bitdefender",
            "mcshield.exe":   "McAfee",
            "savservice.exe": "Sophos",
            "mbam.exe":       "Malwarebytes",
            "csc.exe":        "Comodo",
        }
        for proc in psutil.process_iter(["name"]):
            try:
                nome = proc.info["name"]
                if nome in av_processos:
                    encontrados.append({
                        "nome":   av_processos[nome],
                        "estado": "processo_ativo",
                        "ativo":  True
                    })
            except:
                continue

    logger.debug(f"Antivírus detectados: {[av['nome'] for av in encontrados]}")
    return encontrados


# ══════════════════════════════════════════════════════
# POLÍTICAS DE GRUPO
# ══════════════════════════════════════════════════════

def verificar_politicas_restritivas() -> list:
    """
    Verifica políticas de grupo (Group Policy) que podem bloquear automação.
    Comum em ambientes corporativos.
    """
    restricoes = []

    politicas_verificar = [
        (
            r"SOFTWARE\Policies\Microsoft\Windows\Installer",
            "DisableMSI",
            "Instalação de MSI bloqueada por política"
        ),
        (
            r"SOFTWARE\Policies\Microsoft\Windows\System",
            "DisableCMD",
            "Prompt de comando bloqueado por política"
        ),
        (
            r"SOFTWARE\Policies\Microsoft\Windows\PowerShell",
            "EnableScripts",
            "PowerShell scripts bloqueados por política"
        ),
        (
            r"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer",
            "NoRun",
            "Diálogo 'Executar' bloqueado por política"
        ),
        (
            r"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System",
            "DisableRegistryTools",
            "Editor de registro bloqueado por política"
        ),
    ]

    for chave_path, valor_nome, descricao in politicas_verificar:
        try:
            chave = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, chave_path)
            valor, _ = winreg.QueryValueEx(chave, valor_nome)
            if valor:
                restricoes.append({
                    "politica":  valor_nome,
                    "descricao": descricao,
                    "valor":     valor
                })
        except:
            continue  # Política não configurada = sem restrição

    logger.debug(f"Políticas restritivas encontradas: {len(restricoes)}")
    return restricoes


# ══════════════════════════════════════════════════════
# APPS PROTEGIDOS (RODANDO COMO ADMIN)
# ══════════════════════════════════════════════════════

def detectar_apps_protegidos() -> list:
    """
    Detecta apps que estão rodando com privilégios elevados (admin).
    Para automatizá-los, o agente também precisa rodar como admin.
    """
    protegidos = []

    for proc in psutil.process_iter(["pid", "name", "exe"]):
        try:
            pid  = proc.info["pid"]
            nome = proc.info["name"]

            # Tentar abrir o processo com direitos altos
            PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
            handle = ctypes.windll.kernel32.OpenProcess(
                PROCESS_QUERY_LIMITED_INFORMATION, False, pid
            )

            if not handle:
                # Processo inacessível = provavelmente admin/sistema
                if nome and nome not in ["System", "Idle", "smss.exe",
                                          "csrss.exe", "wininit.exe"]:
                    protegidos.append({
                        "pid":  pid,
                        "nome": nome,
                        "exe":  proc.info.get("exe") or "",
                        "motivo": "Acesso negado — provável processo elevado"
                    })
            else:
                ctypes.windll.kernel32.CloseHandle(handle)

        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue
        except Exception:
            continue

    logger.debug(f"Apps protegidos detectados: {len(protegidos)}")
    return protegidos


# ══════════════════════════════════════════════════════
# VERIFICAÇÃO COMPLETA DE SEGURANÇA
# ══════════════════════════════════════════════════════

def verificar_seguranca_completa() -> StatusSeguranca:
    """
    Executa todas as verificações de segurança e retorna um relatório completo.
    """
    logger.info("━━━ Verificando contexto de segurança ━━━")

    admin            = e_administrador()
    uac_ativo        = uac_esta_ativo()
    nivel            = obter_nivel_integridade()
    antivirus        = detectar_antivirus()
    politicas        = verificar_politicas_restritivas()
    apps_protegidos  = detectar_apps_protegidos()

    recomendacoes = []
    alertas       = []

    # Gerar recomendações e alertas
    if not admin:
        alertas.append(
            "⚠️  Agente NÃO está rodando como administrador — "
            "alguns apps não poderão ser automatizados"
        )
        recomendacoes.append(
            "Execute 'seu-agente start' como administrador para acesso completo"
        )

    if antivirus:
        nomes_av = [av["nome"] for av in antivirus]
        alertas.append(
            f"🛡️  Antivírus detectado: {', '.join(nomes_av)} — "
            "pode bloquear automação ou enviar alertas"
        )
        recomendacoes.append(
            "Adicione o agente à lista de exceções do antivírus para evitar bloqueios"
        )

    if politicas:
        for pol in politicas:
            alertas.append(
                f"🔒 Política restritiva: {pol['descricao']}"
            )

    if apps_protegidos and not admin:
        alertas.append(
            f"🔐 {len(apps_protegidos)} apps protegidos detectados — "
            "requerem agente rodando como admin para automação"
        )

    status = StatusSeguranca(
        e_admin               = admin,
        uac_ativo             = uac_ativo,
        pode_elevar           = uac_ativo and not admin,
        antivirus_detectado   = antivirus,
        politicas_restritivas = politicas,
        apps_protegidos       = apps_protegidos,
        nivel_integridade     = nivel,
        recomendacoes         = recomendacoes,
        alertas               = alertas
    )

    logger.info(
        f"Segurança — Admin: {admin} | UAC: {uac_ativo} | "
        f"AV: {len(antivirus)} | Alertas: {len(alertas)}"
    )

    return status


def verificar_pode_automatizar_app(nome_processo: str) -> dict:
    """
    Verifica se o agente tem permissão suficiente para automatizar um app específico.
    """
    agente_admin = e_administrador()

    for proc in psutil.process_iter(["pid", "name"]):
        try:
            if nome_processo.lower() in proc.info["name"].lower():
                pid = proc.info["pid"]

                # Tentar acesso ao processo
                handle = ctypes.windll.kernel32.OpenProcess(
                    0x1000, False, pid
                )

                if handle:
                    ctypes.windll.kernel32.CloseHandle(handle)
                    return {
                        "pode_automatizar": True,
                        "requer_admin":     False,
                        "motivo":           "Acesso liberado"
                    }
                else:
                    return {
                        "pode_automatizar": agente_admin,
                        "requer_admin":     True,
                        "motivo": (
                            "App protegido — agente já é admin, deve funcionar"
                            if agente_admin else
                            "App protegido — reinicie o agente como administrador"
                        )
                    }
        except:
            continue

    return {
        "pode_automatizar": True,
        "requer_admin":     False,
        "motivo":           "App não encontrado em execução"
    }


# Teste direto
if __name__ == "__main__":
    status = verificar_seguranca_completa()
    print(f"\nAdmin:       {status.e_admin}")
    print(f"UAC ativo:   {status.uac_ativo}")
    print(f"Integridade: {status.nivel_integridade}")
    print(f"Antivírus:   {[av['nome'] for av in status.antivirus_detectado]}")
    print(f"\nAlertas:")
    for a in status.alertas:
        print(f"  {a}")
    print(f"\nRecomendações:")
    for r in status.recomendacoes:
        print(f"  → {r}")
