"""Repository indexing CLI commands for CleverAgents.

The ``agents repo`` command group manages repository indexing:

| Command               | Description                              |
|-----------------------|------------------------------------------|
| ``agents repo index`` | Index or re-index a repository resource  |
| ``agents repo status``| Show indexing status for a resource      |

## Example Usage

```bash
# Index a resource (incremental by default)
agents repo index local/my-repo

# Force full re-index
agents repo index local/my-repo --full

# Check indexing status
agents repo status local/my-repo

# JSON output
agents repo status local/my-repo --format json
```

Wires the existing :class:`RepoIndexingService` backend to the CLI layer.

Based on issue #856: feat(cli): repo indexing CLI functional.
"""

from __future__ import annotations

import logging
from typing import Annotated

import typer
from rich.console import Console
from rich.panel import Panel

from cleveragents.application.container import get_container
from cleveragents.cli.formatting import format_output
from cleveragents.core.exceptions import NotFoundError

logger = logging.getLogger(__name__)

app = typer.Typer(
    help="Repository indexing management.",
)

console = Console()

_FORMAT_HELP = "Output format: text or json (default: text)"


def _resolve_resource(
    resource_name: str,
) -> tuple[str, str]:
    """Resolve a resource name to (resource_id, root_path).

    Uses the ``ResourceRegistryService`` to look up the resource by
    namespaced name or ULID, then extracts the filesystem path from
    the resource's ``location`` or ``properties["path"]``.

    Returns:
        Tuple of (resource_id, root_path).

    Raises:
        typer.Exit: If the resource is not found or has no path.
    """
    container = get_container()
    registry_service = container.resource_registry_service()

    try:
        resource = registry_service.show_resource(resource_name)
    except NotFoundError as exc:
        console.print(f"[red]Error:[/red] Resource not found: {resource_name}")
        raise typer.Exit(code=1) from exc

    # Extract filesystem path from location or properties
    root_path = resource.location
    if not root_path and resource.properties:
        path_val = resource.properties.get("path")
        root_path = str(path_val) if path_val is not None else None

    if not root_path:
        console.print(
            f"[red]Error:[/red] Resource '{resource_name}' has no filesystem path"
        )
        raise typer.Exit(code=1)

    return resource.resource_id, root_path


@app.command("index")
def index_command(
    resource_name: Annotated[
        str,
        typer.Argument(help="Resource name or ULID to index (e.g., local/my-repo)"),
    ],
    full: Annotated[
        bool,
        typer.Option("--full", help="Force full re-index (not incremental)"),
    ] = False,
    timeout_seconds: Annotated[
        float | None,
        typer.Option(
            "--timeout-seconds",
            help=("Abort indexing when elapsed runtime exceeds this many seconds"),
        ),
    ] = None,
    fmt: Annotated[
        str,
        typer.Option("--format", "-f", help=_FORMAT_HELP),
    ] = "text",
) -> None:
    """Index or re-index a repository resource.

    By default performs an incremental refresh (only changed files).
    Use ``--full`` to force a complete re-index.

    Examples:
        agents repo index local/my-repo
        agents repo index local/my-repo --full
        agents repo index local/my-repo --format json
    """
    resource_id, root_path = _resolve_resource(resource_name)

    container = get_container()
    indexing_service = container.repo_indexing_service()

    try:
        if full:
            repo_index = indexing_service.index_resource(
                resource_id,
                root_path,
                timeout_seconds=timeout_seconds,
            )
        else:
            repo_index = indexing_service.refresh_index(
                resource_id,
                root_path,
                timeout_seconds=timeout_seconds,
            )
    except TimeoutError as exc:
        console.print(f"[red]Error:[/red] {exc}")
        raise typer.Exit(code=1) from exc
    except FileNotFoundError as exc:
        console.print(f"[red]Error:[/red] {exc}")
        raise typer.Exit(code=1) from exc
    except ValueError as exc:
        console.print(f"[red]Error:[/red] {exc}")
        raise typer.Exit(code=1) from exc

    metadata = repo_index.metadata

    if fmt.lower() == "json":
        data: dict[str, object] = {
            "resource": resource_name,
            "resource_id": metadata.resource_id,
            "index_id": metadata.index_id,
            "status": metadata.status.value,
            "file_count": metadata.file_count,
            "token_estimate": metadata.token_estimate,
            "primary_language": metadata.primary_language,
            "indexed_at": metadata.indexed_at.isoformat(),
            "mode": "full" if full else "incremental",
        }
        console.print(format_output(data, "json"))
        return

    # Text output
    mode_label = "Full index" if full else "Incremental refresh"
    console.print(f"[green]{mode_label} complete:[/green] {resource_name}")
    details = (
        f"[bold]Status:[/bold] {metadata.status.value}\n"
        f"[bold]Files:[/bold] {metadata.file_count}\n"
        f"[bold]Tokens:[/bold] {metadata.token_estimate:,}\n"
        f"[bold]Language:[/bold] {metadata.primary_language}\n"
        f"[bold]Indexed at:[/bold] {metadata.indexed_at.isoformat()}"
    )
    console.print(Panel(details, title="Index Result", expand=False))


@app.command("status")
def status_command(
    resource_name: Annotated[
        str,
        typer.Argument(
            help="Resource name or ULID to check status (e.g., local/my-repo)"
        ),
    ],
    fmt: Annotated[
        str,
        typer.Option("--format", "-f", help=_FORMAT_HELP),
    ] = "text",
) -> None:
    """Show indexing status for a repository resource.

    Displays index metadata including file count, token estimate,
    primary language, status, and last indexed timestamp.

    Examples:
        agents repo status local/my-repo
        agents repo status local/my-repo --format json
    """
    resource_id, _root_path = _resolve_resource(resource_name)

    container = get_container()
    indexing_service = container.repo_indexing_service()

    metadata = indexing_service.get_index_status(resource_id)

    if metadata is None:
        if fmt.lower() == "json":
            data: dict[str, object] = {
                "resource": resource_name,
                "status": "not_indexed",
                "message": "No index found for this resource",
            }
            console.print(format_output(data, "json"))
        else:
            console.print(
                f"[yellow]No index found for resource:[/yellow] {resource_name}"
            )
        return

    if fmt.lower() == "json":
        data = {
            "resource": resource_name,
            "resource_id": metadata.resource_id,
            "index_id": metadata.index_id,
            "status": metadata.status.value,
            "file_count": metadata.file_count,
            "token_estimate": metadata.token_estimate,
            "primary_language": metadata.primary_language,
            "indexed_at": metadata.indexed_at.isoformat(),
        }
        if metadata.error_message:
            data["error_message"] = metadata.error_message
        console.print(format_output(data, "json"))
        return

    # Text output
    console.print(f"[bold]Index status for:[/bold] {resource_name}")
    error_line = ""
    if metadata.error_message:
        error_line = f"\n[bold]Error:[/bold] {metadata.error_message}"
    details = (
        f"[bold]Status:[/bold] {metadata.status.value}\n"
        f"[bold]Files:[/bold] {metadata.file_count}\n"
        f"[bold]Tokens:[/bold] {metadata.token_estimate:,}\n"
        f"[bold]Language:[/bold] {metadata.primary_language}\n"
        f"[bold]Indexed at:[/bold] {metadata.indexed_at.isoformat()}"
        f"{error_line}"
    )
    console.print(Panel(details, title="Index Status", expand=False))
