"""CLI commands for web pages workflows."""

from __future__ import annotations

import os
from pathlib import Path
from typing import Any, Callable

import typer

from worai.core.config_resolver import load_profile_settings, resolve_ingest_runtime_settings, resolve_value
from worai.core.output_template import render_output_path_template
from worai.core.web_pages import WebPagesClassifyTypesOptions, run_classify_types

app = typer.Typer(add_completion=False, no_args_is_help=True)
try:
    from tqdm import tqdm
except Exception:  # pragma: no cover - tqdm is expected transitively
    tqdm = None  # type: ignore[assignment]


def _resolve_bool_value(
    value: bool | None,
    profile_settings: dict[str, object],
    path: str,
) -> bool | None:
    if value is not None:
        return value
    raw = resolve_value(None, profile_settings, paths=[path])
    if raw is None:
        return None
    if isinstance(raw, bool):
        return raw
    token = str(raw).strip().lower()
    if token in {"1", "true", "yes", "on"}:
        return True
    if token in {"0", "false", "no", "off"}:
        return False
    raise typer.BadParameter(f"Invalid boolean value for {path}: {raw}")


def _progress_enabled() -> bool:
    if os.getenv("WORAI_PROGRESS", "").strip().lower() in {"1", "true", "yes", "on"}:
        return True
    if os.getenv("WORAI_NO_PROGRESS", "").strip().lower() in {"1", "true", "yes", "on"}:
        return False
    if os.getenv("CI", "").strip().lower() in {"1", "true", "yes"}:
        return False
    return True


def _build_classify_progress_callback(
    *,
    enabled: bool | None = None,
    progress_factory: Callable[..., Any] | None = None,
) -> tuple[Callable[[dict[str, Any]], None], Callable[[], None]]:
    is_enabled = _progress_enabled() if enabled is None else enabled
    bar: Any | None = None
    last_completed = 0
    factory = progress_factory or tqdm

    def _close() -> None:
        nonlocal bar
        if bar is not None:
            bar.close()
            bar = None

    def _on_progress(event: dict[str, Any]) -> None:
        nonlocal bar, last_completed
        if not isinstance(event, dict):
            return

        event_name = str(event.get("event", ""))
        meta = event.get("meta")
        if not isinstance(meta, dict):
            meta = {}

        if event_name == "type_classification.progress.updated" and str(meta.get("status", "")).lower() == "error":
            url = str(meta.get("url", "") or "-")
            error_type = str(meta.get("error_type", "") or "-")
            error_message = str(meta.get("error_message", "") or "-")
            typer.echo(
                (
                    f"Type classification failed "
                    f"url={url} error_type={error_type} error_message={error_message}"
                ),
                err=True,
            )

        if not is_enabled or factory is None:
            return

        if event_name == "type_classification.progress.started":
            total = int(meta.get("total", 0) or 0)
            _close()
            bar = factory(total=max(total, 0), desc="Classify types", unit="url", leave=True)
            last_completed = 0
            return

        if event_name == "type_classification.progress.updated":
            completed = int(meta.get("completed", 0) or 0)
            total = int(meta.get("total", completed) or completed)
            if bar is None:
                bar = factory(total=max(total, 0), desc="Classify types", unit="url", leave=True)
                last_completed = 0
            if hasattr(bar, "total"):
                bar.total = max(total, 0)
                if hasattr(bar, "refresh"):
                    bar.refresh()
            if completed > last_completed:
                bar.update(completed - last_completed)
                last_completed = completed
            return

        if event_name == "type_classification.progress.completed":
            completed = int(meta.get("completed", last_completed) or last_completed)
            if bar is not None and completed > last_completed:
                bar.update(completed - last_completed)
                last_completed = completed
            _close()

    return _on_progress, _close


@app.command("classify-types", help="Classify web pages into schema.org types from ingestion.")
def classify_types(
    ctx: typer.Context,
    source: str = typer.Argument(
        ...,
        metavar="SOURCE",
        help="Source input for ingestion (sitemap URL/path, URL file, sheets URL/ID, or local JSON file).",
    ),
    output_csv: str | None = typer.Option(None, "--output", help="Output CSV path."),
    sheet_name: str | None = typer.Option(
        None,
        "--sheet-name",
        help="Source sheet tab name when SOURCE is a Google Spreadsheet.",
    ),
    service_account: str | None = typer.Option(
        None,
        "--service-account",
        help="Google service account as file path or JSON body (required for sheets source).",
    ),
    ingest_source: str | None = typer.Option(
        None,
        "--ingest-source",
        help="SDK ingestion source axis: auto|urls|sitemap|sheets|local.",
    ),
    ingest_loader: str | None = typer.Option(
        None,
        "--ingest-loader",
        help=(
            "SDK ingestion loader axis: "
            "auto|simple|proxy|playwright|premium_scraper|web_scrape_api|passthrough."
        ),
    ),
    ingest_passthrough_when_html: bool | None = typer.Option(
        None,
        "--ingest-passthrough-when-html/--no-ingest-passthrough-when-html",
        help="Prefer passthrough when source records include embedded HTML.",
    ),
    url_regex: str | None = typer.Option(
        None,
        "--url-regex",
        help="Optional regex filter applied to discovered URLs before loader execution.",
    ),
    agent_cli: str | None = typer.Option(
        None,
        "--agent-cli",
        help="Local agent CLI to use (claude|codex|gemini).",
    ),
    agent_timeout_sec: float = typer.Option(
        120.0,
        "--agent-timeout-sec",
        help="Timeout in seconds for each local agent classification call.",
    ),
    max_markdown_chars: int = typer.Option(
        24000,
        "--max-markdown-chars",
        help="Max markdown characters extracted per page before classification.",
    ),
    yes: bool = typer.Option(
        False,
        "-y",
        "--yes",
        help="Skip confirmation prompt and proceed.",
    ),
) -> None:
    if not yes:
        should_continue = typer.confirm(
            "Warning: this command will consume agent credits for page classification. Continue?",
            default=True,
        )
        if not should_continue:
            typer.echo("Aborted.")
            raise typer.Exit(code=0)

    profile_settings, profile_name = load_profile_settings(ctx)
    resolved_output_template = str(
        resolve_value(
            output_csv,
            profile_settings,
            paths=["web_pages.output"],
        )
        or "web_pages_type_classification_{profile}_{date}_{seq:3}.csv"
    )
    resolved_output = str(
        render_output_path_template(
            resolved_output_template,
            profile_name=profile_name,
        )
    )
    resolved_service_account = resolve_value(
        service_account,
        profile_settings,
        paths=["oauth.service_account", "sheets_service_account"],
        env_keys=["OAUTH_SERVICE_ACCOUNT", "SHEETS_SERVICE_ACCOUNT"],
    )
    resolved_ingest_source = resolve_value(ingest_source, profile_settings, paths=["ingest.source"])
    resolved_ingest_loader = resolve_value(ingest_loader, profile_settings, paths=["ingest.loader"])
    resolved_url_regex = resolve_value(url_regex, profile_settings, paths=["ingest.url_regex"])
    resolved_passthrough = _resolve_bool_value(
        ingest_passthrough_when_html,
        profile_settings,
        "ingest.passthrough_when_html",
    )
    try:
        runtime_ingest = resolve_ingest_runtime_settings(profile_settings)
    except ValueError as exc:
        raise typer.BadParameter(str(exc)) from exc
    resolved_agent_cli = resolve_value(agent_cli, profile_settings, paths=["web_pages.agent_cli"])
    resolved_agent_timeout = float(
        resolve_value(
            agent_timeout_sec,
            profile_settings,
            paths=["web_pages.agent_timeout_sec"],
        )
        or 120.0
    )
    resolved_max_markdown = int(
        resolve_value(
            max_markdown_chars,
            profile_settings,
            paths=["web_pages.max_markdown_chars"],
        )
        or 24000
    )

    on_progress, close_progress = _build_classify_progress_callback()
    try:
        result = run_classify_types(
            WebPagesClassifyTypesOptions(
                source=source,
                output_csv=resolved_output,
                sheet_name=sheet_name,
                service_account=resolved_service_account,
                ingest_source=resolved_ingest_source,
                ingest_loader=resolved_ingest_loader,
                ingest_passthrough_when_html=resolved_passthrough,
                ingest_timeout_ms=runtime_ingest.timeout_ms,
                ingest_retry_attempts=runtime_ingest.retry_attempts,
                ingest_retry_backoff_ms=runtime_ingest.retry_backoff_ms,
                playwright_wait_until=runtime_ingest.playwright_wait_until,
                url_regex=resolved_url_regex,
                agent_cli=resolved_agent_cli,
                agent_timeout_sec=resolved_agent_timeout,
                max_markdown_chars=resolved_max_markdown,
            ),
            on_progress=on_progress,
        )
    finally:
        close_progress()
    typer.echo(f"Wrote {len(result)} rows to {Path(resolved_output)}")
