"""Alloy Runtime CLI main entry point.

This module defines the root Typer application and registers all command groups.
Running `alloy` with no arguments launches the unified TUI application.
"""

import json
import errno
import os
import sys
from collections.abc import Callable, Iterator, Sequence
from contextlib import contextmanager
from enum import Enum
from importlib import import_module
from pathlib import Path
from typing import Any, Optional, TextIO, cast

import click
import typer
from click.core import ParameterSource
from rich.console import Console
from typer.main import get_command
from typer.core import TyperGroup

from cli.commands.admin.credentials.create.command import (
    admin_credentials_create_command,
)
from cli.commands.admin.credentials.grant.command import (
    admin_credentials_grant_command,
)
from cli.commands.api_keys.create.command import api_keys_create_command
from cli.commands.agents.create.command import agents_create_command
from cli.commands.agents.delete.command import agents_delete_command
from cli.commands.agents.list.command import agents_list_command
from cli.commands.agents.update.command import agents_update_command
from cli.commands.auth.login.command import login_command
from cli.commands.auth.signup.command import signup_command
from cli.commands.ping.command import ping_command
from cli.commands.agents.get.command import agents_get_command
from cli.commands.audio.transcribe.command import audio_transcribe_command
from cli.commands.billing.costs.by_agent.command import billing_costs_by_agent_command
from cli.commands.billing.costs.by_model.command import billing_costs_by_model_command
from cli.commands.billing.costs.daily.command import billing_costs_daily_command
from cli.commands.billing.costs.summary.command import billing_costs_summary_command
from cli.commands.billing.projects.create.command import billing_projects_create_command
from cli.commands.billing.projects.get.command import billing_projects_get_command
from cli.commands.billing.projects.list.command import billing_projects_list_command
from cli.commands.generate.cancel.command import generate_cancel_command
from cli.commands.generate.status.command import generate_status_command
from cli.commands.knowledge.collections.cluster.command import (
    knowledge_collections_cluster_command,
)
from cli.commands.knowledge.collections.cluster_status.command import (
    knowledge_collections_cluster_status_command,
)
from cli.commands.knowledge.collections.status.command import (
    knowledge_collections_status_command,
)
from cli.commands.knowledge.documents.bulk_metadata.command import (
    knowledge_documents_bulk_metadata_command,
)
from cli.commands.knowledge.documents.count.command import (
    knowledge_documents_count_command,
)
from cli.commands.knowledge.documents.bulk_reingest_failed.command import (
    knowledge_documents_bulk_reingest_failed_command,
)
from cli.commands.knowledge.documents.bulk_reingest_failed_status.command import (
    knowledge_documents_bulk_reingest_failed_status_command,
)
from cli.commands.knowledge.documents.reingest.command import (
    knowledge_documents_reingest_command,
)
from cli.commands.knowledge.documents.update.command import (
    knowledge_documents_update_command,
)
from cli.commands.knowledge.synthesis.update.command import (
    knowledge_synthesis_update_command,
)
from cli.commands.organizations.create.command import organizations_create_command
from cli.commands.pipelines.create.command import pipelines_create_command
from cli.commands.pipelines.env_vars.command import pipelines_env_vars_command
from cli.commands.pipelines.update.command import pipelines_update_command
from cli.commands.pipelines.executions.costs.command import (
    pipeline_execution_costs_command,
)
from cli.commands.pipelines.executions.costs_by_model.command import (
    pipeline_execution_costs_by_model_command,
)
from cli.commands.pipelines.executions.update.command import (
    pipeline_execution_update_command,
)

from cli.commands.pipelines.schedules.env_vars_command import (
    pipelines_schedules_env_vars_command,
)
from cli.commands.pipelines.schedules.create_command import (
    pipelines_schedules_create_command,
)
from cli.commands.pipelines.schedules.update_command import (
    pipelines_schedules_update_command,
)
from cli.commands.pipelines.schedules.once_command import (
    pipelines_schedules_once_command,
)
from cli.commands.schemas.delete.command import schemas_delete_command
from cli.commands.tags.get.command import tags_get_command
from cli.commands.tags.update.command import tags_update_command
from cli.commands.templates.get_by_version.command import (
    templates_get_by_version_command,
)
from cli.commands.tools.get.command import tools_get_command
from cli.commands.tools.list.command import tools_list_command
from cli.commands.users.create.command import users_create_command

from cli.commands.content.edit.command import content_edit_command
from cli.commands.content.get.command import content_get_command
from cli.commands.content.list.command import content_list_command
from cli.commands.credentials.create.command import credentials_create_command
from cli.commands.credentials.list.command import credentials_list_command
from cli.commands.credentials.supported.command import credentials_supported_command
from cli.commands.credentials.update.command import credentials_update_command
from cli.commands.generate.text.command import generate_text_command
from cli.commands.models.list.command import models_list_command
from cli.commands.schemas.create.command import schemas_create_command
from cli.commands.schemas.get.command import schemas_get_command
from cli.commands.schemas.list.command import schemas_list_command
from cli.commands.sync.command import sync_command
from cli.commands.admin.bootstrap_command import bootstrap_admin_command
from cli.commands.templates.create.command import templates_create_command
from cli.commands.templates.delete.command import templates_delete_command
from cli.commands.templates.get.command import templates_get_command
from cli.commands.templates.list.command import templates_list_command
from cli.commands.templates.render.command import templates_render_command
from cli.commands.templates.validate.command import templates_validate_command
from cli.commands.templates.update.command import templates_update_command
from cli.commands.templates.version.command import templates_version_command
from cli.commands.content.delete.command import content_delete_command
from cli.commands.executions.get.command import executions_get_command
from cli.commands.schemas.update.command import schemas_update_command
from cli.commands.render.html_to_image.command import render_html_to_image_command
from cli.commands.knowledge.collections.create.command import (
    knowledge_collections_create_command,
)
from cli.commands.knowledge.collections.delete.command import (
    knowledge_collections_delete_command,
)
from cli.commands.knowledge.collections.get.command import (
    knowledge_collections_get_command,
)
from cli.commands.knowledge.collections.list.command import (
    knowledge_collections_list_command,
)
from cli.commands.knowledge.collections.update.command import (
    knowledge_collections_update_command,
)
from cli.commands.knowledge.documents.delete.command import (
    knowledge_documents_delete_command,
)
from cli.commands.knowledge.documents.get.command import (
    knowledge_documents_get_command,
)
from cli.commands.knowledge.documents.ingest.command import (
    knowledge_documents_ingest_command,
)
from cli.commands.knowledge.documents.list.command import (
    knowledge_documents_list_command,
)
from cli.commands.knowledge.recover.command import knowledge_recover_command
from cli.commands.knowledge.search.command import knowledge_search_command
from cli.commands.knowledge.synthesis.create.command import (
    knowledge_synthesis_create_command,
)
from cli.commands.knowledge.synthesis.delete.command import (
    knowledge_synthesis_delete_command,
)
from cli.commands.knowledge.synthesis.get.command import (
    knowledge_synthesis_get_command,
)
from cli.commands.knowledge.synthesis.list.command import (
    knowledge_synthesis_list_command,
)
from cli.commands.knowledge.synthesis.refresh.command import (
    knowledge_synthesis_refresh_command,
)
from cli.commands.tool_configs.create.command import tool_configs_create_command
from cli.commands.tool_configs.delete.command import tool_configs_delete_command
from cli.commands.tool_configs.get.command import tool_configs_get_command
from cli.commands.tool_configs.list.command import tool_configs_list_command
from cli.commands.tool_configs.update.command import tool_configs_update_command
from cli.commands.pipelines.list.command import pipelines_list_command
from cli.commands.pipelines.get.command import pipelines_get_command
from cli.commands.pipelines.execute.command import pipelines_execute_command
from cli.commands.pipelines.executions.list_command import (
    pipelines_executions_list_command,
)
from cli.commands.pipelines.executions.get_command import (
    pipelines_executions_get_command,
)
from cli.commands.pipelines.schedules.list_command import (
    pipelines_schedules_list_command,
)
from cli.commands.pipelines.schedules.get_command import pipelines_schedules_get_command
from cli.commands.pipelines.schedules.delete_command import (
    pipelines_schedules_delete_command,
)
from cli.commands.pipelines.costs.command import pipelines_costs_command
from cli.commands.pipelines.approvals.get_command import (
    pipelines_approvals_get_command,
)
from cli.commands.pipelines.approvals.decide_command import (
    pipelines_approvals_decide_command,
)
from cli.commands.pipelines.executions.rerun_command import (
    pipelines_executions_rerun_command,
)
from cli.commands.pipelines.costs.daily_command import pipeline_costs_daily_command
from cli.commands.sessions.list.command import sessions_list_command
from cli.commands.sessions.get.command import sessions_get_command
from cli.commands.sessions.delete.command import sessions_delete_command
from cli.commands.sessions.messages.command import sessions_messages_command
from cli.commands.tags.list.command import tags_list_command
from cli.commands.tags.create.command import tags_create_command
from cli.commands.tags.delete.command import tags_delete_command
from cli.infrastructure.config import load_config, set_global_overrides
from cli.infrastructure.error_display import display_usage_error
from cli.infrastructure.help_json import build_help_json
from cli.infrastructure.help_markdown import render_help_markdown
from cli.infrastructure.output import OutputFormat, OutputService
from alloy_runtime_sdk.logging.config import LogConfig, LogFormat, LogLevel, configure

console = Console()


class AlloyTyperGroup(TyperGroup):
    def main(
        self,
        args: Sequence[str] | None = None,
        prog_name: str | None = None,
        complete_var: str | None = None,
        standalone_mode: bool = True,
        windows_expand_args: bool = True,
        **extra: Any,
    ) -> object:
        try:
            if args is None:
                resolved_args = sys.argv[1:]
            else:
                resolved_args = list(args)

            if prog_name is None:
                prog_name = self.name or "alloy"

            self._main_shell_completion(extra, prog_name, complete_var)

            with self.make_context(prog_name, resolved_args, **extra) as ctx:
                rv = self.invoke(ctx)
                if not standalone_mode:
                    return rv
                ctx.exit()
        except EOFError as error:
            click.echo(file=sys.stderr)
            raise click.Abort() from error
        except KeyboardInterrupt as error:
            raise click.exceptions.Exit(130) from error
        except click.ClickException as error:
            command_path = (
                getattr(getattr(error, "ctx", None), "command_path", None)
                or prog_name
                or "alloy"
            )
            display_usage_error(error.format_message(), command_path=command_path)
            if standalone_mode:
                raise SystemExit(error.exit_code) from None
            return error.exit_code
        except OSError as error:
            if error.errno == errno.EPIPE:
                sys.stdout = cast(TextIO, click.utils.PacifyFlushWrapper(sys.stdout))
                sys.stderr = cast(TextIO, click.utils.PacifyFlushWrapper(sys.stderr))
                if standalone_mode:
                    raise SystemExit(1) from None
                return 1
            raise
        except click.exceptions.Exit as error:
            if standalone_mode:
                raise SystemExit(error.exit_code) from None
            return error.exit_code
        except click.Abort:
            if standalone_mode:
                raise SystemExit(1) from None
            raise


# Root CLI application
# Note: no_args_is_help=False and invoke_without_command=True allows
app = typer.Typer(
    name="alloy",
    cls=AlloyTyperGroup,
    help="Alloy Runtime CLI - Unified interface for AI orchestration. Run without arguments to launch TUI. Normal command output supports compact, table, and json formats.",
    no_args_is_help=False,
    invoke_without_command=True,
    add_help_option=False,
)


@app.callback(invoke_without_command=True)
def global_options(
    ctx: typer.Context,
    help_requested: bool = typer.Option(
        False,
        "--help",
        is_eager=True,
        help="Show this message and exit.",
    ),
    help_json: bool = typer.Option(
        False,
        "--help-json",
        is_eager=True,
        help="Show machine-readable CLI help as JSON and exit.",
    ),
    help_markdown: bool = typer.Option(
        False,
        "--help-markdown",
        is_eager=True,
        help="Show machine-readable CLI help as Markdown and exit.",
    ),
    help_toon: bool = typer.Option(
        False,
        "--help-toon",
        is_eager=True,
        help="Show machine-readable CLI help as TOON and exit.",
    ),
    api_url: Optional[str] = typer.Option(
        None,
        "--api-url",
        help="Override API URL",
        envvar="ALLOY_RUNTIME_API_URL",
    ),
    api_key: Optional[str] = typer.Option(
        None,
        "--api-key",
        help="Override API key",
        envvar="ALLOY_RUNTIME_API_KEY",
    ),
    output_format: OutputFormat = typer.Option(
        OutputFormat.COMPACT,
        "--format",
        help="Choose command output format",
    ),
    no_tui: bool = typer.Option(
        False,
        "--no-tui",
        help="Show CLI help instead of TUI",
    ),
) -> None:
    OutputService.configure(output_format)

    api_url_source = ctx.get_parameter_source("api_url")
    api_key_source = ctx.get_parameter_source("api_key")
    set_global_overrides(
        api_url=api_url if api_url_source is ParameterSource.COMMANDLINE else None,
        api_key=api_key if api_key_source is ParameterSource.COMMANDLINE else None,
    )

    help_format = _resolve_help_format()
    structured_help_format = _resolve_structured_help_format(
        help_json=help_json,
        help_markdown=help_markdown,
        help_toon=help_toon,
        env_help_format=help_format,
        help_requested=help_requested,
        no_tui=no_tui,
    )

    if help_requested:
        if structured_help_format is not None:
            _print_structured_help(ctx, structured_help_format)
        else:
            console.print(ctx.get_help(), markup=False, highlight=False)
        raise typer.Exit()

    if help_json:
        _print_structured_help(ctx, "json")
        raise typer.Exit()

    if help_markdown:
        _print_structured_help(ctx, "markdown")
        raise typer.Exit()

    if help_toon:
        _print_structured_help(ctx, "toon")
        raise typer.Exit()

    if ctx.invoked_subcommand is None:
        if structured_help_format is not None:
            _print_structured_help(ctx, structured_help_format)
            raise typer.Exit()

        if no_tui:
            console.print(ctx.get_help(), markup=False, highlight=False)
            raise typer.Exit()

        _launch_tui()
        raise typer.Exit()


def _resolve_help_format() -> str | None:
    value = os.environ.get("ALLOY_RUNTIME_HELP_FORMAT")
    if value is None:
        return None
    normalized = value.strip().lower()
    if not normalized:
        return None
    return normalized


def _resolve_structured_help_format(
    *,
    help_json: bool,
    help_markdown: bool,
    help_toon: bool,
    env_help_format: str | None,
    help_requested: bool,
    no_tui: bool,
) -> str | None:
    if help_json:
        return "json"
    if help_markdown:
        return "markdown"
    if help_toon:
        return "toon"
    if not (help_requested or no_tui):
        return None
    if env_help_format in {"json", "markdown", "toon"}:
        return env_help_format
    return None


def _print_structured_help(ctx: typer.Context, output_format: str) -> None:
    root_ctx = ctx.find_root()
    root_ctx.command = get_command(app)
    payload = build_help_json(root_ctx)
    if output_format == "json":
        typer.echo(json.dumps(payload, indent=2, sort_keys=False))
        return
    if output_format == "toon":
        module = import_module("cli.infrastructure.help_toon")
        render_help_toon = getattr(module, "render_help_toon")
        help_toon_error = getattr(module, "HelpToonUnavailableError")
        install_hint = getattr(module, "TOON_FORMAT_INSTALL_HINT")
        try:
            typer.echo(render_help_toon(payload), nl=False)
        except help_toon_error as error:
            display_usage_error(
                str(error),
                command_path=root_ctx.command_path,
                hint=install_hint,
            )
            raise typer.Exit(code=1)
        return
    typer.echo(render_help_markdown(payload), nl=False)


def _launch_tui() -> None:
    """Launch the unified Terminal UI application."""
    from cli.tui.app import run_app

    try:
        config = load_config()
    except Exception as e:
        console.print(f"Configuration error: {e}", markup=False, highlight=False)
        console.print(
            "\nRun 'alloy login' to authenticate first.",
            markup=False,
            highlight=False,
        )
        raise typer.Exit(code=1)

    if not config.api_key:
        console.print("Not authenticated.", markup=False, highlight=False)
        console.print(
            "\nRun 'alloy login' to authenticate first.",
            markup=False,
            highlight=False,
        )
        raise typer.Exit(code=1)

    run_app(api_url=config.api_url, api_key=config.api_key)


def _normalize_trogon_default_value(value: object) -> object:
    if isinstance(value, Enum):
        return value.value
    if isinstance(value, tuple):
        tuple_values = cast(tuple[object, ...], value)
        normalized_tuple_items: list[object] = []
        for item in tuple_values:
            normalized_tuple_items.append(_normalize_trogon_default_value(item))
        return tuple(normalized_tuple_items)
    if isinstance(value, list):
        list_values = cast(list[object], value)
        normalized_list_items: list[object] = []
        for item in list_values:
            normalized_list_items.append(_normalize_trogon_default_value(item))
        return normalized_list_items
    return value


@contextmanager
def patched_trogon_enum_defaults() -> Iterator[None]:
    from trogon.introspect import MultiValueParamData

    original_process_cli_option = cast(
        Callable[[object], MultiValueParamData],
        cast(Any, MultiValueParamData).process_cli_option,
    )

    def patched_process_cli_option(value: object) -> Any:
        return original_process_cli_option(_normalize_trogon_default_value(value))

    setattr(
        MultiValueParamData,
        "process_cli_option",
        staticmethod(patched_process_cli_option),
    )
    try:
        yield
    finally:
        setattr(
            MultiValueParamData,
            "process_cli_option",
            staticmethod(original_process_cli_option),
        )


def _launch_command_tui() -> None:
    from trogon import typer as trogon_typer

    with patched_trogon_enum_defaults():
        trogon_typer.Trogon(
            typer.main.get_group(app),
            click_context=click.get_current_context(),
        ).run()


# Register top-level auth commands for convenience
app.command("login")(login_command)
app.command("signup")(signup_command)
app.command("ping")(ping_command)

# Credentials command group
credentials_app = typer.Typer(
    help="Manage supported and configured credentials",
    no_args_is_help=True,
)

# Register credentials group with main app
app.add_typer(credentials_app, name="credentials")

# Register credential commands
credentials_app.command("create")(credentials_create_command)
credentials_app.command("list")(credentials_list_command)
credentials_app.command("supported", help="List supported credential targets")(
    credentials_supported_command
)
credentials_app.command("update")(credentials_update_command)

api_keys_app = typer.Typer(
    help="Manage API keys",
    no_args_is_help=True,
)

app.add_typer(api_keys_app, name="api-keys")

api_keys_app.command("create")(api_keys_create_command)

# Models command group
models_app = typer.Typer(
    help="List and query AI models",
    no_args_is_help=True,
)

# Register models group with main app
app.add_typer(models_app, name="models")

# Register model commands
models_app.command("list")(models_list_command)

# ========================================
# Admin group (system administration)
# ========================================
admin_app = typer.Typer(
    name="admin",
    help="System administration commands (requires system admin privileges)",
    no_args_is_help=True,
)

# Register admin group with main app
app.add_typer(admin_app, name="admin")

# Register admin commands
admin_app.command("bootstrap")(bootstrap_admin_command)

# Admin credentials subgroup
admin_credentials_app = typer.Typer(
    help="Manage system credentials",
    no_args_is_help=True,
)

# Register admin credentials subgroup
admin_app.add_typer(admin_credentials_app, name="credentials")

# Register admin credentials commands
admin_credentials_app.command("create")(admin_credentials_create_command)
admin_credentials_app.command("grant")(admin_credentials_grant_command)

# Admin sync subgroup
admin_sync_app = typer.Typer(
    help="Sync data from external sources",
    no_args_is_help=True,
)

# Register admin sync subgroup
admin_app.add_typer(admin_sync_app, name="sync")

# Register admin sync commands
admin_sync_app.command("models")(sync_command)

# Agents command group
agents_app = typer.Typer(
    help="Manage AI agents",
    no_args_is_help=True,
)

# Register agents group with main app
app.add_typer(agents_app, name="agents")

# Register agent commands
agents_app.command("create")(agents_create_command)
agents_app.command("delete")(agents_delete_command)
agents_app.command("list")(agents_list_command)
agents_app.command("update")(agents_update_command)
agents_app.command("get")(agents_get_command)

# Templates command group
templates_app = typer.Typer(
    help="Manage templates (system instructions, user messages, fragments)",
    no_args_is_help=True,
)

# Register templates group with main app
app.add_typer(templates_app, name="templates")

# Register template commands
templates_app.command("create")(templates_create_command)
templates_app.command("delete")(templates_delete_command)
templates_app.command("get")(templates_get_command)
templates_app.command("list")(templates_list_command)
templates_app.command("render")(templates_render_command)
templates_app.command("validate")(templates_validate_command)
templates_app.command("update")(templates_update_command)
templates_app.command("version")(templates_version_command)
templates_app.command("get-by-version")(templates_get_by_version_command)

# Generate command group
generate_app = typer.Typer(
    help="Generate AI content (text, images, etc.)",
    no_args_is_help=True,
)

# Register generate group with main app
app.add_typer(generate_app, name="generate")

# Register generate commands
generate_app.command("text")(generate_text_command)
generate_app.command("status")(generate_status_command)
generate_app.command("cancel")(generate_cancel_command)

# Content command group
content_app = typer.Typer(
    help="Query and manage content from user and assistant messages",
    no_args_is_help=True,
)

# Register content group with main app
app.add_typer(content_app, name="content")

# Register content commands
content_app.command("list")(content_list_command)
content_app.command("get")(content_get_command)
content_app.command("edit")(content_edit_command)
content_app.command("delete")(content_delete_command)

# Schemas command group
schemas_app = typer.Typer(
    help="Manage input/output validation schemas",
    no_args_is_help=True,
)

# Register schemas group with main app
app.add_typer(schemas_app, name="schemas")

# Register schema commands
schemas_app.command("create")(schemas_create_command)
schemas_app.command("get")(schemas_get_command)
schemas_app.command("list")(schemas_list_command)
schemas_app.command("update")(schemas_update_command)
schemas_app.command("delete")(schemas_delete_command)

# Render command group
render_app = typer.Typer(
    help="Render content to various formats (HTML to image, etc.)",
    no_args_is_help=True,
)

# Register render group with main app
app.add_typer(render_app, name="render")

# Register render commands
render_app.command("image")(render_html_to_image_command)

# Knowledge command group
knowledge_app = typer.Typer(
    help="Manage knowledge collections and documents",
    no_args_is_help=True,
)

# Register knowledge group with main app
app.add_typer(knowledge_app, name="knowledge")

# Knowledge collections subgroup
knowledge_collections_app = typer.Typer(
    help="Manage document collections",
    no_args_is_help=True,
)

# Register collections subgroup with knowledge app
knowledge_app.add_typer(knowledge_collections_app, name="collections")

# Register knowledge collection commands
knowledge_collections_app.command("create")(knowledge_collections_create_command)
knowledge_collections_app.command("delete")(knowledge_collections_delete_command)
knowledge_collections_app.command("get")(knowledge_collections_get_command)
knowledge_collections_app.command("list")(knowledge_collections_list_command)
knowledge_collections_app.command("update")(knowledge_collections_update_command)
knowledge_collections_app.command("cluster")(knowledge_collections_cluster_command)
knowledge_collections_app.command("cluster-status")(
    knowledge_collections_cluster_status_command
)
knowledge_collections_app.command("status")(knowledge_collections_status_command)

# Knowledge documents subgroup
knowledge_documents_app = typer.Typer(
    help="Manage documents in collections",
    no_args_is_help=True,
)

# Register documents subgroup with knowledge app
knowledge_app.add_typer(knowledge_documents_app, name="documents")

# Register knowledge document commands
knowledge_documents_app.command("delete")(knowledge_documents_delete_command)
knowledge_documents_app.command("get")(knowledge_documents_get_command)
knowledge_documents_app.command("ingest")(knowledge_documents_ingest_command)
knowledge_documents_app.command("list")(knowledge_documents_list_command)
knowledge_documents_app.command("count")(knowledge_documents_count_command)
knowledge_documents_app.command("update")(knowledge_documents_update_command)
knowledge_documents_app.command("reingest")(knowledge_documents_reingest_command)
knowledge_documents_app.command("reingest-failed")(
    knowledge_documents_bulk_reingest_failed_command
)
knowledge_documents_app.command("reingest-failed-status")(
    knowledge_documents_bulk_reingest_failed_status_command
)
knowledge_documents_app.command("bulk-metadata")(
    knowledge_documents_bulk_metadata_command
)

# Knowledge synthesis subgroup
knowledge_synthesis_app = typer.Typer(
    help="Manage corpus-level syntheses",
    no_args_is_help=True,
)

# Register synthesis subgroup with knowledge app
knowledge_app.add_typer(knowledge_synthesis_app, name="synthesis")

# Register knowledge synthesis commands
knowledge_synthesis_app.command("create")(knowledge_synthesis_create_command)
knowledge_synthesis_app.command("delete")(knowledge_synthesis_delete_command)
knowledge_synthesis_app.command("get")(knowledge_synthesis_get_command)
knowledge_synthesis_app.command("list")(knowledge_synthesis_list_command)
knowledge_synthesis_app.command("refresh")(knowledge_synthesis_refresh_command)
knowledge_synthesis_app.command("update")(knowledge_synthesis_update_command)

# Register top-level knowledge commands
knowledge_app.command("recover")(knowledge_recover_command)
knowledge_app.command("search")(knowledge_search_command)

# Tool configs command group
tool_configs_app = typer.Typer(
    help="Manage reusable tool configurations",
    no_args_is_help=True,
)

# Register tool-configs group with main app
app.add_typer(tool_configs_app, name="tool-configs")

# Register tool-configs commands
tool_configs_app.command("create")(tool_configs_create_command)
tool_configs_app.command("delete")(tool_configs_delete_command)
tool_configs_app.command("get")(tool_configs_get_command)
tool_configs_app.command("list")(tool_configs_list_command)
tool_configs_app.command("update")(tool_configs_update_command)

executions_app = typer.Typer(
    help="Inspect model executions",
    no_args_is_help=True,
)

app.add_typer(executions_app, name="executions")

executions_app.command("get")(executions_get_command)

# Pipelines command group
pipelines_app = typer.Typer(
    help="Manage and execute pipelines",
    no_args_is_help=True,
)

# Register pipelines group with main app
app.add_typer(pipelines_app, name="pipelines")

# Register pipeline commands
pipelines_app.command("list")(pipelines_list_command)
pipelines_app.command("get")(pipelines_get_command)
pipelines_app.command("execute")(pipelines_execute_command)
pipelines_app.command("costs")(pipelines_costs_command)
pipelines_app.command("costs-daily")(pipeline_costs_daily_command)
pipelines_app.command("create")(pipelines_create_command)
pipelines_app.command("update", help="Update an existing pipeline")(
    pipelines_update_command
)
pipelines_app.command("env")(pipelines_env_vars_command)

# Pipelines executions subgroup
pipelines_executions_app = typer.Typer(
    help="Manage pipeline executions",
    no_args_is_help=True,
)

# Register executions subgroup with pipelines app
pipelines_app.add_typer(pipelines_executions_app, name="executions")

# Register pipeline execution commands
pipelines_executions_app.command("list")(pipelines_executions_list_command)
pipelines_executions_app.command("get")(pipelines_executions_get_command)
pipelines_executions_app.command("rerun")(pipelines_executions_rerun_command)
pipelines_executions_app.command("update")(pipeline_execution_update_command)
pipelines_executions_app.command("costs")(pipeline_execution_costs_command)
pipelines_executions_app.command("costs-by-model")(
    pipeline_execution_costs_by_model_command
)

# Pipelines schedules subgroup
pipelines_schedules_app = typer.Typer(
    help="Manage pipeline schedules",
    no_args_is_help=True,
)

# Register schedules subgroup with pipelines app
pipelines_app.add_typer(pipelines_schedules_app, name="schedules")

# Register pipeline schedule commands
pipelines_schedules_app.command("list")(pipelines_schedules_list_command)
pipelines_schedules_app.command("get")(pipelines_schedules_get_command)
pipelines_schedules_app.command("delete")(pipelines_schedules_delete_command)
pipelines_schedules_app.command("env")(pipelines_schedules_env_vars_command)
pipelines_schedules_app.command("create")(pipelines_schedules_create_command)
pipelines_schedules_app.command("update")(pipelines_schedules_update_command)
pipelines_schedules_app.command("once")(pipelines_schedules_once_command)


# Pipelines approvals subgroup
pipelines_approvals_app = typer.Typer(
    help="Manage approval checkpoints for human-in-the-loop pipelines",
    no_args_is_help=True,
)

# Register approvals subgroup with pipelines app
pipelines_app.add_typer(pipelines_approvals_app, name="approvals")

# Register pipeline approval commands
pipelines_approvals_app.command("get")(pipelines_approvals_get_command)
pipelines_approvals_app.command("decide")(pipelines_approvals_decide_command)

# Sessions command group
sessions_app = typer.Typer(
    help="Manage chat sessions",
    no_args_is_help=True,
)

# Register sessions group with main app
app.add_typer(sessions_app, name="sessions")

# Register session commands
sessions_app.command("list")(sessions_list_command)
sessions_app.command("get")(sessions_get_command)
sessions_app.command("delete")(sessions_delete_command)
sessions_app.command("messages")(sessions_messages_command)

# Tags command group
tags_app = typer.Typer(
    help="Manage tags for organizing content",
    no_args_is_help=True,
)

# Register tags group with main app
app.add_typer(tags_app, name="tags")

# Register tag commands
tags_app.command("list")(tags_list_command)
tags_app.command("create")(tags_create_command)
tags_app.command("delete")(tags_delete_command)
tags_app.command("get")(tags_get_command)
tags_app.command("update")(tags_update_command)

# Billing command group
billing_app = typer.Typer(
    help="Manage billing projects and costs",
    no_args_is_help=True,
)

app.add_typer(billing_app, name="billing")

# Billing projects subgroup
billing_projects_app = typer.Typer(
    help="Manage billing projects",
    no_args_is_help=True,
)

billing_app.add_typer(billing_projects_app, name="projects")

billing_projects_app.command("create")(billing_projects_create_command)
billing_projects_app.command("list")(billing_projects_list_command)
billing_projects_app.command("get")(billing_projects_get_command)

# Billing costs subgroup
billing_costs_app = typer.Typer(
    help="View billing costs",
    no_args_is_help=True,
)

billing_app.add_typer(billing_costs_app, name="costs")

billing_costs_app.command("summary")(billing_costs_summary_command)
billing_costs_app.command("daily")(billing_costs_daily_command)
billing_costs_app.command("by-agent")(billing_costs_by_agent_command)
billing_costs_app.command("by-model")(billing_costs_by_model_command)

# Tools command group
tools_app = typer.Typer(
    help="List and inspect available tools",
    no_args_is_help=True,
)

app.add_typer(tools_app, name="tools")

tools_app.command("list")(tools_list_command)
tools_app.command("get")(tools_get_command)

# Audio command group
audio_app = typer.Typer(
    help="Audio processing commands",
    no_args_is_help=True,
)

app.add_typer(audio_app, name="audio")

audio_app.command("transcribe")(audio_transcribe_command)

# Organizations command group
organizations_app = typer.Typer(
    help="Manage organizations",
    no_args_is_help=True,
)

app.add_typer(organizations_app, name="organizations")

organizations_app.command("create")(organizations_create_command)

# Users command group
users_app = typer.Typer(
    help="Manage users",
    no_args_is_help=True,
)

app.add_typer(users_app, name="users")

users_app.command("create")(users_create_command)


app.command("tui", help="Open Textual TUI.")(_launch_command_tui)


def main() -> None:
    """CLI entry point."""
    # Configure logging for CLI (logs to file, Rich console for user output)
    log_level_str = os.environ.get("LOG_LEVEL", "warning").lower()
    log_level = LogLevel(log_level_str)

    # Determine log file path - use root logs directory
    # From cli/main.py -> cli -> packages -> core-service
    project_root = Path(__file__).parent.parent.parent.parent
    log_dir = project_root / "logs"
    log_file = log_dir / "cli.log"

    configure(
        LogConfig(
            level=log_level,
            format=LogFormat.JSON,  # Always JSON for parsability
            service_name="alloy-runtime-cli",
            output_file=str(log_file),
            capture_stdlib_logging=True,
        )
    )

    app()


if __name__ == "__main__":
    main()
