import asyncio
import os
import signal
import subprocess
import json
from typing import Optional
import aiohttp


class Navegador:
    def __init__(self,
                 chrome_path: str,
                 user_data_dir: str,
                 id: Optional[int] = None,
                 download_dir: Optional[str] = None,
                 debugging_port: int = 9222,
                 headless: bool = False,
                 user_agent: Optional[str] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"):
        self.chrome_path = chrome_path
        self.user_data_dir = user_data_dir
        self.id = id
        self.download_dir = download_dir or os.path.join(self.user_data_dir, "downloads")
        self.debugging_port = debugging_port
        self.headless = headless
        self.user_agent = user_agent
        self.chrome_process: Optional[subprocess.Popen] = None

    async def _esperar_debugger(self, timeout=10):
        url = f"http://127.0.0.1:{self.debugging_port}/json"
        for _ in range(timeout * 10):  # 10 intentos por segundo
            try:
                async with aiohttp.ClientSession() as session:
                    async with session.get(url) as resp:
                        if resp.status == 200:
                            print("✅ Chrome listo para debugging.")
                            return
            except Exception:
                pass
            await asyncio.sleep(0.1)
        raise RuntimeError("❌ Chrome no respondió en el puerto de debugging.")

    def _preconfigurar_preferencias(self):
        prefs_path = os.path.join(self.user_data_dir, "Default", "Preferences")
        os.makedirs(os.path.dirname(prefs_path), exist_ok=True)
        os.makedirs(self.download_dir, exist_ok=True)

        prefs = {
            "profile": {
                "exit_type": "Normal",
                "exited_cleanly": True
            },
            "browser": {
                "has_seen_welcome_page": True
            },
            "distribution": {
                "skip_first_run_ui": True
            },
            "download": {
                "default_directory": self.download_dir,
                "prompt_for_download": False,
                "directory_upgrade": True,
                "extensions_to_open": ""
            },
            "savefile": {
                "default_directory": self.download_dir
            }
        }

        if os.path.exists(prefs_path):
            try:
                with open(prefs_path, "r", encoding="utf-8") as f:
                    existing = json.load(f)
                existing.update(prefs)
                prefs = existing
            except Exception:
                pass

        with open(prefs_path, "w", encoding="utf-8") as f:
            json.dump(prefs, f, indent=2)

    def _build_args(self):
        os.makedirs(self.user_data_dir, exist_ok=True)
        self._preconfigurar_preferencias()

        args = [
            f"--remote-debugging-port={self.debugging_port}",
            f"--user-data-dir={self.user_data_dir}",
            "--disable-blink-features=AutomationControlled",
            "--no-sandbox",
            # "--disable-web-security",
            # "--disable-extensions",
            # "--disable-dev-shm-usage",
            "--disable-infobars",
            "--disable-popup-blocking",
            "--disable-default-apps",
            "--mute-audio",
            "--window-size=1024,1024",
            "--no-first-run",
            "--no-default-browser-check",
            "--disable-features=DefaultBrowserPrompt",
            "--disable-component-update",
            "--disable-background-networking",
            "--disable-sync",
            "--disable-translate",
            "--disable-background-timer-throttling",
            "--disable-client-side-phishing-detection",
            "--disable-component-extensions-with-background-pages",
            "--metrics-recording-only",
            "--safebrowsing-disable-auto-update",


        ]

        if self.headless:
            args.append("--headless=new")

        if self.user_agent:
            args.append(f"--user-agent={self.user_agent}")

        return args
    


    async def inyectar_spoof_chrome(self):
        script = """
        window.chrome = {
            app: {
                isInstalled: false,
                InstallState: {
                    DISABLED: 'disabled',
                    INSTALLED: 'installed',
                    NOT_INSTALLED: 'not_installed'
                },
                RunningState: {
                    CANNOT_RUN: 'cannot_run',
                    READY_TO_RUN: 'ready_to_run',
                    RUNNING: 'running'
                }
            },
            runtime: {
                PlatformOs: { MAC: 'mac', WIN: 'win', ANDROID: 'android', CROS: 'cros', LINUX: 'linux', OPENBSD: 'openbsd' },
                PlatformArch: { ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64' },
                PlatformNaclArch: { ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64' },
                RequestUpdateCheckStatus: { THROTTLED: 'throttled', NO_UPDATE: 'no_update', UPDATE_AVAILABLE: 'update_available' },
                OnInstalledReason: { INSTALL: 'install', UPDATE: 'update', CHROME_UPDATE: 'chrome_update', SHARED_MODULE_UPDATE: 'shared_module_update' },
                OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' }
            }
        };
        """

        url = f"http://127.0.0.1:{self.debugging_port}/json"
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as resp:
                targets = await resp.json()

            for target in targets:
                if "webSocketDebuggerUrl" not in target:
                    continue

                target_id = target["id"]
                async with session.post(
                    f"http://127.0.0.1:{self.debugging_port}/json/protocol",
                    json={"targetId": target_id}
                ):
                    pass  # CDP protocol fetch optional

                async with session.post(
                    f"http://127.0.0.1:{self.debugging_port}/json/send",
                    json={
                        "id": 1,
                        "method": "Page.addScriptToEvaluateOnNewDocument",
                        "params": {"source": script}
                    }
                ) as inject_resp:
                    if inject_resp.status == 200:
                        print("✅ chrome.* spoof inyectado.")


    async def iniciar(self):
        args = self._build_args()
        self.chrome_process = subprocess.Popen([self.chrome_path] + args)
        print(f"Chrome iniciado (headless={self.headless}). Esperando disponibilidad del debugger...")
        await self._esperar_debugger()
        await self.inyectar_spoof_chrome()

    async def cerrar(self):
        if self.chrome_process and self.chrome_process.poll() is None:
            self.chrome_process.terminate()
            try:
                await asyncio.wait_for(asyncio.to_thread(self.chrome_process.wait), timeout=5)
            except asyncio.TimeoutError:
                self.chrome_process.kill()
            print("🛑 Chrome cerrado correctamente.")
