from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any

from radicli import Radicli, get_list_converter
from wasabi import msg

from prodigy_teams_broker_sdk.errors import BrokerError
from prodigy_teams_pam_sdk.errors import AuthError, RecipeProcessingError

from . import about
from .errors import CLIError, HTTPError, HTTPStatusError, HTTPXErrors, ProdigyTeamsError
from .messages import Messages
from .ui import isatty

HELP = """Ellf Command Line Interface."""


def _emit_error(title: str, detail: str | None = None) -> int:
    if not isatty(sys.stderr):
        error: dict[str, str] = {"error": title}
        if detail:
            error["detail"] = detail
        print(json.dumps(error), file=sys.stderr)
    else:
        msg.fail(title, detail or "")
    return 1


def handle_cli_error(err: CLIError | RecipeProcessingError) -> int:
    return _emit_error(str(err.title), str(err.text) if err.text else None)


def handle_teams_error(err: ProdigyTeamsError) -> int:
    return _emit_error(str(err.message))


def handle_broker_error(err: BrokerError) -> int:
    err_type = "Unexpected error"
    err_message = None
    if err.detail is not None:
        try:
            detail = json.loads(err.detail)
            err_type = detail["type"]
            err_message = detail["message"]
        except (json.JSONDecodeError, KeyError):
            pass
    text = Messages.E046.format(type=err_type)
    return _emit_error(text, err_message)


def handle_http_error(err: HTTPError) -> int:
    # This happens for all other request errors we're not catching
    if isinstance(err, HTTPStatusError):
        try:
            detail = json.loads(err.response.text)
            raw_detail = detail["detail"]
            if isinstance(raw_detail, str):
                message = raw_detail
            else:
                message = ", ".join(str(d) for d in raw_detail)
        except (json.JSONDecodeError, KeyError, TypeError):
            message = str(err)
        return _emit_error(Messages.E047 + f" ({err.response.status_code})", message)
    else:
        return _emit_error(Messages.E047, str(err))


def handle_auth_error(err: AuthError) -> int:
    return _emit_error(
        Messages.E039.format(message=err.message),
        Messages.E040.format(command=f"{about.__prog__} login"),
    )


# Custom types mapped to how they should be converted on the CLI
CONVERTERS: dict[Any, Any] = {list[str]: get_list_converter(str)}
# Custom error types mapped to how they should be handled
ERRORS = {
    CLIError: handle_cli_error,
    RecipeProcessingError: handle_cli_error,
    ProdigyTeamsError: handle_teams_error,
    BrokerError: handle_broker_error,
    **{error: handle_http_error for error in HTTPXErrors},
}


cli = Radicli(
    prog=about.__prog__,
    help=HELP,
    version=about.__version__,
    converters=CONVERTERS,
    errors=ERRORS,
)
cli.placeholder("actions", description=Messages.doc_actions)
cli.placeholder("coding-tasks", description=Messages.doc_coding_tasks)
cli.placeholder("agents", description=Messages.doc_agents)
cli.placeholder("assets", description=Messages.doc_assets)
cli.placeholder("clusters", description=Messages.doc_clusters)
cli.placeholder("config", description=Messages.doc_config)
cli.placeholder("datasets", description=Messages.doc_datasets)
cli.placeholder("files", description=Messages.doc_files)
cli.placeholder("gcp", description=Messages.doc_gcp)
cli.placeholder("jobs", description=Messages.doc_jobs)
cli.placeholder("packages", description=Messages.doc_packages)
cli.placeholder("paths", description=Messages.doc_paths)
cli.placeholder("projects", description=Messages.doc_projects)
cli.placeholder("recipes", description=Messages.doc_recipes)
cli.placeholder("secrets", description=Messages.doc_secrets)
cli.placeholder("tasks", description=Messages.doc_tasks)
cli.placeholder("publish", description="Publish")


def document_cli(cli: Radicli, *, root: Path = Path.cwd()) -> str:
    """Auto-generate Markdown API docs for ptc CLI"""
    title = "Ellf CLI"
    desc = (
        f"Before using Ellf CLI you need to have an **Ellf** "
        f"account. You also need a deployed cluster and Python 3.6+. To "
        f"see all available commands or subcommands, you can use the `--help` "
        f"flag, e.g. `{cli.prog} --help`."
    )
    return cli.document(title=title, description=desc, path_root=root)
