"""Simplified Command builder - string-based and function-based support."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Callable, Any, cast, TYPE_CHECKING

if TYPE_CHECKING:
    from hil_testbench.task.specs import CommandValidator

from hil_testbench.data_structs.hosts import HostDefinition


@dataclass
class Command:
    """Simplified command definition with string and function support.

    Examples:
        # String-based (most common)
        Command("iperf3 -s -p 5201", host=server)

        # Function-based (for complex logic)
        Command(run=lambda c: c.run("echo hello"), host=server)

        # With explicit name
        Command("iperf3 -c server", host=client, name="iperf_client")

        # With validator
        Command("iperf3 -c server", host=client, validator=IperfValidator())
    """

    # Command specification (one of these must be provided)
    cmd: str | None = None  # String command
    run: Callable[[Any], Any] | None = None  # Function: (ExecutionContext) -> Any

    # Execution context
    host: HostDefinition | None = None
    name: str | None = None  # Auto-generated if None
    env: dict[str, str] = field(default_factory=dict)
    cwd: str | None = None
    use_pty: bool = False
    immediate: bool = False
    retry: int = 0

    # Validation
    validator: CommandValidator | None = None  # Custom validation logic

    def __post_init__(self):
        """Validate and auto-generate name."""
        if self.cmd is None and self.run is None:
            raise ValueError("Either 'cmd' or 'run' must be specified")

        if self.cmd and self.run:
            raise ValueError("Cannot specify both 'cmd' and 'run'")

        # Auto-generate name if not provided
        if self.name is None:
            self.name = self._generate_name()

    def _generate_name(self) -> str:
        """Generate name from command string or use generic name."""
        if self.cmd:
            # Extract binary name from command string
            # "iperf3 -s -p 5201" -> "iperf3"
            parts = self.cmd.strip().split()
            if parts:
                binary = parts[0].split("/")[-1]  # Handle paths
                return binary
        return "command"

    def to_command_definition(self):
        """Convert to old CommandDefinition format for compatibility."""
        from hil_testbench.task.specs import (  # hil: allow-lazy - avoid circular import
            CommandDefinition,
        )

        if self.cmd:
            # String command - wrap in lambda
            run_func = lambda c: c.run(self.cmd)  # noqa: E731
        else:
            # Function command - use directly
            run_func = cast(Callable[[Any], Any], self.run)
        name = self.name if self.name is not None else self._generate_name()

        return CommandDefinition(
            name=name,
            run=run_func,
            host=self.host,
            use_pty=self.use_pty,
            immediate=self.immediate,
            retry=self.retry,
            env=self.env,
            cwd=self.cwd,
            validator=self.validator,
        )
