from __future__ import annotations

import typer

from flow_xhs.cli.spec.flow_xhs_spec import FLOW_XHS_SPEC
from flow_xhs.cli.spec.model import CommandSpec, ParameterSpec
from flow_xhs.cli.spec.render_help import render_command_help, render_group_help
from flow_xhs.cli.spec.validate import get_command_spec
from flow_xhs.cli.spec.views import get_group_view
from flow_xhs.cli.utils import all_option_params_are_default

_UNSET = object()


def get_command_contract(path: tuple[str, ...]) -> CommandSpec:
    command = get_command_spec(FLOW_XHS_SPEC, path)
    if command is None:
        raise KeyError(f"unknown command contract: {' '.join(path)}")
    return command


def get_command_parameter(path: tuple[str, ...], param_id: str) -> ParameterSpec:
    command = get_command_contract(path)
    for param in command.parameters:
        if param.id == param_id:
            return param
    raise KeyError(f"unknown parameter contract: {' '.join(path)}.{param_id}")


def get_global_parameter(param_id: str) -> ParameterSpec:
    for param in FLOW_XHS_SPEC.global_options:
        if param.id == param_id:
            return param
    raise KeyError(f"unknown global parameter contract: {param_id}")


def command_summary(path: tuple[str, ...]) -> str:
    return get_command_contract(path).summary


def command_description(path: tuple[str, ...]) -> str | None:
    return get_command_contract(path).description


def group_summary(path: tuple[str, ...]) -> str:
    group = get_group_view(FLOW_XHS_SPEC, path)
    if group is None:
        raise KeyError(f"unknown group contract: {' '.join(path)}")
    return group.summary


def group_description(path: tuple[str, ...]) -> str | None:
    group = get_group_view(FLOW_XHS_SPEC, path)
    if group is None:
        raise KeyError(f"unknown group contract: {' '.join(path)}")
    return group.description


def parameter_help(path: tuple[str, ...], param_id: str) -> str:
    param = get_command_parameter(path, param_id)
    return param.description or param.summary


def parameter_default(path: tuple[str, ...], param_id: str):
    param = get_command_parameter(path, param_id)
    if param.default is None:
        return None
    return param.default.value


def parameter_min(path: tuple[str, ...], param_id: str):
    param = get_command_parameter(path, param_id)
    return param.value_type.min


def parameter_max(path: tuple[str, ...], param_id: str):
    param = get_command_parameter(path, param_id)
    return param.value_type.max


def global_help(param_id: str) -> str:
    param = get_global_parameter(param_id)
    return param.description or param.summary


def global_default(param_id: str):
    param = get_global_parameter(param_id)
    if param.default is None:
        return None
    return param.default.value


def command_option(path: tuple[str, ...], param_id: str, default=_UNSET, **kwargs):
    param = get_command_parameter(path, param_id)
    option_default = parameter_default(path, param_id) if default is _UNSET else default
    kwargs.setdefault("help", parameter_help(path, param_id))
    return typer.Option(option_default, *param.cli_names, **kwargs)


def global_option(param_id: str, default=_UNSET, **kwargs):
    param = get_global_parameter(param_id)
    option_default = global_default(param_id) if default is _UNSET else default
    kwargs.setdefault("help", global_help(param_id))
    return typer.Option(option_default, *param.cli_names, **kwargs)


def emit_command_help(
    path: tuple[str, ...],
) -> None:
    command = get_command_contract(path)
    typer.echo(render_command_help(FLOW_XHS_SPEC, command))
    raise typer.Exit(code=0)


def emit_group_help(path: tuple[str, ...]) -> None:
    group = get_group_view(FLOW_XHS_SPEC, path)
    if group is None:
        raise KeyError(f"unknown group contract: {' '.join(path)}")
    typer.echo(render_group_help(group))
    raise typer.Exit(code=0)


def emit_command_help_if_empty(
    ctx,
    path: tuple[str, ...],
) -> None:
    if all_option_params_are_default(ctx=ctx):
        emit_command_help(path)


def emit_group_help_if_empty(ctx, path: tuple[str, ...]) -> None:
    if all_option_params_are_default(ctx=ctx):
        emit_group_help(path)
