from __future__ import annotations

from dataclasses import dataclass, field as dc_field
from typing import Any

from flow_xhs.cli.spec.model import CliSpec, CommandSpec, ParameterSpec


@dataclass(frozen=True)
class CommandEntryView:
    kind: str
    name: str
    path: tuple[str, ...]
    display_path: str
    summary: str


@dataclass(frozen=True)
class GroupView:
    kind: str
    path: tuple[str, ...]
    display_path: str
    usage: str
    summary: str
    description: str | None
    commands: tuple[CommandEntryView, ...] = dc_field(default_factory=tuple)
    global_options: tuple[ParameterSpec, ...] = dc_field(default_factory=tuple)
    use_cases: tuple[str, ...] = dc_field(default_factory=tuple)


def list_root_entries(spec: CliSpec) -> tuple[CommandEntryView, ...]:
    entries: list[CommandEntryView] = []
    seen: set[tuple[str, ...]] = set()
    for command in spec.commands:
        path = command.path[:1]
        if path in seen:
            continue
        seen.add(path)
        if len(command.path) == 1:
            entries.append(
                CommandEntryView(
                    kind="command",
                    name=path[0],
                    path=path,
                    display_path=f"{spec.cli_name} {path[0]}",
                    summary=command.summary,
                )
            )
            continue
        entries.append(
            CommandEntryView(
                kind="group",
                name=path[0],
                path=path,
                display_path=f"{spec.cli_name} {path[0]}",
                summary=_get_group_summary(spec, path),
            )
        )
    return tuple(entries)


def get_group_view(spec: CliSpec, path: tuple[str, ...]) -> GroupView | None:
    if not _is_group_path(spec, path):
        return None

    commands = _list_group_entries(spec, path)
    display_path = spec.cli_name if not path else f"{spec.cli_name} {' '.join(path)}"
    usage = (
        f"{display_path} <COMMAND>"
        if path
        else spec.canonical_syntax
    )
    return GroupView(
        kind="group",
        path=path,
        display_path=display_path,
        usage=usage,
        summary=_get_group_summary(spec, path),
        description=_get_group_description(spec, path),
        commands=commands,
        global_options=_visible_global_options(spec),
        use_cases=_get_group_use_cases(spec, path),
    )


def _is_group_path(spec: CliSpec, path: tuple[str, ...]) -> bool:
    if not path:
        return True
    for command in spec.commands:
        if len(command.path) > len(path) and command.path[: len(path)] == path:
            return True
    return False


def _list_group_entries(spec: CliSpec, path: tuple[str, ...]) -> tuple[CommandEntryView, ...]:
    entries: list[CommandEntryView] = []
    seen: set[tuple[str, ...]] = set()

    for command in spec.commands:
        if len(command.path) <= len(path) or command.path[: len(path)] != path:
            continue
        child_path = command.path[: len(path) + 1]
        if child_path in seen:
            continue
        seen.add(child_path)
        if len(command.path) == len(path) + 1:
            entries.append(
                CommandEntryView(
                    kind="command",
                    name=child_path[-1],
                    path=child_path,
                    display_path=command.display_path,
                    summary=command.summary,
                )
            )
            continue
        entries.append(
            CommandEntryView(
                kind="group",
                name=child_path[-1],
                path=child_path,
                display_path=f"{spec.cli_name} {' '.join(child_path)}",
                summary=_get_group_summary(spec, child_path),
            )
        )

    return tuple(entries)


def _get_group_metadata(spec: CliSpec, path: tuple[str, ...]) -> dict[str, Any]:
    groups = spec.metadata.get("groups", {})
    raw = groups.get(path)
    if raw is not None:
        return raw
    return groups.get(" ".join(path), {})


def _get_group_summary(spec: CliSpec, path: tuple[str, ...]) -> str:
    meta = _get_group_metadata(spec, path)
    summary = meta.get("summary")
    if isinstance(summary, str) and summary.strip():
        return summary.strip()
    if not path:
        return spec.summary
    return f"{path[-1]} 相关命令。"


def _get_group_description(spec: CliSpec, path: tuple[str, ...]) -> str | None:
    meta = _get_group_metadata(spec, path)
    description = meta.get("description")
    if isinstance(description, str) and description.strip():
        return description.strip()
    return None


def _visible_global_options(spec: CliSpec) -> tuple[ParameterSpec, ...]:
    hidden_ids = {"help", "version"}
    return tuple(param for param in spec.global_options if param.id not in hidden_ids)


def _get_group_use_cases(spec: CliSpec, path: tuple[str, ...]) -> tuple[str, ...]:
    meta = _get_group_metadata(spec, path)
    raw = meta.get("use_cases")
    if isinstance(raw, (list, tuple)):
        values = tuple(item.strip() for item in raw if isinstance(item, str) and item.strip())
        return values
    return ()
