import os
import sys
import time
import socket
import json
import subprocess
import platform

CLI_DIR = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity-cli')
ACTIVE_TASKS_FILE = os.path.join(CLI_DIR, 'cache', 'active_tasks.json')
LOG_FILE = os.path.join(CLI_DIR, 'cache', 'watchdog_system.log')

def log(msg):
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S')
    line = f"[{timestamp}] {msg}\n"
    try:
        print(msg)
    except Exception:
        pass
    try:
        os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True)
        with open(LOG_FILE, 'a', encoding='utf-8') as f:
            f.write(line)
    except Exception:
        pass

# Garantir instância única do watchdog
_lock_socket = None
def ensure_single_watchdog_instance():
    global _lock_socket
    try:
        _lock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Porta 55678 para o watchdog (o principal usa 55677)
        _lock_socket.bind(('127.0.0.1', 55678))
        log("[*] Instancia unica do watchdog garantida na porta 55678.")
    except socket.error as e:
        log(f"[-] Erro ao garantir instancia unica (porta ocupada?): {e}")
        sys.exit(0)

def is_pid_agy_running(pid):
    if not pid:
        return False
    if platform.system() == "Windows":
        try:
            # Usa Win32 API via ctypes para evitar abrir subprocessos e prevenir crashes sob pythonw.exe
            import ctypes
            PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
            kernel32 = ctypes.windll.kernel32
            handle = kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, int(pid))
            if handle:
                exit_code = ctypes.c_ulong()
                kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code))
                kernel32.CloseHandle(handle)
                # STILL_ACTIVE = 259
                is_active = (exit_code.value == 259)
                return is_active
            return False
        except Exception as e:
            log(f"[-] Erro ao verificar PID {pid} via ctypes: {e}")
            return False
    else:
        try:
            res = subprocess.run(["ps", "-p", str(pid), "-o", "comm="], capture_output=True, text=True, stdin=subprocess.DEVNULL)
            out = res.stdout.lower()
            # agy (Antigravity), claude (binário nativo) ou node (claude via npm)
            return any(name in out for name in ("agy", "claude", "codex", "node"))
        except Exception as e:
            log(f"[-] Erro ao verificar PID {pid} via ps: {e}")
            return False

def is_main_agent_running():
    # Verifica se a porta 55677 está ocupada
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.bind(('127.0.0.1', 55677))
        s.close()
        # Se conseguimos fazer bind, a porta estava livre, logo o principal NÃO está rodando
        return False
    except socket.error:
        # Se não conseguimos fazer bind, a porta está ocupada (ou seja, o principal provavelmente está rodando)
        return True

def get_active_tasks():
    if os.path.exists(ACTIVE_TASKS_FILE):
        try:
            with open(ACTIVE_TASKS_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            log(f"[-] Erro ao carregar active_tasks: {e}")
    return {}

def start_main_agent():
    log("[*] Iniciando o agente principal em segundo plano...")
    install_dir = os.path.dirname(os.path.abspath(__file__))
    agent_py_path = os.path.join(install_dir, "agent.py")
    
    if not os.path.exists(agent_py_path):
        log(f"[-] Erro: agent.py nao encontrado em {agent_py_path}")
        return False
        
    try:
        if platform.system() == "Windows":
            pythonw_path = sys.executable.lower().replace("python.exe", "pythonw.exe")
            # DETACHED_PROCESS (0x00000008) + CREATE_NEW_PROCESS_GROUP (0x00000200)
            flags = 0x00000008 | 0x00000200
            subprocess.Popen([pythonw_path, agent_py_path], cwd=install_dir, close_fds=True, stdin=subprocess.DEVNULL, creationflags=flags)
        else:
            with open(os.devnull, 'r+') as devnull:
                subprocess.Popen([sys.executable, agent_py_path, '--headless'], cwd=install_dir, stdin=devnull, stdout=devnull, stderr=devnull, start_new_session=True)
        log("[+] Agente principal reiniciado com sucesso!")
        return True
    except Exception as e:
        log(f"[-] Erro ao reiniciar o agente principal: {e}")
        return False

def main():
    try:
        ensure_single_watchdog_instance()
        log("====================================================")
        log("      Antigravity Watchdog Agent (Shadow Mode)")
        log("====================================================")
        
        # Loop de monitoramento
        while True:
            try:
                # 1. Verifica se o agente principal está rodando
                if is_main_agent_running():
                    # Se estiver rodando, tudo OK
                    time.sleep(5)
                    continue
                    
                # 2. O principal foi fechado.
                # Vamos aguardar o processamento de todas as conversas ativas.
                log("[!] Agente principal nao esta rodando. Verificando conversas ativas...")
                
                while True:
                    active_tasks = get_active_tasks()
                    running_tasks_count = 0
                    
                    for client_conv_id, t_info in list(active_tasks.items()):
                        pid = t_info.get("pid")
                        if is_pid_agy_running(pid):
                            running_tasks_count += 1
                    
                    if running_tasks_count > 0:
                        log(f"[*] Aguardando {running_tasks_count} conversas ativas finalizarem...")
                        time.sleep(3)
                    else:
                        break
                
                # 3. Verifica novamente se o principal não reabriu nesse meio tempo
                if is_main_agent_running():
                    log("[*] Agente principal voltou a rodar de forma independente. Retornando ao monitoramento.")
                    time.sleep(5)
                    continue
                    
                # 4. Todas as conversas ativas finalizaram (ou não havia nenhuma).
                # Agora ativamos o script principal novamente!
                start_main_agent()
                
                # Aguarda 15 segundos para dar tempo do principal inicializar e travar a porta 55677
                time.sleep(15)
                
            except Exception as e:
                log(f"[-] Erro no loop do watchdog: {e}")
                time.sleep(5)
    except Exception as e:
        log(f"[-] Erro fatal no watchdog: {e}")

if __name__ == '__main__':
    main()
