"""Permission fixer: applies mount permission policies post-start."""

import logging
import subprocess
from typing import Any

logger = logging.getLogger(__name__)


def apply_mount_permissions(
    container_id: str,
    mounts: list[dict],
    timeout: int = 10,
) -> list[dict[str, Any]]:
    """Apply permission policies to mounted directories in a running container.

    Runs `chown`, `chmod`, and file-mode fixes for each mount that declares
    an owner, mode, or file_mode. Requires the container to have a root user.

    Args:
        container_id: Docker container ID or name.
        mounts: List of mount definitions from the manifest.
        timeout: Max seconds per docker exec command.

    Returns:
        List of result dicts: [{mount_name, success, error}]
    """
    results = []

    for mount in mounts:
        name = mount.get("name", "unknown")
        target = mount["target"]
        owner = mount.get("owner")
        mode = mount.get("mode")
        file_mode = mount.get("file_mode")

        result: dict[str, Any] = {
            "mount_name": name,
            "success": True,
            "error": None,
        }

        # Skip read-only mounts — their permissions cannot be changed
        # post-start because the bind mount is locked.
        if mount.get("readonly", False):
            logger.debug(
                "Skipping permission fix for read-only mount %s (target=%s)",
                name,
                target,
            )
            results.append(result)
            continue

        # Skip if no permission policy defined
        if not owner and not mode and not file_mode:
            results.append(result)
            continue

        try:
            if owner:
                _run_in_container(
                    container_id,
                    ["chown", "-R", f"{owner}:{owner}", target],
                    timeout,
                )
                logger.debug(
                    "Applied owner %s to %s in container %s",
                    owner,
                    target,
                    container_id,
                )

            if mode and result["success"]:
                _run_in_container(
                    container_id,
                    ["chmod", mode, target],
                    timeout,
                )
                logger.debug(
                    "Applied mode %s to %s in container %s",
                    mode,
                    target,
                    container_id,
                )

            if file_mode and result["success"]:
                _run_in_container(
                    container_id,
                    [
                        "sh",
                        "-c",
                        f"find {target} -type f -exec chmod {file_mode} {{}} +",
                    ],
                    timeout,
                )
                logger.debug(
                    "Applied file_mode %s to files in %s in container %s",
                    file_mode,
                    target,
                    container_id,
                )

        except PermissionFixError as exc:
            result["success"] = False
            result["error"] = str(exc)
            logger.warning(
                "Permission fix failed for mount %s (target=%s): %s",
                name,
                target,
                exc,
            )

        results.append(result)

    return results


def _exec_and_log(
    container_id: str,
    command: list[str],
    timeout: int,
    description: str,
) -> str:
    """Run a docker exec command and log stdout/stderr for debugging."""
    cmd = ["docker", "exec", "--user", "root", container_id] + command
    logger.debug("[SSH-fix] %s: %s", description, " ".join(cmd))

    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
        )
    except subprocess.TimeoutExpired:
        raise PermissionFixError(
            f"Command timed out after {timeout}s: {' '.join(command)}"
        )
    except FileNotFoundError:
        raise PermissionFixError(f"Docker command not found: {' '.join(command)}")

    stdout = result.stdout.strip()
    stderr = result.stderr.strip()
    if stdout:
        logger.debug("[SSH-fix] %s stdout: %s", description, stdout)
    if stderr:
        logger.debug("[SSH-fix] %s stderr: %s", description, stderr)

    if result.returncode != 0:
        raise PermissionFixError(
            f"Command failed (rc={result.returncode}): {stderr or '(no stderr)'}"
        )
    return stdout


def apply_ssh_permissions(
    container_id: str,
    ssh_target: str,
    container_user: str,
    timeout: int = 10,
) -> dict[str, Any]:
    """Fix SSH directory ownership and permissions in a running container.

    Runs chown and chmod on the ~/.ssh directory so the container user
    can use the keys (SSH requires the private key to be owned by the
    user with mode 600).

    Args:
        container_id: Docker container ID or name.
        ssh_target: Absolute path to the .ssh directory inside the container.
        container_user: The container user that should own the keys.
        timeout: Max seconds per docker exec command.

    Returns:
        Result dict with keys: success, error.
    """
    result: dict[str, Any] = {"success": True, "error": None}
    try:
        # 1. Ensure directory is owned by the container user
        _exec_and_log(
            container_id,
            ["chown", "-R", f"{container_user}:{container_user}", ssh_target],
            timeout,
            "chown",
        )

        # 2. Set directory permissions
        _exec_and_log(
            container_id,
            ["chmod", "700", ssh_target],
            timeout,
            "chmod-dir",
        )

        # 3. Set private key permissions (id_ed25519, id_rsa, etc.)
        _exec_and_log(
            container_id,
            [
                "sh",
                "-c",
                f"find {ssh_target} -name 'id_*' -type f -exec chmod 600 {{}} +",
            ],
            timeout,
            "chmod-keys",
        )

        # 4. Verify final state
        ls_output = _exec_and_log(
            container_id,
            ["ls", "-la", ssh_target],
            timeout,
            "verify-ls",
        )
        stat_output = _exec_and_log(
            container_id,
            ["stat", "-c", "%U:%G %a %n", ssh_target],
            timeout,
            "verify-stat-dir",
        )
        key_stat = _exec_and_log(
            container_id,
            [
                "sh",
                "-c",
                f"stat -c '%U:%G %a %n' {ssh_target}/id_* 2>/dev/null || echo 'no id_* files found'",
            ],
            timeout,
            "verify-stat-keys",
        )

        logger.info(
            "SSH permissions fixed for container %s (user=%s, target=%s). "
            "ls:\n%s\nstat-dir: %s\nstat-keys: %s",
            container_id,
            container_user,
            ssh_target,
            ls_output,
            stat_output,
            key_stat,
        )
    except PermissionFixError as exc:
        result["success"] = False
        result["error"] = str(exc)
        logger.warning(
            "SSH permission fix failed for container %s (target=%s): %s",
            container_id,
            ssh_target,
            exc,
        )
    return result


class PermissionFixError(Exception):
    """Raised when a permission fix command fails."""

    pass


def _run_in_container(
    container_id: str,
    command: list[str],
    timeout: int,
) -> None:
    """Run a command inside a container as root.

    Args:
        container_id: Docker container ID or name.
        command: Command + args to execute.
        timeout: Max seconds to wait.

    Raises:
        PermissionFixError: If the command fails or times out.
    """
    cmd = ["docker", "exec", "--user", "root", container_id] + command

    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
        )
    except subprocess.TimeoutExpired:
        raise PermissionFixError(
            f"Command timed out after {timeout}s: {' '.join(command)}"
        )
    except FileNotFoundError:
        raise PermissionFixError(f"Docker command not found: {' '.join(command)}")

    if result.returncode != 0:
        raise PermissionFixError(
            f"Command failed (rc={result.returncode}): {result.stderr.strip()}"
        )


def check_root_user_available(container_id: str, timeout: int = 5) -> bool:
    """Check if the container has a root user we can exec as.

    Args:
        container_id: Docker container ID or name.
        timeout: Max seconds to wait.

    Returns:
        True if root user exists and is usable.
    """
    try:
        _run_in_container(container_id, ["id", "root"], timeout)
        return True
    except PermissionFixError:
        return False
