"""Workflow-related CLI commands with MCP-parity flags and output."""

from __future__ import annotations

import asyncio
import json
import sys
from pathlib import Path
from typing import Any, Callable, Coroutine

import typer
from dotenv import load_dotenv

from skyvern.config import settings
from skyvern.utils.env_paths import resolve_backend_env_path

from .commands._output import output, output_error
from .mcp_tools.workflow import skyvern_workflow_cancel as tool_workflow_cancel
from .mcp_tools.workflow import skyvern_workflow_create as tool_workflow_create
from .mcp_tools.workflow import skyvern_workflow_delete as tool_workflow_delete
from .mcp_tools.workflow import skyvern_workflow_get as tool_workflow_get
from .mcp_tools.workflow import skyvern_workflow_list as tool_workflow_list
from .mcp_tools.workflow import skyvern_workflow_run as tool_workflow_run
from .mcp_tools.workflow import skyvern_workflow_status as tool_workflow_status
from .mcp_tools.workflow import skyvern_workflow_update as tool_workflow_update

workflow_app = typer.Typer(help="Manage Skyvern workflows.", no_args_is_help=True)


def _emit_tool_result(result: dict[str, Any], *, json_output: bool) -> None:
    if json_output:
        json.dump(result, sys.stdout, indent=2, default=str)
        sys.stdout.write("\n")
        if not result.get("ok", False):
            raise SystemExit(1)
        return

    if result.get("ok", False):
        output(result.get("data"), action=str(result.get("action", "")), json_mode=False)
        return

    err = result.get("error") or {}
    output_error(str(err.get("message", "Unknown error")), hint=str(err.get("hint", "")), json_mode=False)


def _run_tool(
    runner: Callable[[], Coroutine[Any, Any, dict[str, Any]]],
    *,
    json_output: bool,
    hint_on_exception: str,
) -> None:
    try:
        result: dict[str, Any] = asyncio.run(runner())
        _emit_tool_result(result, json_output=json_output)
    except typer.BadParameter:
        raise
    except Exception as e:
        output_error(str(e), hint=hint_on_exception, json_mode=json_output)


def _resolve_inline_or_file(value: str | None, *, param_name: str) -> str | None:
    if value is None or not value.startswith("@"):
        return value

    file_path = value[1:]
    if not file_path:
        raise typer.BadParameter(f"{param_name} file path cannot be empty after '@'.")

    path = Path(file_path).expanduser()
    try:
        return path.read_text(encoding="utf-8")
    except OSError as e:
        raise typer.BadParameter(f"Unable to read {param_name} file '{path}': {e}") from e


@workflow_app.callback()
def workflow_callback(
    api_key: str | None = typer.Option(
        None,
        "--api-key",
        help="Skyvern API key",
        envvar="SKYVERN_API_KEY",
    ),
) -> None:
    """Load workflow CLI environment and optional API key override."""
    load_dotenv(resolve_backend_env_path())
    if api_key:
        settings.SKYVERN_API_KEY = api_key


@workflow_app.command("list")
def workflow_list(
    search: str | None = typer.Option(
        None,
        "--search",
        help="Search across workflow titles, folder names, and parameter metadata.",
    ),
    page: int = typer.Option(1, "--page", min=1, help="Page number (1-based)."),
    page_size: int = typer.Option(10, "--page-size", min=1, max=100, help="Results per page."),
    only_workflows: bool = typer.Option(
        False,
        "--only-workflows",
        help="Only return multi-step workflows (exclude saved tasks).",
    ),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """List workflows."""

    async def _run() -> dict[str, Any]:
        return await tool_workflow_list(
            search=search,
            page=page,
            page_size=page_size,
            only_workflows=only_workflows,
        )

    _run_tool(_run, json_output=json_output, hint_on_exception="Check your API key and workflow list filters.")


@workflow_app.command("get")
def workflow_get(
    workflow_id: str = typer.Option(..., "--id", help="Workflow permanent ID (wpid_...)."),
    version: int | None = typer.Option(None, "--version", min=1, help="Specific version to retrieve."),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Get a workflow definition by ID."""

    async def _run() -> dict[str, Any]:
        return await tool_workflow_get(workflow_id=workflow_id, version=version)

    _run_tool(_run, json_output=json_output, hint_on_exception="Check your API key and workflow ID.")


@workflow_app.command("create")
def workflow_create(
    definition: str = typer.Option(
        ...,
        "--definition",
        help="Workflow definition as YAML/JSON string or @file path.",
    ),
    definition_format: str = typer.Option(
        "auto",
        "--format",
        help="Definition format: json, yaml, or auto.",
    ),
    folder_id: str | None = typer.Option(None, "--folder-id", help="Folder ID (fld_...) for the workflow."),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Create a workflow."""

    async def _run() -> dict[str, Any]:
        resolved_definition = _resolve_inline_or_file(definition, param_name="definition")
        assert resolved_definition is not None
        return await tool_workflow_create(
            definition=resolved_definition,
            format=definition_format,
            folder_id=folder_id,
        )

    _run_tool(_run, json_output=json_output, hint_on_exception="Check the workflow definition syntax.")


@workflow_app.command("update")
def workflow_update(
    workflow_id: str = typer.Option(..., "--id", help="Workflow permanent ID (wpid_...)."),
    definition: str = typer.Option(
        ...,
        "--definition",
        help="Updated workflow definition as YAML/JSON string or @file path.",
    ),
    definition_format: str = typer.Option(
        "auto",
        "--format",
        help="Definition format: json, yaml, or auto.",
    ),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Update a workflow definition."""

    async def _run() -> dict[str, Any]:
        resolved_definition = _resolve_inline_or_file(definition, param_name="definition")
        assert resolved_definition is not None
        return await tool_workflow_update(
            workflow_id=workflow_id,
            definition=resolved_definition,
            format=definition_format,
        )

    _run_tool(_run, json_output=json_output, hint_on_exception="Check the workflow ID and definition syntax.")


@workflow_app.command("delete")
def workflow_delete(
    workflow_id: str = typer.Option(..., "--id", help="Workflow permanent ID (wpid_...)."),
    force: bool = typer.Option(False, "--force", help="Confirm permanent deletion."),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Delete a workflow."""

    async def _run() -> dict[str, Any]:
        return await tool_workflow_delete(workflow_id=workflow_id, force=force)

    _run_tool(_run, json_output=json_output, hint_on_exception="Check the workflow ID and your permissions.")


@workflow_app.command("run")
def workflow_run(
    workflow_id: str = typer.Option(..., "--id", help="Workflow permanent ID (wpid_...)."),
    params: str | None = typer.Option(
        None,
        "--params",
        "--parameters",
        "-p",
        help="Workflow parameters as JSON string or @file path.",
    ),
    session: str | None = typer.Option(None, "--session", help="Browser session ID (pbs_...) to reuse."),
    webhook: str | None = typer.Option(None, "--webhook", help="Status webhook callback URL."),
    proxy: str | None = typer.Option(None, "--proxy", help="Proxy location (e.g., RESIDENTIAL)."),
    wait: bool = typer.Option(False, "--wait", help="Wait for workflow completion before returning."),
    timeout: int = typer.Option(
        300,
        "--timeout",
        min=10,
        max=3600,
        help="Max wait time in seconds when --wait is set.",
    ),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Run a workflow."""

    async def _run() -> dict[str, Any]:
        resolved_params = _resolve_inline_or_file(params, param_name="params")
        return await tool_workflow_run(
            workflow_id=workflow_id,
            parameters=resolved_params,
            browser_session_id=session,
            webhook_url=webhook,
            proxy_location=proxy,
            wait=wait,
            timeout_seconds=timeout,
        )

    _run_tool(_run, json_output=json_output, hint_on_exception="Check the workflow ID and run parameters.")


@workflow_app.command("status")
def workflow_status(
    run_id: str = typer.Option(..., "--run-id", help="Run ID (wr_... or tsk_v2_...)."),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Get workflow run status."""

    async def _run() -> dict[str, Any]:
        return await tool_workflow_status(run_id=run_id)

    _run_tool(_run, json_output=json_output, hint_on_exception="Check the run ID and API key.")


@workflow_app.command("cancel")
def workflow_cancel(
    run_id: str = typer.Option(..., "--run-id", help="Run ID (wr_... or tsk_v2_...)."),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON."),
) -> None:
    """Cancel a workflow run."""

    async def _run() -> dict[str, Any]:
        return await tool_workflow_cancel(run_id=run_id)

    _run_tool(_run, json_output=json_output, hint_on_exception="Check the run ID and API key.")
