"""Tier 4: Persistent browser daemon without authentication.

Subclass this and implement _do_search() and _do_get().
Call make_app() to get the aiohttp Application, then web.run_app().

Example:
    class CordobaDaemon(BaseBrowserDaemon):
        KEEPALIVE_URL = "https://jurisprudencia.justiciacordoba.gob.ar"
        PORT = 8995

        async def _do_search(self, page, params):
            ...

        async def _do_get(self, page, id):
            ...
"""
from __future__ import annotations

import asyncio
import os
from abc import ABC, abstractmethod
from datetime import datetime, timezone
from pathlib import Path

from aiohttp import web

from juris_base.utils.browser import ensure_xvfb, get_live_page
from juris_base.utils.keepalive import make_keepalive_loop


class BaseBrowserDaemon(ABC):
    """Tier 4: stateful browser daemon, no login, behavioral CAPTCHA only."""

    KEEPALIVE_URL: str        # subclass must set
    PORT: int                 # subclass must set
    PROFILE_DIR: Path | None = None   # defaults to ~/.cache/{classname}-profile
    KEEPALIVE_INTERVAL: int = 300
    DISPLAY: str = ":99"

    def __init__(self):
        if self.PROFILE_DIR is None:
            self.PROFILE_DIR = Path.home() / ".cache" / f"{type(self).__name__.lower()}-profile"
        self._state: dict = {"pw": None, "browser": None, "page": None}
        self._page_lock = asyncio.Lock()

    async def _get_page(self):
        headed = bool(os.environ.get("DISPLAY") or ensure_xvfb(self.DISPLAY))
        return await get_live_page(self._state, self.PROFILE_DIR, headed=headed)

    @abstractmethod
    async def _do_search(self, page, params: dict) -> dict:
        """Perform site-specific search. Return {total, results}."""
        ...

    @abstractmethod
    async def _do_get(self, page, id: str) -> dict:
        """Fetch full document by ID."""
        ...

    # --- HTTP handlers ---

    async def handle_health(self, request):
        page = self._state.get("page")
        alive = False
        if page:
            try:
                await page.evaluate("1")
                alive = True
            except Exception:
                pass
        return web.json_response({"status": "ok" if alive else "no_browser", "port": self.PORT})

    async def handle_search(self, request):
        params = dict(request.query)
        async with self._page_lock:
            page = await self._get_page()
            try:
                result = await self._do_search(page, params)
                return web.json_response({**result, "timestamp": datetime.now(timezone.utc).isoformat()})
            except Exception as e:
                return web.json_response({"error": str(e)}, status=502)

    async def handle_get(self, request):
        id_ = request.query.get("id", "")
        if not id_:
            return web.json_response({"error": "id required"}, status=400)
        async with self._page_lock:
            page = await self._get_page()
            try:
                result = await self._do_get(page, id_)
                return web.json_response({**result, "timestamp": datetime.now(timezone.utc).isoformat()})
            except Exception as e:
                return web.json_response({"error": str(e)}, status=502)

    # --- Lifecycle ---

    def make_app(self) -> web.Application:
        app = web.Application()
        app.router.add_get("/health", self.handle_health)
        app.router.add_get("/search", self.handle_search)
        app.router.add_get("/get", self.handle_get)

        keepalive = make_keepalive_loop(
            get_page_fn=self._get_page,
            page_lock=self._page_lock,
            keepalive_url=self.KEEPALIVE_URL,
            interval=self.KEEPALIVE_INTERVAL,
        )

        async def on_startup(app):
            app["keepalive"] = asyncio.create_task(keepalive())

        async def on_cleanup(app):
            app["keepalive"].cancel()
            try:
                if self._state["browser"]:
                    await self._state["browser"].close()
            except Exception:
                pass
            try:
                if self._state["pw"]:
                    await self._state["pw"].stop()
            except Exception:
                pass

        app.on_startup.append(on_startup)
        app.on_cleanup.append(on_cleanup)
        return app

    def main(self):
        app = self.make_app()
        print(f"{type(self).__name__} daemon starting on port {self.PORT}", flush=True)
        web.run_app(app, host="127.0.0.1", port=self.PORT, print=None)
