"""
koci Container Runtime Interface

Abstract interface for container runtimes (Docker, Podman, containerd).
"""

import shutil
from dataclasses import dataclass, field
from typing import Optional, Iterator, Protocol, runtime_checkable
from enum import Enum


class RuntimeType(Enum):
    """Supported container runtime types."""
    DOCKER = "docker"
    PODMAN = "podman"
    CONTAINERD = "containerd"


@dataclass
class ContainerInfo:
    """Information about a running container."""
    id: str
    name: str
    image: str
    status: str
    runtime: RuntimeType
    ports: dict[str, str] = field(default_factory=dict)
    labels: dict[str, str] = field(default_factory=dict)


@dataclass
class ExecResult:
    """Result of executing a command in a container."""
    exit_code: int
    stdout: str
    stderr: str

    @property
    def success(self) -> bool:
        return self.exit_code == 0

    @property
    def output(self) -> str:
        """Combined stdout and stderr."""
        return self.stdout + self.stderr


@dataclass
class ContainerConfig:
    """Configuration for creating a container."""
    image: str
    name: Optional[str] = None
    command: Optional[list[str]] = None
    entrypoint: Optional[list[str]] = None
    env: dict[str, str] = field(default_factory=dict)
    volumes: dict[str, dict] = field(default_factory=dict)  # {host_path: {bind: container_path, mode: rw}}
    ports: dict[str, str] = field(default_factory=dict)  # {container_port: host_port}
    working_dir: str = "/workspace"
    network: Optional[str] = None
    labels: dict[str, str] = field(default_factory=dict)
    user: Optional[str] = None
    privileged: bool = False
    auto_remove: bool = False
    detach: bool = True


@runtime_checkable
class ContainerRuntime(Protocol):
    """
    Protocol defining the container runtime interface.

    All runtime implementations (Docker, Podman, etc.) must implement this.
    """

    @property
    def runtime_type(self) -> RuntimeType:
        """Get the runtime type."""
        ...

    def is_available(self) -> bool:
        """Check if the runtime is available on this system."""
        ...

    def pull_image(self, image: str, quiet: bool = False) -> bool:
        """
        Pull a container image.

        Args:
            image: Image name with optional tag
            quiet: Suppress output

        Returns:
            True if successful
        """
        ...

    def image_exists(self, image: str) -> bool:
        """Check if an image exists locally."""
        ...

    def create_container(self, config: ContainerConfig) -> str:
        """
        Create a new container.

        Args:
            config: Container configuration

        Returns:
            Container ID
        """
        ...

    def start_container(self, container_id: str) -> bool:
        """
        Start a container.

        Args:
            container_id: Container ID

        Returns:
            True if successful
        """
        ...

    def stop_container(self, container_id: str, timeout: int = 10) -> bool:
        """
        Stop a container.

        Args:
            container_id: Container ID
            timeout: Seconds to wait before killing

        Returns:
            True if successful
        """
        ...

    def remove_container(self, container_id: str, force: bool = False) -> bool:
        """
        Remove a container.

        Args:
            container_id: Container ID
            force: Force removal even if running

        Returns:
            True if successful
        """
        ...

    def exec_command(
        self,
        container_id: str,
        command: list[str],
        working_dir: Optional[str] = None,
        env: Optional[dict[str, str]] = None,
        user: Optional[str] = None,
    ) -> ExecResult:
        """
        Execute a command in a running container.

        Args:
            container_id: Container ID
            command: Command and arguments
            working_dir: Working directory
            env: Environment variables
            user: User to run as

        Returns:
            Execution result
        """
        ...

    def exec_stream(
        self,
        container_id: str,
        command: list[str],
        working_dir: Optional[str] = None,
        env: Optional[dict[str, str]] = None,
    ) -> Iterator[str]:
        """
        Execute a command and stream output.

        Args:
            container_id: Container ID
            command: Command and arguments
            working_dir: Working directory
            env: Environment variables

        Yields:
            Output lines as they are produced
        """
        ...

    def copy_to_container(
        self,
        container_id: str,
        src_path: str,
        dest_path: str,
    ) -> bool:
        """
        Copy files from host to container.

        Args:
            container_id: Container ID
            src_path: Source path on host
            dest_path: Destination path in container

        Returns:
            True if successful
        """
        ...

    def copy_from_container(
        self,
        container_id: str,
        src_path: str,
        dest_path: str,
    ) -> bool:
        """
        Copy files from container to host.

        Args:
            container_id: Container ID
            src_path: Source path in container
            dest_path: Destination path on host

        Returns:
            True if successful
        """
        ...

    def get_container_info(self, container_id: str) -> Optional[ContainerInfo]:
        """
        Get information about a container.

        Args:
            container_id: Container ID

        Returns:
            Container info or None if not found
        """
        ...

    def container_logs(
        self,
        container_id: str,
        follow: bool = False,
        tail: Optional[int] = None,
    ) -> Iterator[str]:
        """
        Get container logs.

        Args:
            container_id: Container ID
            follow: Follow log output
            tail: Number of lines from end

        Yields:
            Log lines
        """
        ...

    def create_network(self, name: str) -> str:
        """
        Create a container network.

        Args:
            name: Network name

        Returns:
            Network ID
        """
        ...

    def remove_network(self, name: str) -> bool:
        """
        Remove a container network.

        Args:
            name: Network name

        Returns:
            True if successful
        """
        ...

    def wait_for_container(
        self,
        container_id: str,
        timeout: Optional[int] = None,
    ) -> int:
        """
        Wait for a container to finish.

        Args:
            container_id: Container ID
            timeout: Max seconds to wait

        Returns:
            Exit code
        """
        ...


def detect_runtime() -> Optional[RuntimeType]:
    """
    Detect available container runtime.

    Checks in order: Docker -> Podman -> containerd

    Returns:
        RuntimeType or None if no runtime available
    """
    # Check Docker
    if shutil.which('docker'):
        return RuntimeType.DOCKER

    # Check Podman
    if shutil.which('podman'):
        return RuntimeType.PODMAN

    # Check containerd (via nerdctl)
    if shutil.which('nerdctl'):
        return RuntimeType.CONTAINERD

    return None


def get_runtime(runtime_type: Optional[RuntimeType] = None) -> 'ContainerRuntime':
    """
    Get a container runtime instance.

    Args:
        runtime_type: Specific runtime to use, or auto-detect

    Returns:
        ContainerRuntime instance

    Raises:
        RuntimeError: If no runtime is available
    """
    if runtime_type is None:
        runtime_type = detect_runtime()

    if runtime_type is None:
        raise RuntimeError(
            "No container runtime found. Please install Docker, Podman, or containerd."
        )

    if runtime_type == RuntimeType.DOCKER:
        from .docker import DockerRuntime
        return DockerRuntime()

    elif runtime_type == RuntimeType.PODMAN:
        from .podman import PodmanRuntime
        return PodmanRuntime()

    elif runtime_type == RuntimeType.CONTAINERD:
        from .containerd import ContainerdRuntime
        return ContainerdRuntime()

    raise RuntimeError(f"Unknown runtime type: {runtime_type}")
