"""Profile loader for gaol."""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

import yaml

from gaol.config import get_config, get_data_dir
from gaol.models.profile import Profile

if TYPE_CHECKING:
    from gaol.sharing.models import ImportResult

# Built-in profiles stored as Python dicts
BUILTIN_PROFILES: dict[str, dict] = {
    "base": {
        "name": "base",
        "description": "Minimal base system with essential tools",
        "packages": ["curl", "wget", "vim", "htop", "less", "procps", "net-tools", "iputils-ping"],
        "setup_commands": [],  # Package manager handled by setup script
    },
    "tailscale": {
        "name": "tailscale",
        "description": "Tailscale VPN connectivity",
        "requires_root": True,
        "depends_on": ["base"],
        "packages": [],
        "setup_commands": [
            "curl -fsSL https://tailscale.com/install.sh | sh",
        ],
        "services": ["tailscaled"],
        "environment": {"TAILSCALE_AUTHKEY": ""},
        "capabilities_add": ["NET_ADMIN", "NET_RAW"],
    },
    "tor": {
        "name": "tor",
        "description": "Tor network routing with torsocks",
        "depends_on": ["base"],
        "packages": ["tor", "torsocks"],
        "services": ["tor"],
        "setup_commands": [],
    },
    "tor-transparent": {
        "name": "tor-transparent",
        "description": "Transparent Tor proxy - routes ALL traffic through Tor",
        "depends_on": ["base"],
        "requires_root": True,
        "packages": ["tor", "torsocks", "iptables"],
        "services": ["tor"],
        "setup_commands": [
            # Create tor user if not exists
            "id -u debian-tor &>/dev/null || useradd -r -s /bin/false debian-tor",
            # Configure Tor for transparent proxy
            "mkdir -p /var/lib/tor",
            "chown debian-tor:debian-tor /var/lib/tor",
            "chmod 700 /var/lib/tor",
        ],
        "files": {
            "/etc/tor/torrc": (
                "# Gaol transparent Tor proxy configuration\n"
                "SocksPort 9050\n"
                "ControlPort 9051\n"
                "TransPort 9040\n"
                "DNSPort 5353\n"
                "AutomapHostsOnResolve 1\n"
                "VirtualAddrNetworkIPv4 10.192.0.0/10\n"
                "DataDirectory /var/lib/tor\n"
                "User debian-tor\n"
            ),
            "/etc/iptables/rules.v4": (
                "*nat\n"
                ":PREROUTING ACCEPT [0:0]\n"
                ":INPUT ACCEPT [0:0]\n"
                ":OUTPUT ACCEPT [0:0]\n"
                ":POSTROUTING ACCEPT [0:0]\n"
                "# Tor transparent proxy rules\n"
                "-A OUTPUT -m owner --uid-owner debian-tor -j RETURN\n"
                "-A OUTPUT -d 127.0.0.0/8 -j RETURN\n"
                "-A OUTPUT -d 10.0.0.0/8 -j RETURN\n"
                "-A OUTPUT -d 172.16.0.0/12 -j RETURN\n"
                "-A OUTPUT -d 192.168.0.0/16 -j RETURN\n"
                "-A OUTPUT -p udp --dport 53 -j REDIRECT --to-ports 5353\n"
                "-A OUTPUT -p tcp --syn -j REDIRECT --to-ports 9040\n"
                "COMMIT\n"
            ),
        },
        "environment": {
            "TOR_SOCKS_PORT": "9050",
            "TOR_CONTROL_PORT": "9051",
            "TOR_TRANS_PORT": "9040",
            "TOR_DNS_PORT": "5353",
        },
        "capabilities_add": ["NET_ADMIN", "NET_RAW"],
        "exposed_ports": [],
    },
    "gui": {
        "name": "gui",
        "description": "X11/Wayland forwarding support with display passthrough",
        "depends_on": ["base"],
        "packages": [
            "xauth",
            "x11-apps",
            "dbus-x11",
            "libgl1-mesa-dri",
        ],
        "environment": {"DISPLAY": "${DISPLAY}", "WAYLAND_DISPLAY": "${WAYLAND_DISPLAY}"},
        "volumes": {
            "/tmp/.X11-unix": "/tmp/.X11-unix",
            "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}": "/tmp/${WAYLAND_DISPLAY}",
        },
    },
    "sunshine": {
        "name": "sunshine",
        "description": "Moonlight-compatible game streaming server",
        "depends_on": ["gui"],
        "requires_privileged": True,
        "packages": ["wget", "udev"],
        "setup_commands": [
            # Distro-specific installation
            "if command -v apt-get &> /dev/null; then",
            "  wget -q https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine-debian-bookworm-amd64.deb -O /tmp/sunshine.deb",
            "  apt-get install -y /tmp/sunshine.deb || apt-get install -f -y",
            "  rm /tmp/sunshine.deb",
            "elif command -v dnf &> /dev/null; then",
            "  dnf install -y https://github.com/LizardByte/Sunshine/releases/latest/download/sunshine-fedora-40-amd64.rpm || echo 'Sunshine RPM install failed'",
            "elif command -v pacman &> /dev/null; then",
            "  echo 'Install sunshine from AUR: yay -S sunshine'",
            "else",
            "  echo 'Sunshine installation not supported on this distro'",
            "fi",
        ],
        "services": ["sunshine"],
        "exposed_ports": [47984, 47989, 47990, 48010],
    },
    "rustdesk": {
        "name": "rustdesk",
        "description": "Self-hosted remote desktop (client and server)",
        "depends_on": ["gui"],
        "packages": ["wget", "libxdo3", "libxfixes3", "libxrandr2", "libxcb-shape0"],
        "setup_commands": [
            # Distro-specific installation
            "if command -v apt-get &> /dev/null; then",
            "  wget -q https://github.com/rustdesk/rustdesk/releases/latest/download/rustdesk-1.3.1-x86_64.deb -O /tmp/rustdesk.deb || true",
            "  dpkg -i /tmp/rustdesk.deb || apt-get install -f -y",
            "  rm -f /tmp/rustdesk.deb",
            "elif command -v dnf &> /dev/null; then",
            "  wget -q https://github.com/rustdesk/rustdesk/releases/latest/download/rustdesk-1.3.1-0.x86_64.rpm -O /tmp/rustdesk.rpm || true",
            "  dnf install -y /tmp/rustdesk.rpm || true",
            "  rm -f /tmp/rustdesk.rpm",
            "elif command -v pacman &> /dev/null; then",
            "  echo 'Install rustdesk from AUR: yay -S rustdesk-bin'",
            "else",
            "  echo 'RustDesk installation not supported on this distro'",
            "fi",
        ],
        "exposed_ports": [21115, 21116, 21117, 21118, 21119],
    },
    "vnc": {
        "name": "vnc",
        "description": "TigerVNC server with virtual display",
        "depends_on": ["gui"],
        "packages": [
            "tigervnc-standalone-server",
            "tigervnc-common",
            "xfce4",
            "xfce4-terminal",
            "dbus-x11",
        ],
        "setup_commands": [
            "mkdir -p ~/.vnc",
            'echo "#!/bin/bash\\nstartxfce4 &" > ~/.vnc/xstartup',
            "chmod +x ~/.vnc/xstartup",
        ],
        "exposed_ports": [5901],
    },
    "rdp": {
        "name": "rdp",
        "description": "xrdp server for Windows Remote Desktop compatibility",
        "depends_on": ["gui"],
        "packages": ["xrdp", "xfce4", "xfce4-terminal", "dbus-x11"],
        "services": ["xrdp", "xrdp-sesman"],
        "setup_commands": [
            'echo "startxfce4" > ~/.xsession',
            "chmod +x ~/.xsession",
        ],
        "exposed_ports": [3389],
    },
    "dev-python": {
        "name": "dev-python",
        "description": "Python development environment with uv, ruff, and pytest",
        "depends_on": ["base"],
        "packages": ["python3", "python3-pip", "python3-venv", "git"],
        "setup_commands": [
            "curl -LsSf https://astral.sh/uv/install.sh | sh",
            "echo 'export PATH=\"$HOME/.cargo/bin:$PATH\"' >> ~/.bashrc",
        ],
        "environment": {"UV_SYSTEM_PYTHON": "1"},
    },
    "dev-node": {
        "name": "dev-node",
        "description": "Node.js development environment with pnpm and prettier",
        "depends_on": ["base"],
        "packages": ["git"],
        "setup_commands": [
            # Install Node.js using distro-appropriate method
            "if command -v apt-get &> /dev/null; then",
            "  curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -",
            "  apt-get install -y nodejs",
            "elif command -v dnf &> /dev/null; then",
            "  dnf module install -y nodejs:20/common || dnf install -y nodejs npm",
            "elif command -v apk &> /dev/null; then",
            "  apk add nodejs npm",
            "elif command -v pacman &> /dev/null; then",
            "  pacman -S --noconfirm nodejs npm",
            "fi",
            "npm install -g pnpm prettier",
        ],
    },
}


class ProfileLoader:
    """Load and manage profiles."""

    def __init__(self) -> None:
        """Initialize the profile loader."""
        self._cache: dict[str, Profile] = {}
        self._profile_dirs: list[Path] = []
        self._init_profile_dirs()

    def _init_profile_dirs(self) -> None:
        """Initialize profile search directories."""
        config = get_config()

        # Add built-in profiles directory
        builtin_dir = get_data_dir() / "profiles" / "builtin"
        builtin_dir.mkdir(parents=True, exist_ok=True)
        self._profile_dirs.append(builtin_dir)

        # Add user-configured directories
        for dir_str in config.profile_dirs:
            dir_path = Path(dir_str).expanduser()
            if dir_path.exists():
                self._profile_dirs.append(dir_path)

    def get(self, name: str) -> Profile:
        """Get a profile by name.

        Args:
            name: Profile name

        Returns:
            Profile instance

        Raises:
            KeyError: If profile not found
        """
        if name in self._cache:
            return self._cache[name]

        # Check built-in profiles first
        if name in BUILTIN_PROFILES:
            profile = Profile.from_yaml(BUILTIN_PROFILES[name])
            self._cache[name] = profile
            return profile

        # Search in profile directories
        for profile_dir in self._profile_dirs:
            for ext in [".yaml", ".yml"]:
                profile_file = profile_dir / f"{name}{ext}"
                if profile_file.exists():
                    with open(profile_file) as f:
                        data = yaml.safe_load(f)
                    profile = Profile.from_yaml(data)
                    self._cache[name] = profile
                    return profile

        # Check imported profiles
        from gaol.profiles.store import get_profile_store

        store = get_profile_store()
        profile = store.get(name)
        if profile is not None:
            self._cache[name] = profile
            return profile

        raise KeyError(f"Profile not found: {name}")

    def list_available(self) -> list[str]:
        """List all available profile names."""
        profiles = set(BUILTIN_PROFILES.keys())

        for profile_dir in self._profile_dirs:
            if profile_dir.exists():
                for f in profile_dir.glob("*.yaml"):
                    profiles.add(f.stem)
                for f in profile_dir.glob("*.yml"):
                    profiles.add(f.stem)

        # Include imported profiles
        from gaol.profiles.store import get_profile_store

        profiles.update(get_profile_store().list())

        return sorted(profiles)

    def resolve_dependencies(self, profile_names: list[str]) -> list[Profile]:
        """Resolve profile dependencies and return ordered list.

        Args:
            profile_names: List of profile names to resolve

        Returns:
            Ordered list of profiles with dependencies first

        Raises:
            KeyError: If a profile is not found
            ValueError: If there are conflicting profiles
        """
        resolved: list[Profile] = []
        resolved_names: set[str] = set()
        conflicts: set[str] = set()

        def resolve_one(name: str) -> None:
            if name in resolved_names:
                return

            profile = self.get(name)

            # Check for conflicts
            for conflict in profile.conflicts_with:
                if conflict in resolved_names:
                    raise ValueError(f"Profile '{name}' conflicts with '{conflict}'")
                conflicts.add(conflict)

            if name in conflicts:
                raise ValueError(f"Profile '{name}' conflicts with a previously loaded profile")

            # Resolve dependencies first
            for dep in profile.depends_on:
                if (
                    not dep.optional
                    or dep.name in BUILTIN_PROFILES
                    or self._profile_exists(dep.name)
                ):
                    resolve_one(dep.name)

            resolved.append(profile)
            resolved_names.add(name)

        for name in profile_names:
            resolve_one(name)

        return resolved

    def _profile_exists(self, name: str) -> bool:
        """Check if a profile exists."""
        if name in BUILTIN_PROFILES:
            return True
        for profile_dir in self._profile_dirs:
            for ext in [".yaml", ".yml"]:
                if (profile_dir / f"{name}{ext}").exists():
                    return True
        # Check imported profiles
        from gaol.profiles.store import get_profile_store

        return get_profile_store().exists(name)

    def import_from_url(
        self,
        url: str,
        name: str | None = None,
        skip_security_scan: bool = False,
        overwrite: bool = False,
    ) -> ImportResult:
        """Import a profile from a URL.

        Args:
            url: URL or reference to import from (https://, github:, gist:)
            name: Override name for the profile
            skip_security_scan: Skip security scanning
            overwrite: Overwrite if profile already exists

        Returns:
            ImportResult with status and details

        Raises:
            ImportError: If import fails
            FetchError: If fetching fails
            ContentValidationError: If content is invalid
            ProfileExistsError: If profile exists and overwrite=False
        """
        from gaol.sharing import get_content_importer
        from gaol.sharing.exceptions import ProfileExistsError
        from gaol.profiles.store import get_profile_store

        # Check if already exists
        store = get_profile_store()
        check_name = name
        if check_name is None:
            # We need to fetch first to know the name
            pass
        elif store.exists(check_name) and not overwrite:
            raise ProfileExistsError(check_name)

        importer = get_content_importer()
        result = importer.import_profile(
            url,
            name=name,
            save_locally=True,
            skip_security_scan=skip_security_scan,
        )

        # Clear cache if we imported successfully
        if result.success and result.name in self._cache:
            del self._cache[result.name]

        return result


# Global loader instance
_loader: ProfileLoader | None = None


def get_profile_loader() -> ProfileLoader:
    """Get the global profile loader instance."""
    global _loader
    if _loader is None:
        _loader = ProfileLoader()
    return _loader
