"""Action management commands for CleverAgents CLI.

The ``agents action`` command group manages actions (reusable plan templates)
in the CleverAgents v3 plan lifecycle.

## Commands

| Command                 | Description                              |
|-------------------------|------------------------------------------|
| ``agents action create``| Create action from YAML config file      |
| ``agents action list``  | List actions with optional filters       |
| ``agents action show``  | Show full action details                 |
| ``agents action archive``| Soft-delete (transition to ``archived``)|

## Config-Only Create

Actions are created **exclusively** via a YAML configuration file:

```bash
agents action create --config ./actions/code-coverage.yaml
```

Legacy inline flags (``--name``, ``--strategy-actor``, etc.) are **not
supported** and will be rejected with an error.

### YAML Configuration File

```yaml
name: local/code-coverage
description: Increase code coverage
strategy_actor: openai/gpt-4
execution_actor: openai/gpt-4
definition_of_done: |
  Line coverage reaches the target percentage
  for all modules under src/.
```

## Error Handling

| Error                      | Cause                                    |
|----------------------------|------------------------------------------|
| ``Config file error``      | File not found or not readable           |
| ``Schema validation error``| YAML does not match ActionConfigSchema   |
| ``Validation Error``       | Business rule violation                  |

Based on v3_spec.md implementation plan Stage A4b.
"""

from __future__ import annotations

import re
from pathlib import Path
from typing import TYPE_CHECKING, Annotated

import typer
from pydantic import ValidationError as PydanticValidationError
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

if TYPE_CHECKING:
    from cleveragents.application.services.plan_lifecycle_service import (
        PlanLifecycleService,
    )

from cleveragents.application.container import get_container
from cleveragents.cli.formatting import OutputFormat, format_output
from cleveragents.core.exceptions import (
    CleverAgentsError,
    NotFoundError,
    ValidationError,
)
from cleveragents.domain.models.core.action import Action

# Create sub-app for action commands
app = typer.Typer(
    help="Manage actions (reusable plan templates) for the v3 plan lifecycle."
)
console = Console()

# Reusable --format option description
_FORMAT_HELP = "Output format: json, yaml, plain, table, or rich (default: rich)"


def _get_lifecycle_service() -> PlanLifecycleService:
    """Get the PlanLifecycleService from the container.

    Uses the container's registered provider which includes UnitOfWork
    for database persistence across subprocess invocations.
    """
    container = get_container()
    return container.plan_lifecycle_service()


def _action_spec_dict(action: Action) -> dict[str, object]:
    """Return action data using spec field names.

    Keys: namespaced_name, short_name, state, description,
    definition_of_done, strategy_actor, execution_actor,
    estimation_actor, invariant_actor, automation_profile,
    arguments, inputs_schema, invariants.
    """
    result: dict[str, object] = {
        "namespaced_name": str(action.namespaced_name),
        "short_name": action.namespaced_name.name,
        "state": action.state.value,
        "description": action.description,
        "definition_of_done": action.definition_of_done,
        "strategy_actor": action.strategy_actor,
        "execution_actor": action.execution_actor,
    }
    if action.estimation_actor:
        result["estimation_actor"] = action.estimation_actor
    if action.invariant_actor:
        result["invariant_actor"] = action.invariant_actor
    result["automation_profile"] = action.automation_profile
    result["arguments"] = [
        {
            "name": arg.name,
            "type": arg.arg_type.value,
            "required": arg.requirement.value == "required",
            "description": arg.description,
        }
        for arg in action.arguments
    ]
    if action.inputs_schema:
        result["inputs_schema"] = action.inputs_schema
    result["invariants"] = action.invariants
    result["reusable"] = action.reusable
    result["read_only"] = action.read_only
    result["created_at"] = action.created_at.isoformat()
    return result


def _print_action(
    action: Action,
    title: str = "Action",
    fmt: str = OutputFormat.RICH.value,
) -> None:
    """Print action details in the requested format."""
    if fmt != OutputFormat.RICH.value:
        data = _action_spec_dict(action)
        console.print(format_output(data, fmt))
        return

    # Rich panel (original behaviour)
    args_display = ""
    if action.arguments:
        args_lines: list[str] = []
        for arg in action.arguments:
            req = "required" if arg.requirement.value == "required" else "optional"
            args_lines.append(f"  • {arg.name} ({arg.arg_type.value}, {req})")
            if arg.description:
                args_lines.append(f"    {arg.description}")
        args_display = "\n".join(args_lines)
    else:
        args_display = "  (none)"

    dod_summary = action.definition_of_done
    if len(dod_summary) > 120:
        dod_summary = dod_summary[:117] + "..."

    details = (
        f"[bold]Namespaced Name:[/bold] {action.namespaced_name}\n"
        f"[bold]Short Name:[/bold] {action.namespaced_name.name}\n"
        f"[bold]Description:[/bold] {action.description}\n"
        f"[bold]State:[/bold] {action.state.value}\n"
        f"[bold]Strategy Actor:[/bold] {action.strategy_actor}\n"
        f"[bold]Execution Actor:[/bold] {action.execution_actor}\n"
    )

    # Optional actors
    if action.estimation_actor:
        details += f"[bold]Estimation Actor:[/bold] {action.estimation_actor}\n"
    if action.invariant_actor:
        details += f"[bold]Invariant Actor:[/bold] {action.invariant_actor}\n"

    details += (
        f"[bold]Reusable:[/bold] {'yes' if action.reusable else 'no'}\n"
        f"[bold]Read Only:[/bold] {'yes' if action.read_only else 'no'}\n"
        f"[bold]Definition of Done:[/bold]\n  {dod_summary}\n"
        f"[bold]Arguments:[/bold]\n{args_display}\n"
    )

    # Invariants
    if action.invariants:
        inv_lines = "\n".join(f"  • {inv}" for inv in action.invariants)
        details += f"[bold]Invariants:[/bold]\n{inv_lines}\n"

    # Inputs schema
    if action.inputs_schema:
        import json

        schema_str = json.dumps(action.inputs_schema, indent=2)
        details += f"[bold]Inputs Schema:[/bold]\n  {schema_str}\n"

    # Automation profile
    if action.automation_profile:
        details += f"[bold]Automation Profile:[/bold] {action.automation_profile}\n"

    details += f"[bold]Created:[/bold] {action.created_at}"

    console.print(Panel(details, title=title, expand=False))


@app.command()
def create(
    config: Annotated[
        Path,
        typer.Option(
            "--config",
            "-c",
            help="Path to the action YAML configuration file",
            exists=False,
        ),
    ],
    fmt: Annotated[str, typer.Option("--format", "-f", help=_FORMAT_HELP)] = "rich",
) -> None:
    """Create a new action from a YAML configuration file.

    Actions are created ONLY via ``--config <file>``. Inline flags such as
    ``--name``, ``--strategy-actor``, ``--execution-actor``,
    ``--definition-of-done``, and ``--arg`` are not supported.

    Examples:
        agents action create --config ./actions/code-coverage.yaml
        agents action create --config ./actions/code-coverage.yaml --format json
    """
    try:
        from cleveragents.action.schema import ActionConfigSchema

        # Load and validate config via ActionConfigSchema
        schema = ActionConfigSchema.from_yaml_file(config)

        # Convert to Action via from_config
        config_dict = schema.model_dump()
        action = Action.from_config(config_dict)

        service = _get_lifecycle_service()

        # Register the action via the lifecycle service
        action = service.create_action(
            name=str(action.namespaced_name),
            definition_of_done=action.definition_of_done,
            strategy_actor=action.strategy_actor,
            execution_actor=action.execution_actor,
            description=action.description,
            long_description=action.long_description,
            arguments=action.arguments,
            reusable=action.reusable,
            read_only=action.read_only,
            tags=action.tags,
        )

        _print_action(action, title="Action Created", fmt=fmt)

    except FileNotFoundError as e:
        console.print(f"[red]Config file error:[/red] {e}")
        raise typer.Abort() from e
    except PydanticValidationError as e:
        console.print(f"[red]Schema validation error:[/red] {e}")
        raise typer.Abort() from e
    except ValueError as e:
        console.print(f"[red]Config validation error:[/red] {e}")
        raise typer.Abort() from e
    except ValidationError as e:
        console.print(f"[red]Validation Error:[/red] {e.message}")
        raise typer.Abort() from e
    except CleverAgentsError as e:
        console.print(f"[red]Error:[/red] {e.message}")
        raise typer.Abort() from e


@app.command("list")
def list_actions(
    namespace: Annotated[
        str | None,
        typer.Option(
            "--namespace",
            "-n",
            help="Filter by namespace",
        ),
    ] = None,
    state: Annotated[
        str | None,
        typer.Option(
            "--state",
            "-s",
            help="Filter by state (available, archived)",
        ),
    ] = None,
    regex: Annotated[
        str | None,
        typer.Argument(
            help="Optional regex pattern to filter action names",
        ),
    ] = None,
    fmt: Annotated[
        str,
        typer.Option(
            "--format",
            "-f",
            help=_FORMAT_HELP,
        ),
    ] = "rich",
) -> None:
    """List all actions.

    Shows actions with optional filtering by namespace, state, and name regex.

    Examples:
        agents action list
        agents action list --namespace local
        agents action list --state available
        agents action list "code-.*"
        agents action list --format json
    """
    try:
        from cleveragents.domain.models.core.action import ActionState

        service = _get_lifecycle_service()

        # Parse state filter
        state_filter = None
        if state:
            try:
                state_filter = ActionState(state.lower())
            except ValueError as exc:
                console.print(
                    f"[red]Invalid state:[/red] {state}. "
                    "Valid values: available, archived"
                )
                raise typer.Abort() from exc

        actions = service.list_actions(namespace=namespace, state=state_filter)

        # Apply regex filter if provided
        if regex:
            try:
                pattern = re.compile(regex)
            except re.error as exc:
                console.print(f"[red]Invalid regex pattern:[/red] {regex}")
                raise typer.Abort() from exc
            actions = [
                a
                for a in actions
                if pattern.search(str(a.namespaced_name))
                or pattern.search(a.namespaced_name.name)
            ]

        if not actions:
            console.print("[yellow]No actions found.[/yellow]")
            console.print("Create one with 'agents action create --config <file>'")
            return

        # Non-rich formats use the formatting helper
        if fmt != OutputFormat.RICH.value:
            data = [_action_spec_dict(a) for a in actions]
            console.print(format_output(data, fmt))
            return

        # Display actions table (rich default)
        table = Table(title=f"Actions ({len(actions)} total)")
        table.add_column("Namespaced Name", style="cyan")
        table.add_column("Short Name", style="blue")
        table.add_column("State", style="yellow")
        table.add_column("Strategy Actor", style="magenta")
        table.add_column("Execution Actor", style="magenta")
        table.add_column("Definition of Done", style="dim")
        table.add_column("Reusable", justify="center")
        table.add_column("Created", style="green")

        for action in actions:
            # Truncate definition of done for table display
            dod = action.definition_of_done
            if len(dod) > 40:
                dod = dod[:37] + "..."

            table.add_row(
                str(action.namespaced_name),
                action.namespaced_name.name,
                action.state.value,
                action.strategy_actor,
                action.execution_actor,
                dod,
                "✓" if action.reusable else "",
                str(action.created_at.strftime("%Y-%m-%d %H:%M")),
            )

        console.print(table)

    except CleverAgentsError as e:
        console.print(f"[red]Error:[/red] {e.message}")
        raise typer.Abort() from e


@app.command()
def show(
    name: Annotated[
        str,
        typer.Argument(help="Namespaced name of the action to show"),
    ],
    fmt: Annotated[
        str,
        typer.Option(
            "--format",
            "-f",
            help=_FORMAT_HELP,
        ),
    ] = "rich",
) -> None:
    """Show details for an action.

    Specify the namespaced name (e.g. 'local/code-coverage').
    """
    try:
        service = _get_lifecycle_service()

        action = service.get_action_by_name(name)

        _print_action(action, title="Action Details", fmt=fmt)

    except NotFoundError as e:
        console.print(f"[red]Action not found:[/red] {name}")
        raise typer.Abort() from e
    except CleverAgentsError as e:
        console.print(f"[red]Error:[/red] {e.message}")
        raise typer.Abort() from e


@app.command()
def archive(
    name: Annotated[
        str,
        typer.Argument(help="Namespaced name of the action to archive"),
    ],
    fmt: Annotated[
        str,
        typer.Option(
            "--format",
            "-f",
            help=_FORMAT_HELP,
        ),
    ] = "rich",
) -> None:
    """Archive an action (soft delete).

    Archived actions cannot be used but are preserved for history.
    """
    try:
        service = _get_lifecycle_service()

        action = service.get_action_by_name(name)
        action = service.archive_action(str(action.namespaced_name))

        if fmt != OutputFormat.RICH.value:
            data = _action_spec_dict(action)
            data["archived"] = True
            console.print(format_output(data, fmt))
            return

        console.print(f"[green]✓[/green] Action archived: {action.namespaced_name}")

    except NotFoundError as e:
        console.print(f"[red]Action not found:[/red] {name}")
        raise typer.Abort() from e
    except CleverAgentsError as e:
        console.print(f"[red]Error:[/red] {e.message}")
        raise typer.Abort() from e
