"""
AI-LSC -- Stack export and container backend.

Contains pure-logic functions for:

* **build_stack_spec** -- serialises the current pipeline state plus
  registry metadata into a portable JSON spec.
* **ContainerBackend** -- generates Podman/Docker compose YAML,
  LXC container configs, or JSON fallback from that spec.

No UI code here.  All path operations use :mod:`pathlib`.
"""

from __future__ import annotations

import json
from datetime import datetime
from pathlib import Path
from typing import Any

from ai_lsc.constants import BASE_DIR, STACK_SCHEMA_VERSION
from ai_lsc.registry.manager import RegistryManager
from ai_lsc.utils.paths import build_path_tree


def build_stack_spec(
    state: dict[str, Any],
    registry: RegistryManager,
    backend: str = "podman",
) -> dict[str, Any]:
    """Build a portable stack spec from the current state + registry.

    The resulting dict is JSON-serialisable and can be passed to
    ``ContainerBackend`` for compose-file / config generation.

    Parameters
    ----------
    state :
        Pipeline state dict (must contain ``active_tools`` and
        optionally ``port_map``).
    registry :
        A loaded ``RegistryManager`` instance.
    backend :
        Container backend type (``"podman"``, ``"docker"``, or ``"lxc"``).
    """
    tools = [
        {
            "id": tid,
            "name": registry.get_tool(tid).get("name"),
            "layer": registry.get_tool(tid).get("layer"),
            "role": registry.get_tool(tid).get("role"),
            "category": registry.get_tool(tid).get("category"),
            "launcher": registry.get_tool(tid).get("launcher"),
            "installer": registry.get_tool(tid).get("installer"),
            "deps": registry.get_tool(tid).get("deps", []),
        }
        for tid in state.get("active_tools", [])
        if not tid.startswith("skill:")
    ]

    return {
        "schema": STACK_SCHEMA_VERSION,
        "created": datetime.now().isoformat(),
        "backend": backend,
        "tools": tools,
        "ports": state.get("port_map", {}),
        "base_dir": BASE_DIR,
    }


class ContainerBackend:
    """Generates container deployment files from a stack spec.

    Supports three backends:

    * **podman/docker** -- ``docker-compose.yaml`` (version 3.8)
    * **lxc** -- per-container config files + launch script

    Parameters
    ----------
    exports_root :
        Directory where output files are written.
    """

    def __init__(self, exports_root: str | Path) -> None:
        self.exports_root = Path(exports_root)

    # ── Compose (Podman / Docker) ──────────────────────────────────────

    def generate_compose_yaml(self, spec: dict) -> dict:
        """Transform a stack spec into a compose-file data structure.

        Each tool becomes a service with ``network_mode: host``,
        ``restart: unless-stopped``, and the base directory mounted as a
        volume.  The launcher command's ``{placeholders}`` are resolved
        to absolute paths.
        """
        paths = build_path_tree(spec.get("base_dir", BASE_DIR))
        services: dict[str, dict] = {}

        for tool in spec.get("tools", []):
            raw_cmd = tool.get("launcher", {}).get("cmd", "")
            svc: dict[str, Any] = {
                "image": f"localhost/ai-lsc-{tool['id']}:latest",
                "network_mode": "host",
                "restart": "unless-stopped",
                "volumes": [f"{paths['base_dir']}:{paths['base_dir']}"],
            }
            clean_cmd = (
                raw_cmd
                .replace("{base_dir}", str(paths["base_dir"]))
                .replace("{tools_root}", str(paths["tools_root"]))
                .replace("{models_root}", str(paths["models_root"]))
                .replace("{workspaces_root}", str(paths["workspaces_root"]))
            )
            if clean_cmd.strip():
                svc["command"] = clean_cmd
            services[tool["id"]] = svc

        return {"version": "3.8", "services": services}

    def write_compose(
        self,
        spec: dict,
        backend_type: str = "podman",
    ) -> Path:
        """Write compose YAML (or JSON fallback) to disk.

        Returns the path of the written file.
        """
        self.exports_root.mkdir(parents=True, exist_ok=True)
        file_path = self.exports_root / f"{backend_type}-compose.yml"
        compose_data = self.generate_compose_yaml(spec)

        try:
            import yaml
            file_path.write_text(
                yaml.dump(compose_data, sort_keys=False),
                encoding="utf-8",
            )
        except ImportError:
            file_path.write_text(
                json.dumps(compose_data, indent=4),
                encoding="utf-8",
            )

        return file_path

    # ── LXC backend ─────────────────────────────────────────────────────

    def generate_lxc_configs(self, spec: dict) -> dict[str, str]:
        """Generate LXC config blocks for each tool in the stack.

        Returns a dict mapping container names to their config file
        content.  Each config block includes:

        * Base OS template (default: ``ubuntu:22.04``)
        * Network mode (``lxc.net.0.type = veth`` or ``none``)
        * Mount points for the base directory hierarchy
        * Autostart flag
        * Tool-specific command to execute on start
        """
        paths = build_path_tree(spec.get("base_dir", BASE_DIR))
        configs: dict[str, str] = {}

        for tool in spec.get("tools", []):
            container_name = f"ai-lsc-{tool['id']}"
            raw_cmd = tool.get("launcher", {}).get("cmd", "")

            # Resolve placeholders in the command
            clean_cmd = (
                raw_cmd
                .replace("{base_dir}", str(paths["base_dir"]))
                .replace("{tools_root}", str(paths["tools_root"]))
                .replace("{models_root}", str(paths["models_root"]))
                .replace("{workspaces_root}", str(paths["workspaces_root"]))
            )

            lines = [
                f"# AI-LSC auto-generated LXC config for {tool['id']}",
                f"# Container: {container_name}",
                f"# Tool: {tool.get('name', tool['id'])}",
                "",
                "# -- Basic container settings --",
                "lxc.uts.name = " + container_name,
                "lxc.init.cmd = /bin/bash",
                "",
                "# -- Network --",
                "lxc.net.0.type = veth",
                "lxc.net.0.flags = up",
                "lxc.net.0.link = lxcbr0",
                "",
                "# -- Mounts --",
                f"lxc.mount.auto = proc:mixed sys:ro",
                f"lxc.rootfs.mount = {paths['base_dir']}/containers/{container_name}/rootfs",
                f"# Mount AI stack base directory",
                f"lxc.mount.entry = {paths['base_dir']} {paths['base_dir']} none bind,rw 0 0",
                f"# Mount tools directory",
                f"lxc.mount.entry = {paths['tools_root']} {paths['tools_root']} none bind,rw 0 0",
                f"# Mount models directory",
                f"lxc.mount.entry = {paths['models_root']} {paths['models_root']} none bind,rw 0 0",
                "",
                "# -- Autostart --",
                "lxc.start.auto = 1",
                "lxc.start.delay = 5",
                "",
                "# -- Tool launch command --",
                f"lxc.execute.post = /usr/bin/env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin {clean_cmd}" if clean_cmd.strip() else "",
            ]

            configs[container_name] = "\n".join(
                line for line in lines if line
            )

        return configs

    def write_lxc(
        self,
        spec: dict,
    ) -> Path:
        """Write LXC configs and a launch script to disk.

        Creates:
        * ``<exports_root>/lxc/`` -- per-container config files
        * ``<exports_root>/lxc-launch.sh`` -- shell script to create
          and start all containers

        Returns the path of the launch script.
        """
        lxc_dir = self.exports_root / "lxc"
        lxc_dir.mkdir(parents=True, exist_ok=True)

        configs = self.generate_lxc_configs(spec)
        container_names: list[str] = []

        # Write individual config files
        for container_name, config_text in configs.items():
            config_path = lxc_dir / f"{container_name}.conf"
            config_path.write_text(config_text, encoding="utf-8")
            container_names.append(container_name)

        # Generate launch script
        script_lines = [
            "#!/usr/bin/env bash",
            "# AI-LSC LXC Stack Launcher",
            f"# Generated: {datetime.now().isoformat()}",
            f"# Containers: {len(container_names)}",
            f"# Backend: lxc",
            "",
            'set -euo pipefail',
            "",
            "LXC_DIR=\"$(cd \"$(dirname \"$0\")\" && pwd)\"",
            "",
            "echo \"[AI-LSC] Creating LXC containers...\"",
            "",
        ]

        for container_name in container_names:
            conf_file = f"{container_name}.conf"
            script_lines.extend([
                f"if ! lxc-info -n {container_name} >/dev/null 2>&1; then",
                f"    echo \"  Creating {container_name}...\"",
                f"    lxc-create -n {container_name} -t download -- -d ubuntu -r jammy -a amd64",
                f"    cp \"$LXC_DIR/{conf_file}\" /var/lib/lxc/{container_name}/config.d/ai-lsc.conf",
                f"else",
                f"    echo \"  {container_name} already exists, skipping creation\"",
                f"fi",
                "",
            ])

        script_lines.extend([
            "",
            "echo \"[AI-LSC] Starting LXC containers...\"",
            "",
        ])

        for container_name in container_names:
            script_lines.append(
                f"echo \"  Starting {container_name}...\""
            )
            script_lines.append(
                f"lxc-start -n {container_name} -d -F \"$LXC_DIR/{container_name}.conf\" 2>/dev/null || "
                f"lxc-start -n {container_name} -d"
            )
            script_lines.append("")

        script_lines.extend([
            "echo \"[AI-LSC] Stack launched. Use 'lxc-ls --running' to verify.\"",
            f"echo \"[AI-LSC] {len(container_names)} containers active.\"",
            "",
            "# -- Stop command --",
            f"echo ''",
            f"echo 'To stop all containers:'",
            f"for c in {' '.join(container_names)}; do",
            f"    echo \"  lxc-stop -n $c -t 5\"",
            f"done",
        ])

        launch_script = self.exports_root / "lxc-launch.sh"
        launch_script.write_text("\n".join(script_lines), encoding="utf-8")
        launch_script.chmod(0o755)

        return launch_script

    # ── Unified write (auto-selects backend) ──────────────────────────

    def write(
        self,
        spec: dict,
        backend_type: str = "podman",
    ) -> Path:
        """Write deployment files for the specified backend.

        Parameters
        ----------
        spec :
            Stack specification dict (from ``build_stack_spec``).
        backend_type :
            One of ``"podman"``, ``"docker"``, or ``"lxc"``.

        Returns
        -------
        Path to the primary output file.
        """
        if backend_type == "lxc":
            return self.write_lxc(spec)
        return self.write_compose(spec, backend_type=backend_type)
