"""acl browser * — высокоуровневые браузерные команды."""

from __future__ import annotations

import hashlib
import json as _json
import time
import webbrowser
from pathlib import Path
from typing import Optional
from urllib.parse import urlparse

import typer

from acl_cli import client, config
from acl_cli.output import console, error, info, json_output, success, table, warning, spinner

app = typer.Typer(help="Управление браузером", no_args_is_help=True)

def _session_dir() -> Path:
    return config.CONFIG_DIR


def _session_file(user: str, url: str) -> Path:
    """Return path to session file for user+domain pair."""
    domain = urlparse(url).netloc or urlparse(url).path
    safe = hashlib.md5(domain.encode()).hexdigest()[:8]  # noqa: S324
    return _session_dir() / f"session_{user}_{safe}"


def _get_or_create_session(user: str, url: str) -> str:
    """Read cached session_id or create a new session."""
    sf = _session_file(user, url)
    if sf.exists():
        sid = sf.read_text().strip()
        if sid:
            return sid

    info("Создаю новую сессию...")
    result = client.post(
        "/sessions",
        json={"user_id": user, "site_url": url},
        headers={"X-User-ID": user},
    )
    sid = result.get("session_id") or result.get("id", "")
    _session_dir().mkdir(parents=True, exist_ok=True)
    sf.write_text(sid)
    return sid


def _read_last_session(user: str) -> str:
    """Find any existing session file for this user, or raise."""
    sd = _session_dir()
    sd.mkdir(parents=True, exist_ok=True)
    for f in sorted(sd.glob(f"session_{user}_*"), key=lambda p: p.stat().st_mtime, reverse=True):
        sid = f.read_text().strip()
        if sid:
            return sid
    error("Нет активной сессии. Сначала выполните: acl browser open <url>")
    raise typer.Exit(1)


# ── 1. open ──────────────────────────────────────────────────────────────


@app.command("open")
def open_url(
    url: str = typer.Argument(..., help="URL для открытия"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
    wait: str = typer.Option("networkidle", "--wait", help="Стратегия ожидания"),
) -> None:
    """Открыть URL в браузере (создать сессию если нет)."""
    sid = _get_or_create_session(user, url)
    with spinner(f"Открываю {url}..."):
        result = client.post(
            "/browser/navigate",
            json={"session_id": sid, "url": url, "wait_for": wait},
            headers={"X-User-ID": user},
        )
    title = result.get("title", "")
    success(f"Открыто: {url}" + (f" ({title})" if title else ""))


# ── 2. extract ───────────────────────────────────────────────────────────


@app.command("extract")
def extract(
    selector: str = typer.Argument(..., help="CSS-селектор"),
    fields: Optional[str] = typer.Option(
        None, "--fields", "-f",
        help="Поля через запятую: name:.name,price:.price",
    ),
    limit: int = typer.Option(20, "--limit", "-l"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
    raw_json: bool = typer.Option(False, "--json", help="Сырой JSON"),
) -> None:
    """Извлечь данные со страницы по CSS-селектору."""
    sid = _read_last_session(user)
    payload: dict = {"session_id": sid, "selector": selector, "limit": limit}

    field_map: dict | None = None
    if fields:
        field_map = {}
        for pair in fields.split(","):
            k, v = pair.split(":", 1)
            field_map[k.strip()] = v.strip()
        payload["fields"] = field_map

    with spinner("Извлекаю данные..."):
        result = client.post(
            "/browser/extract",
            json=payload,
            headers={"X-User-ID": user},
        )

    if raw_json:
        json_output(result)
        return

    items = result.get("items") or result.get("results") or result.get("data") or result
    if isinstance(items, list) and field_map:
        table(items, columns=list(field_map.keys()))
    elif isinstance(items, list):
        for i, item in enumerate(items, 1):
            text = item.get("text", item) if isinstance(item, dict) else item
            console.print(f"  {i}. {text}")
    else:
        json_output(result)


# ── 3. fill ──────────────────────────────────────────────────────────────


@app.command("fill")
def fill(
    selector: str = typer.Argument(..., help="CSS-селектор поля"),
    value: str = typer.Argument(..., help="Значение для заполнения"),
    field_type: str = typer.Option("text", "--type", "-t", help="text|select|checkbox"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
) -> None:
    """Заполнить поле формы."""
    sid = _read_last_session(user)
    client.post(
        "/browser/fill",
        json={
            "session_id": sid,
            "fields": [{"selector": selector, "value": value, "type": field_type}],
        },
        headers={"X-User-ID": user},
    )
    success(f'Заполнено: {selector} = "{value}"')


# ── 4. click ─────────────────────────────────────────────────────────────


@app.command("click")
def click(
    selector: str = typer.Argument(..., help="CSS-селектор элемента"),
    wait: Optional[str] = typer.Option(None, "--wait", help="Стратегия ожидания после клика"),
    wait_for: Optional[str] = typer.Option(None, "--wait-for", help="Селектор для ожидания"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
) -> None:
    """Кликнуть по элементу."""
    sid = _read_last_session(user)
    payload: dict = {"session_id": sid, "selector": selector}
    if wait:
        payload["wait_after"] = wait
    if wait_for:
        payload["wait_for_selector"] = wait_for
    client.post(
        "/browser/click",
        json=payload,
        headers={"X-User-ID": user},
    )
    success(f"Нажато: {selector}")


# ── 5. screenshot ────────────────────────────────────────────────────────


@app.command("screenshot")
def screenshot(
    selector: Optional[str] = typer.Option(None, "--selector", "-s", help="CSS-селектор"),
    open_file: bool = typer.Option(False, "--open", help="Открыть в браузере"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
) -> None:
    """Сделать скриншот страницы или элемента."""
    sid = _read_last_session(user)
    with spinner("Делаю скриншот..."):
        result = client.post(
            "/browser/screenshot",
            json={"session_id": sid, "selector": selector, "format": "png"},
            headers={"X-User-ID": user},
        )

    screenshot_url = result.get("url") or result.get("screenshot_url", "")
    error_msg = result.get("error_msg")

    if error_msg:
        error(f"Скриншот не удался: {error_msg}")
        raise typer.Exit(1)

    ts = int(time.time())
    filename = f"screenshot_{ts}.png"

    if screenshot_url:
        import httpx

        # Relative URL from API → prepend base_url
        if screenshot_url.startswith("/"):
            screenshot_url = config.get_base_url() + "/browser" + screenshot_url

        resp = httpx.get(
            screenshot_url,
            headers={"Authorization": f"Bearer {config.require_api_key()}"},
            timeout=30.0,
        )
        Path(filename).write_bytes(resp.content)
        success(f"Скриншот сохранён: {filename}")
    else:
        warning("Сервер не вернул URL скриншота (возможно, хранилище недоступно)")
        success(f"Скриншот готов (размер: {result.get('width', '?')}x{result.get('height', '?')})")

    if open_file and Path(filename).exists():
        webbrowser.open(Path(filename).resolve().as_uri())


# ── 6. auth ──────────────────────────────────────────────────────────────


@app.command("auth")
def auth(
    site_url: str = typer.Argument(..., help="URL сайта для авторизации"),
    auth_type: str = typer.Option("login_password", "--type", "-t", help="login_password|qr"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
) -> None:
    """Авторизоваться на сайте."""
    sid = _get_or_create_session(user, site_url)

    if auth_type == "qr":
        result = client.post(
            "/browser/authenticate",
            json={
                "session_id": sid,
                "site_url": site_url,
                "auth_type": "qr",
                "user_id": user,
            },
            headers={"X-User-ID": user},
        )
        qr_url = result.get("qr_url", "")
        if qr_url:
            info(f"QR-код: {qr_url}")
            console.print("[dim]Ожидание сканирования...[/dim]")
            for _ in range(60):
                time.sleep(2)
                status = client.get(
                    f"/sessions/{sid}",
                    headers={"X-User-ID": user},
                )
                if status.get("authenticated"):
                    success("Авторизация через QR завершена")
                    return
            error("Таймаут ожидания QR-сканирования")
            raise typer.Exit(1)
        else:
            json_output(result)
    else:
        with spinner(f"Авторизация на {site_url}..."):
            client.post(
                "/browser/authenticate",
                json={
                    "session_id": sid,
                    "site_url": site_url,
                    "auth_type": "login_password",
                    "user_id": user,
                },
                headers={"X-User-ID": user},
            )
        success(f"Авторизация на {site_url} завершена")


# ── 7. upload ────────────────────────────────────────────────────────────


@app.command("upload")
def upload(
    file_path: str = typer.Argument(..., help="Путь к локальному файлу"),
    selector: str = typer.Argument(..., help="CSS-селектор input[type=file]"),
    user: str = typer.Option("default", "--user", "-u", help="X-User-ID"),
) -> None:
    """Загрузить локальный файл через input[type=file]."""
    sid = _read_last_session(user)

    local = Path(file_path)
    if not local.exists():
        error(f"Файл не найден: {file_path}")
        raise typer.Exit(1)

    import httpx

    with spinner(f"Загрузка {local.name}..."):
        with httpx.Client(
            base_url=config.get_base_url(),
            headers={"Authorization": f"Bearer {config.require_api_key()}"},
            timeout=60.0,
        ) as hc:
            resp = hc.post(
                "/files/upload",
                files={"file": (local.name, local.read_bytes())},
                headers={"X-User-ID": user},
            )
            resp.raise_for_status()
            file_url = resp.json().get("url", "")

        client.post(
            "/browser/upload",
            json={"session_id": sid, "selector": selector, "file_url": file_url},
            headers={"X-User-ID": user},
        )
    success(f"Файл {local.name} загружен")
