"""CloneBox adapter: wraps clonebox CLI for VM lifecycle management.

Provides a unified interface for creating, starting, stopping, and snapshotting
VMs via CloneBox. Falls back gracefully when clonebox is not installed.

Usage:
    adapter = CloneBoxAdapter()
    if adapter.is_available():
        adapter.create_vm(config)
        adapter.start_vm("windsurf-work")
"""

from __future__ import annotations

import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import structlog

logger = structlog.get_logger()


@dataclass
class CloneBoxResult:
    """Result of a clonebox CLI invocation."""
    success: bool
    stdout: str = ""
    stderr: str = ""
    returncode: int = 0


class CloneBoxAdapter:
    """Adapter wrapping the clonebox CLI for VM operations.

    All operations use `--user` flag for rootless/session-level VMs.
    Profile YAML files control VM configuration (packages, apps, mounts, GUI).
    """

    def __init__(self, profiles_dir: str = "~/.config/proxym/profiles"):
        self.profiles_dir = Path(profiles_dir).expanduser()
        self._clonebox_bin: str | None = None

    def is_available(self) -> bool:
        """Check if clonebox is installed and reachable."""
        return self._find_bin() is not None

    def version(self) -> str | None:
        """Return clonebox version string, or None if not available."""
        result = self._run(["--version"])
        if result.success:
            return result.stdout.strip()
        return None

    # ── VM Lifecycle ─────────────────────────────────────────

    def create_vm(self, name: str, profile_path: str | Path | None = None,
                  extra_args: dict[str, Any] | None = None) -> CloneBoxResult:
        """Create a VM clone from a profile.

        Equivalent to: clonebox clone . --name {name} --user --no-run [--profile ...]
        """
        cmd = ["clone", ".", "--name", name, "--user", "--no-run"]
        if profile_path:
            cmd += ["--profile", str(profile_path)]
        if extra_args:
            for k, v in extra_args.items():
                cmd += [f"--{k}", str(v)]
        result = self._run(cmd)
        if result.success:
            logger.info("clonebox_vm_created", name=name)
        else:
            logger.error("clonebox_vm_create_failed", name=name, error=result.stderr)
        return result

    def start_vm(self, name: str) -> CloneBoxResult:
        """Start a VM.

        Equivalent to: clonebox start {name} --user
        """
        result = self._run(["start", name, "--user"])
        if result.success:
            logger.info("clonebox_vm_started", name=name)
        else:
            logger.error("clonebox_vm_start_failed", name=name, error=result.stderr)
        return result

    def stop_vm(self, name: str) -> CloneBoxResult:
        """Stop a VM.

        Equivalent to: clonebox stop {name} --user
        """
        result = self._run(["stop", name, "--user"])
        if result.success:
            logger.info("clonebox_vm_stopped", name=name)
        else:
            logger.error("clonebox_vm_stop_failed", name=name, error=result.stderr)
        return result

    def snapshot(self, name: str, snapshot_name: str) -> CloneBoxResult:
        """Create a snapshot of a VM.

        Equivalent to: clonebox snapshot create {name} --name {snap} --user
        """
        result = self._run(["snapshot", "create", name, "--name", snapshot_name, "--user"])
        if result.success:
            logger.info("clonebox_snapshot_created", vm=name, snapshot=snapshot_name)
        else:
            logger.error("clonebox_snapshot_failed", vm=name, error=result.stderr)
        return result

    def restore_snapshot(self, name: str, snapshot_name: str) -> CloneBoxResult:
        """Restore a VM snapshot.

        Equivalent to: clonebox snapshot restore {name} --name {snap} --user
        """
        return self._run(["snapshot", "restore", name, "--name", snapshot_name, "--user"])

    def health(self, name: str) -> CloneBoxResult:
        """Check VM health status.

        Equivalent to: clonebox health {name} --user
        """
        return self._run(["health", name, "--user"])

    def delete_vm(self, name: str) -> CloneBoxResult:
        """Delete a VM completely.

        Equivalent to: clonebox delete {name} --user --force
        """
        result = self._run(["delete", name, "--user", "--force"])
        if result.success:
            logger.info("clonebox_vm_deleted", name=name)
        return result

    def list_vms(self) -> CloneBoxResult:
        """List all clonebox VMs.

        Equivalent to: clonebox list --user --json
        """
        return self._run(["list", "--user", "--json"])

    def detect(self) -> CloneBoxResult:
        """Detect current system configuration for cloning.

        Equivalent to: clonebox detect --json
        """
        return self._run(["detect", "--json"])

    def export_encrypted(self, name: str, output_path: str) -> CloneBoxResult:
        """Export a VM as an encrypted package.

        Equivalent to: clonebox export-encrypted {name} --output {path} --user
        """
        return self._run(["export-encrypted", name, "--output", output_path, "--user"])

    # ── Profile Management ───────────────────────────────────

    def get_profile_path(self, tool: str) -> Path | None:
        """Get the profile YAML path for a tool (windsurf, cursor, etc.)."""
        profile = self.profiles_dir / f"{tool}.clonebox.yaml"
        if profile.exists():
            return profile
        return None

    def generate_profile_name(self, tool: str, account_name: str) -> str:
        """Generate a VM name from tool + account: e.g. 'windsurf-work-anthropic'."""
        safe_account = account_name.lower().replace(" ", "-").replace("_", "-")
        return f"{tool}-{safe_account}"

    # ── Internals ────────────────────────────────────────────

    def _find_bin(self) -> str | None:
        """Locate the clonebox binary."""
        if self._clonebox_bin is not None:
            return self._clonebox_bin
        path = shutil.which("clonebox")
        if path:
            self._clonebox_bin = path
        return self._clonebox_bin

    def _run(self, args: list[str], timeout: int = 60) -> CloneBoxResult:
        """Execute a clonebox CLI command."""
        binary = self._find_bin()
        if not binary:
            return CloneBoxResult(
                success=False,
                stderr="clonebox not found in PATH",
                returncode=-1,
            )
        cmd = [binary] + args
        try:
            proc = subprocess.run(
                cmd, capture_output=True, text=True, timeout=timeout,
            )
            return CloneBoxResult(
                success=proc.returncode == 0,
                stdout=proc.stdout,
                stderr=proc.stderr,
                returncode=proc.returncode,
            )
        except subprocess.TimeoutExpired:
            logger.warning("clonebox_timeout", cmd=args, timeout=timeout)
            return CloneBoxResult(success=False, stderr="timeout", returncode=-1)
        except Exception as e:
            logger.error("clonebox_error", cmd=args, error=str(e))
            return CloneBoxResult(success=False, stderr=str(e), returncode=-1)
