"""Command request/response models."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime
from enum import Enum
from typing import Any


class CommandStatus(str, Enum):
    """Command execution status."""

    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"
    TIMEOUT = "timeout"
    REJECTED = "rejected"


class CommandType(str, Enum):
    """Types of commands."""

    SHELL = "shell"
    SCRIPT = "script"
    RESTART = "restart"
    CONFIG = "config"
    CUSTOM = "custom"


@dataclass
class CommandRequest:
    """
    Command received from the server.

    Represents a remediation action to be executed on the agent.
    """

    id: str
    command: str
    command_type: CommandType = CommandType.SHELL
    args: list[str] = field(default_factory=list)
    env: dict[str, str] = field(default_factory=dict)
    timeout: float = 300.0
    working_dir: str | None = None
    signature: str | None = None
    signature_algorithm: str = "hmac-sha256"
    key_id: str | None = None
    created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
    metadata: dict[str, Any] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> CommandRequest:
        """Create from dictionary."""
        created_at = data.get("created_at")
        if isinstance(created_at, str):
            created_at = datetime.fromisoformat(created_at)
        elif created_at is None:
            created_at = datetime.now(UTC)

        command_type = data.get("command_type", "shell")
        if isinstance(command_type, str):
            command_type = CommandType(command_type)

        return cls(
            id=data["id"],
            command=data["command"],
            command_type=command_type,
            args=data.get("args", []),
            env=data.get("env", {}),
            timeout=data.get("timeout", 300.0),
            working_dir=data.get("working_dir"),
            signature=data.get("signature"),
            signature_algorithm=data.get("signature_algorithm", "hmac-sha256"),
            key_id=data.get("key_id"),
            created_at=created_at,
            metadata=data.get("metadata", {}),
        )

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "id": self.id,
            "command": self.command,
            "command_type": self.command_type.value,
            "args": self.args,
            "env": self.env,
            "timeout": self.timeout,
            "working_dir": self.working_dir,
            "signature": self.signature,
            "signature_algorithm": self.signature_algorithm,
            "key_id": self.key_id,
            "created_at": self.created_at.isoformat(),
            "metadata": self.metadata,
        }

    def get_signature_data(self) -> bytes:
        """Get the data used for signature verification."""
        # Signature covers: id + command + args + timeout
        parts = [
            self.id,
            self.command,
            ",".join(self.args),
            str(self.timeout),
        ]
        return "|".join(parts).encode("utf-8")


@dataclass
class CommandResponse:
    """
    Result of command execution.

    Sent back to the server after command completion.
    """

    command_id: str
    status: CommandStatus
    exit_code: int | None = None
    stdout: str = ""
    stderr: str = ""
    started_at: datetime | None = None
    completed_at: datetime | None = None
    duration_ms: float = 0.0
    error: str | None = None
    truncated: bool = False

    @property
    def success(self) -> bool:
        """Check if command completed successfully."""
        return self.status == CommandStatus.COMPLETED and self.exit_code == 0

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for API."""
        return {
            "command_id": self.command_id,
            "status": self.status.value,
            "exit_code": self.exit_code,
            "stdout": self.stdout,
            "stderr": self.stderr,
            "started_at": self.started_at.isoformat() if self.started_at else None,
            "completed_at": self.completed_at.isoformat() if self.completed_at else None,
            "duration_ms": self.duration_ms,
            "error": self.error,
            "truncated": self.truncated,
        }

    @classmethod
    def rejected(
        cls,
        command_id: str,
        reason: str,
    ) -> CommandResponse:
        """Create a rejected response."""
        return cls(
            command_id=command_id,
            status=CommandStatus.REJECTED,
            error=reason,
        )

    @classmethod
    def failed(
        cls,
        command_id: str,
        error: str,
        started_at: datetime | None = None,
    ) -> CommandResponse:
        """Create a failed response."""
        return cls(
            command_id=command_id,
            status=CommandStatus.FAILED,
            error=error,
            started_at=started_at,
            completed_at=datetime.now(UTC),
        )

    @classmethod
    def timeout(
        cls,
        command_id: str,
        started_at: datetime,
        partial_stdout: str = "",
        partial_stderr: str = "",
    ) -> CommandResponse:
        """Create a timeout response."""
        return cls(
            command_id=command_id,
            status=CommandStatus.TIMEOUT,
            stdout=partial_stdout,
            stderr=partial_stderr,
            started_at=started_at,
            completed_at=datetime.now(UTC),
            error="Command timed out",
            truncated=True,
        )
