"""VMOrchestrator: manage isolated dev environments via CloneBox or libvirt.

Facade class that composes behaviour from mixin modules:
  _infra.py       — _InfraMixin: _run, check_libvirt_available, _generate_domain_xml
  _lifecycle.py   — _LifecycleMixin: create_vm, start_vm, stop_vm, pause_vm, resume_vm
  _snapshot.py    — _SnapshotMixin: snapshot_vm, restore_snapshot, delete_vm
  _state.py       — _StateMixin: get_status, list_vms, _refresh_state_*, _get_vm_ip
  _project.py     — _ProjectMixin: switch_project, _remount_project
  _vm_config.py   — _ConfigMixin: _generate_vm_key, _render_template, _inject_vm_config
"""

from __future__ import annotations

import json
from dataclasses import asdict
from pathlib import Path

import structlog

from proxym.virt._check import _check_clonebox
from proxym.virt._config import VMConfig
from proxym.virt._enums import VMBackend, VMState
from proxym.virt._infra import _InfraMixin
from proxym.virt._lifecycle import _LifecycleMixin
from proxym.virt._project import _ProjectMixin
from proxym.virt._snapshot import _SnapshotMixin
from proxym.virt._state import _StateMixin
from proxym.virt._vm_config import _ConfigMixin

logger = structlog.get_logger()


class VMOrchestrator(
    _InfraMixin, _LifecycleMixin, _SnapshotMixin, _StateMixin, _ProjectMixin, _ConfigMixin,
):
    """Manages VMs for isolated dev environments.

    Backend selection:
      - CloneBox (preferred): uses clonebox CLI with YAML profiles
      - virsh (fallback): direct libvirt virsh commands

    Set backend="auto" (default) to auto-detect.
    """

    def __init__(self, config_dir: str = "~/.config/proxym/vms",
                 images_dir: str = "~/.local/share/proxym/images",
                 overlays_dir: str = "/var/tmp/proxym-overlays",
                 profiles_dir: str = "~/.config/proxym/profiles",
                 projects_root: str = "~/github"):
        self.config_dir = Path(config_dir).expanduser()
        self.images_dir = Path(images_dir).expanduser()
        self.overlays_dir = Path(overlays_dir).expanduser()
        self.profiles_dir = Path(profiles_dir).expanduser()
        self.projects_root = Path(projects_root).expanduser()
        self.config_dir.mkdir(parents=True, exist_ok=True)
        self.overlays_dir.mkdir(parents=True, exist_ok=True)
        self.vms: dict[str, VMConfig] = {}
        self._clonebox = None
        self._load_configs()

    def _load_configs(self) -> None:
        for f in self.config_dir.glob("*.json"):
            try:
                data = json.loads(f.read_text())
                vm = VMConfig(**{k: v for k, v in data.items()
                                if k in VMConfig.__dataclass_fields__})
                vm.state = VMState(data.get("state", "stopped"))
                self.vms[vm.id] = vm
            except Exception as e:
                logger.warning("vm_config_load_failed", file=f.name, error=str(e))

    def _save_config(self, vm: VMConfig) -> None:
        path = self.config_dir / f"{vm.id}.json"
        path.write_text(json.dumps(asdict(vm), indent=2, default=str))

    def _get_clonebox(self):
        """Get or create the CloneBox adapter (lazy init)."""
        if self._clonebox is None and _check_clonebox():
            from proxym.virt.clonebox_adapter import CloneBoxAdapter
            self._clonebox = CloneBoxAdapter(
                profiles_dir=str(self.profiles_dir),
            )
        return self._clonebox

    def _resolve_backend(self, config: VMConfig) -> VMBackend:
        """Determine which backend to use for a VM."""
        if config.backend == "clonebox":
            return VMBackend.CLONEBOX
        if config.backend == "virsh":
            return VMBackend.VIRSH
        # auto: prefer clonebox if available
        if self._get_clonebox() is not None:
            return VMBackend.CLONEBOX
        return VMBackend.VIRSH
