"""Browser automation engine — wraps browser-use as a sub-agent.

Creates a browser-use Agent with the user's configured LLM, runs it against
a website task, and returns structured results. The browser lives and dies
within a single run_browser_task() call — no session persistence.
"""

import base64
import os
import signal
import time
from pathlib import Path

# Disable browser-use telemetry (PostHog version mismatch causes errors)
os.environ.setdefault("ANONYMIZED_TELEMETRY", "false")
# Suppress noisy === separator lines from browser-use
os.environ.setdefault("BROWSER_USE_LOGGING_LEVEL", "result")

import structlog

log = structlog.get_logger()


def _create_browser_llm() -> object:
    """Create a LangChain-compatible Chat model from Fliiq's LLM config.

    Reads the same env vars as the main agent (ANTHROPIC_API_KEY, etc.)
    and creates the corresponding Chat model that browser-use expects.
    """
    from fliiq.runtime.agent.setup import resolve_llm_config

    config = resolve_llm_config()

    if config.provider == "anthropic":
        from langchain_anthropic import ChatAnthropic

        kwargs = {"model": config.model or "claude-sonnet-4-5-20250929", "api_key": config.api_key}
        if config.base_url:
            kwargs["base_url"] = config.base_url
        return ChatAnthropic(**kwargs)

    elif config.provider == "openai":
        from langchain_openai import ChatOpenAI

        kwargs = {"model": config.model or "gpt-4o", "api_key": config.api_key}
        if config.base_url:
            kwargs["base_url"] = config.base_url
        return ChatOpenAI(**kwargs)

    elif config.provider == "gemini":
        from langchain_google_genai import ChatGoogleGenerativeAI

        return ChatGoogleGenerativeAI(
            model=config.model or "gemini-2.0-flash",
            api_key=config.api_key,
        )

    raise ValueError(f"Unsupported LLM provider for web navigation: {config.provider}")


def _build_controller(task_dir: Path):
    """Create a browser-use Controller with custom Fliiq actions."""
    from browser_use import Controller

    controller = Controller()

    @controller.action(
        "Pause and ask user for confirmation before submitting a form, making a payment, "
        "creating an account, or taking any irreversible action. You MUST call this before "
        "clicking any submit/confirm/pay/book button."
    )
    async def request_confirmation(action_summary: str, filled_fields: str) -> str:
        """Present checkpoint to user, wait for approval."""
        import asyncio

        from rich.console import Console
        from rich.panel import Panel
        from rich.prompt import Prompt

        console = Console()
        console.print()
        console.print(Panel(
            f"[bold]Action:[/bold] {action_summary}\n\n"
            f"[bold]Details:[/bold]\n{filled_fields}",
            title="Web Task Checkpoint",
            border_style="yellow",
        ))

        answer = await asyncio.to_thread(
            Prompt.ask,
            "Proceed?",
            choices=["yes", "no"],
            default="yes",
        )

        if answer == "no":
            return "User cancelled. Stop the task immediately and report what was done so far."
        return "User approved. Proceed with the action."

    return controller


def _task_screenshots_dir() -> Path:
    """Return a unique task directory for storing screenshots."""
    task_dir = Path.home() / ".fliiq" / "browser_tasks" / str(int(time.time()))
    task_dir.mkdir(parents=True, exist_ok=True)
    return task_dir


def _save_screenshots(history, task_dir: Path) -> list[str]:
    """Save base64 screenshots from browser-use history to disk as PNGs."""
    saved = []
    for i, screenshot_b64 in enumerate(history.screenshots() or []):
        if not screenshot_b64:
            continue
        dest = task_dir / f"step_{i}.png"
        try:
            dest.write_bytes(base64.b64decode(screenshot_b64))
            saved.append(str(dest))
        except Exception:
            pass
    return saved


async def run_browser_task(
    task: str,
    url: str | None = None,
    browser_visible: bool = False,
    max_steps: int = 50,
) -> dict:
    """Run a browser-use agent to complete a web task.

    Args:
        task: Natural language task description.
        url: Optional starting URL.
        browser_visible: Show browser window (False = headless).
        max_steps: Max browser actions before stopping.

    Returns:
        Dict with success, result, screenshots, urls_visited, steps_taken, error.
    """
    from browser_use import Agent, Browser, BrowserConfig

    task_dir = _task_screenshots_dir()

    # Build task prompt with confirmation instruction
    task_prompt = task
    if url:
        task_prompt = f"Navigate to {url} and then: {task}"
    task_prompt += (
        "\n\nIMPORTANT: Before submitting any form, making any payment, "
        "creating any account, or taking any irreversible action, you MUST "
        "call request_confirmation with a summary of what you're about to do "
        "and the details of all filled fields. Do NOT guess or fabricate any "
        "information not provided in the task description."
    )

    llm = _create_browser_llm()
    browser = Browser(config=BrowserConfig(headless=not browser_visible))
    controller = _build_controller(task_dir)

    try:
        import asyncio

        from rich.console import Console
        from rich.live import Live
        from rich.spinner import Spinner

        console = Console()
        live_display = None
        browse_start = time.time()
        step_text = "initializing..."

        def _on_step(browser_state, agent_output, step_num):
            nonlocal step_text
            goal = ""
            if agent_output and agent_output.current_state:
                goal = agent_output.current_state.next_goal
            step_text = f"{goal} (step {step_num})" if goal else f"step {step_num}"

        async def _update_elapsed():
            while True:
                await asyncio.sleep(1)
                elapsed = int(time.time() - browse_start)
                if live_display:
                    live_display.update(Spinner("dots", text=f"Browsing... {elapsed}s\n  {step_text}"))

        agent = Agent(
            task=task_prompt,
            llm=llm,
            browser=browser,
            controller=controller,
            max_actions_per_step=5,
            enable_memory=False,
            register_new_step_callback=_on_step,
        )

        log.info("browser_task_started", url=url, visible=browser_visible, max_steps=max_steps)

        original_handler = signal.getsignal(signal.SIGINT)
        with Live(Spinner("dots", text="Browsing...\n  initializing..."), console=console, refresh_per_second=4) as live:
            live_display = live
            elapsed_task = asyncio.create_task(_update_elapsed())
            try:
                history = await agent.run(max_steps=max_steps)
            finally:
                elapsed_task.cancel()
                try:
                    await elapsed_task
                except asyncio.CancelledError:
                    pass
                signal.signal(signal.SIGINT, original_handler)
                live_display = None

        # Save screenshots (base64 → PNG files)
        screenshots = _save_screenshots(history, task_dir)

        result = {
            "success": history.is_done(),
            "result": history.final_result() or "",
            "screenshots": screenshots,
            "urls_visited": history.urls() or [],
            "steps_taken": history.number_of_steps(),
            "error": None,
        }
        log.info("browser_task_completed", success=result["success"], steps=result["steps_taken"])
        return result

    except Exception as e:
        log.error("browser_task_failed", error=str(e))
        return {
            "success": False,
            "result": "",
            "screenshots": [],
            "urls_visited": [],
            "steps_taken": 0,
            "error": str(e),
        }
    finally:
        await browser.close()
