"""
CLI schemas from cli.yaml.

Defines the structure for all `lw` CLI commands, arguments, and flags.
Used for command validation, help generation, and context injection.

SST File: lightwave/schema/definitions/cli.yaml
Runtime Artifact: .claude/reference/CLI_INDEX.json
"""

from pathlib import Path
from typing import Any, ClassVar

import yaml
from pydantic import Field

from lightwave.schema.pydantic.core.base import LightwaveBaseSchema, SSTSchema


class CLICommandSchema(LightwaveBaseSchema):
    """Individual CLI command definition."""

    name: str = Field(..., description="Command name (e.g., 'list', 'create')")
    args: list[str] = Field(default_factory=list, description="Required positional arguments")
    flags: list[str] = Field(default_factory=list, description="Optional flags")
    description: str | None = Field(None, description="Command description")


class CLIDomainSchema(LightwaveBaseSchema):
    """CLI domain (command group) definition."""

    name: str = Field(..., description="Domain name (e.g., 'task', 'story')")
    description: str = Field(..., description="Domain description")
    commands: list[CLICommandSchema] = Field(default_factory=list, description="Available commands")

    def get_command(self, name: str) -> CLICommandSchema | None:
        """Get a command by name."""
        for cmd in self.commands:
            if cmd.name == name:
                return cmd
        return None

    def has_command(self, name: str) -> bool:
        """Check if domain has a command."""
        return self.get_command(name) is not None


class CLIStatusAliasSchema(LightwaveBaseSchema):
    """Status alias mapping."""

    alias: str = Field(..., description="Short alias (e.g., 'hold')")
    full_status: str = Field(..., description="Full createOS status")


class CLIConfig(SSTSchema):
    """
    CLI configuration from cli.yaml.

    Provides typed access to all CLI commands, arguments, and flags.

    Example:
        config = CLIConfig.load()
        task_domain = config.get_domain("task")
        list_cmd = task_domain.get_command("list")
        assert "--all" in list_cmd.flags

    SST Validation:
        config = CLIConfig.load()
        assert config.has_domain("task")
        assert config.get_domain("task").has_command("list")
    """

    SST_KEY: ClassVar[str] = "cli"  # Registry key in __index.yaml

    version: str = Field(..., description="Schema version")
    domains: list[CLIDomainSchema] = Field(default_factory=list, description="CLI command domains")
    status_aliases: list[CLIStatusAliasSchema] = Field(default_factory=list, description="Status alias mappings")
    global_flags: list[str] = Field(default_factory=list, description="Flags available on all commands")

    @classmethod
    def get_sst_path(cls) -> Path:
        """Get path to SST YAML file (from __index.yaml registry)."""
        from lightwave.schema.core.paths import get_sst_path

        return get_sst_path(cls.SST_KEY)

    @classmethod
    def load_sst_data(cls) -> dict[str, Any]:
        """Load SST YAML file."""
        sst_path = cls.get_sst_path()
        if not sst_path.exists():
            raise FileNotFoundError(f"SST file not found: {sst_path}")
        with open(sst_path) as f:
            return yaml.safe_load(f)

    @classmethod
    def load(cls) -> "CLIConfig":
        """
        Load CLI configuration from SST YAML.

        Returns:
            CLIConfig instance with all domains and commands
        """
        data = cls.load_sst_data()
        meta = data.get("_meta", {})

        # Parse domains
        domains = []
        domains_raw = data.get("domains", {})
        for domain_name, domain_data in domains_raw.items():
            commands = []
            for cmd_data in domain_data.get("commands", []):
                commands.append(CLICommandSchema(**cmd_data))

            domains.append(
                CLIDomainSchema(
                    name=domain_name,
                    description=domain_data.get("description", ""),
                    commands=commands,
                )
            )

        # Parse status aliases
        aliases = []
        aliases_raw = data.get("status_aliases", {})
        for alias, full_status in aliases_raw.items():
            aliases.append(CLIStatusAliasSchema(alias=alias, full_status=full_status))

        return cls(
            version=meta.get("version", "1.0.0"),
            domains=domains,
            status_aliases=aliases,
            global_flags=data.get("global_flags", []),
        )

    def get_domain(self, name: str) -> CLIDomainSchema | None:
        """Get a domain by name."""
        for domain in self.domains:
            if domain.name == name:
                return domain
        return None

    def has_domain(self, name: str) -> bool:
        """Check if domain exists."""
        return self.get_domain(name) is not None

    def get_all_domain_names(self) -> list[str]:
        """Get list of all domain names."""
        return [d.name for d in self.domains]

    def get_command(self, domain: str, command: str) -> CLICommandSchema | None:
        """Get a specific command from a domain."""
        domain_obj = self.get_domain(domain)
        if domain_obj:
            return domain_obj.get_command(command)
        return None

    def resolve_status_alias(self, alias: str) -> str | None:
        """Resolve a status alias to full status."""
        for sa in self.status_aliases:
            if sa.alias == alias:
                return sa.full_status
        return None

    def to_json_index(self) -> dict[str, Any]:
        """
        Convert to JSON index format for fast lookup.

        This matches the structure of .claude/reference/CLI_INDEX.json
        """
        domains_dict = {}
        for domain in self.domains:
            cmds = {}
            for cmd in domain.commands:
                cmd_data: dict[str, Any] = {"args": cmd.args}
                if cmd.flags:
                    cmd_data["flags"] = cmd.flags
                cmds[cmd.name] = cmd_data
            domains_dict[domain.name] = {"desc": domain.description, "cmds": cmds}

        aliases_dict = {sa.alias: sa.full_status for sa in self.status_aliases}

        return {
            "$schema": "lightwave://schemas/cli",
            "version": self.version,
            "generated_from": f"packages/lightwave-core/lightwave/schema/definitions/{self.SST_FILE}",
            "domains": domains_dict,
            "statusAliases": aliases_dict,
            "globalFlags": self.global_flags,
        }


# Convenience function for quick access
def get_cli_config() -> CLIConfig:
    """
    Load and return CLI configuration.

    This is the primary entry point for accessing CLI definitions.

    Example:
        from lightwave.schema.pydantic.models.cli import get_cli_config

        config = get_cli_config()
        if config.has_domain("task"):
            task = config.get_domain("task")
            print(f"Task commands: {[c.name for c in task.commands]}")
    """
    return CLIConfig.load()
