"""CloneIntoHandler: git clone into a running container.

Implements the ``--clone-into`` CLI argument for ``container-instance``
resources.  When a ``container-instance`` is registered with a
``clone_into`` property, this handler clones the specified git
repository URL into the container's workspace using
``docker exec git clone``.

Based on:
    - Issue #7555: container-instance --clone-into CLI argument
    - ADR-043: Devcontainer Integration
"""

from __future__ import annotations

import logging
import subprocess

logger = logging.getLogger(__name__)

#: Timeout in seconds for the git clone subprocess.
CLONE_TIMEOUT: int = 300

#: Default target directory inside the container for cloned repos.
DEFAULT_CLONE_TARGET: str = "/workspace"


class CloneIntoError(RuntimeError):
    """Raised when a git clone into a container fails.

    Attributes:
        repo_url: The repository URL that was being cloned.
        container_id: The container ID that was targeted.
        stderr: The stderr output from the failed command.
    """

    def __init__(
        self,
        repo_url: str,
        container_id: str,
        stderr: str,
    ) -> None:
        self.repo_url = repo_url
        self.container_id = container_id
        self.stderr = stderr
        super().__init__(
            f"Failed to clone '{repo_url}' into container '{container_id}': {stderr}"
        )


def clone_repo_into_container(
    container_id: str,
    repo_url: str,
    target_dir: str = DEFAULT_CLONE_TARGET,
    *,
    timeout: int = CLONE_TIMEOUT,
) -> str:
    """Clone a git repository into a running container.

    Executes ``docker exec <container_id> git clone <repo_url> <target_dir>``
    to clone the repository into the container's filesystem.

    Args:
        container_id: The Docker container ID or name to clone into.
        repo_url: The git repository URL to clone.
        target_dir: The target directory inside the container.
            Defaults to ``/workspace``.
        timeout: Maximum seconds to wait for the clone to complete.
            Defaults to :data:`CLONE_TIMEOUT` (300 seconds).

    Returns:
        The target directory path where the repository was cloned.

    Raises:
        ValueError: If ``container_id`` or ``repo_url`` is empty.
        CloneIntoError: If the git clone command fails.
        subprocess.TimeoutExpired: If the clone exceeds ``timeout``.
    """
    if not container_id or not container_id.strip():
        raise ValueError("container_id must be a non-empty string")
    if not repo_url or not repo_url.strip():
        raise ValueError("repo_url must be a non-empty string")

    container_id = container_id.strip()
    repo_url = repo_url.strip()
    target_dir = target_dir.strip() if target_dir else DEFAULT_CLONE_TARGET

    logger.info(
        "Cloning '%s' into container '%s' at '%s'",
        repo_url,
        container_id,
        target_dir,
    )

    result = subprocess.run(
        [
            "docker",
            "exec",
            container_id,
            "git",
            "clone",
            repo_url,
            target_dir,
        ],
        capture_output=True,
        text=True,
        timeout=timeout,
        check=False,
    )

    if result.returncode != 0:
        stderr = result.stderr.strip()
        logger.error(
            "git clone failed for '%s' in container '%s': %s",
            repo_url,
            container_id,
            stderr,
        )
        raise CloneIntoError(
            repo_url=repo_url,
            container_id=container_id,
            stderr=stderr,
        )

    logger.info(
        "Successfully cloned '%s' into container '%s' at '%s'",
        repo_url,
        container_id,
        target_dir,
    )
    return target_dir


def validate_clone_into_url(url: str) -> None:
    """Validate that a URL is a plausible git repository URL.

    Accepts:
    - HTTPS URLs: ``https://...``
    - SSH URLs: ``git@...`` or ``ssh://...``
    - Local paths: ``/...`` or ``./...``
    - Git protocol: ``git://...``

    Args:
        url: The URL string to validate.

    Raises:
        ValueError: If the URL is empty or does not match any valid prefix.
    """
    if not url or not url.strip():
        raise ValueError("URL must be a non-empty string")
    url = url.strip()
    valid_prefixes = (
        "https://",
        "http://",
        "git@",
        "ssh://",
        "git://",
        "/",
        "./",
        "../",
    )
    if not any(url.startswith(prefix) for prefix in valid_prefixes):
        raise ValueError(
            f"Invalid git repository URL: {url!r}. "
            f"Must start with one of: {', '.join(valid_prefixes)}"
        )
