"""High-level network setup from specs.

Bridges the spec → allocator → resolver → executor pipeline.
This is the main entry point for setting up container networking
from a ContainerSpec's archetype field.
"""

from __future__ import annotations

import logging
from pathlib import Path

from gaol.networking.allocator import allocate, release, to_veth_config
from gaol.networking.archetypes import (
    NetworkArchetype,
    ResolvedNetworkPlan,
    resolve_bridged,
    resolve_host,
    resolve_shared,
    resolve_tailscale,
    resolve_tor_vpn_exit,
    resolve_vpn_exit,
)
from gaol.networking.executor import ExecutionResult, apply_plan, teardown_plan
from gaol.specs.models import ContainerSpec

logger = logging.getLogger(__name__)


def resolve_from_spec(
    spec: ContainerSpec,
    state_path: Path | None = None,
) -> ResolvedNetworkPlan:
    """Resolve a network plan from a container spec.

    Reads the spec's ``network.archetype`` field, allocates subnets
    as needed, and returns a complete plan ready for execution.

    Args:
        spec: Container specification with ``network.archetype`` set.
        state_path: Override for subnet allocation state file (testing).

    Returns:
        ResolvedNetworkPlan ready to pass to ``apply_plan()``.

    Raises:
        ValueError: If archetype is not set or invalid.
        RuntimeError: If subnet pool is exhausted.
    """
    archetype_str = spec.network.archetype
    if not archetype_str:
        # Fall back to mode-based resolution
        mode = spec.network.mode
        if mode == "host":
            return resolve_host()
        if mode == "bridged":
            bridge = spec.network.bridge or "br0"
            return resolve_bridged(bridge)
        # Default: shared
        return resolve_shared()

    archetype = NetworkArchetype(archetype_str)

    if archetype == NetworkArchetype.HOST:
        return resolve_host()

    if archetype == NetworkArchetype.SHARED:
        return resolve_shared()

    if archetype == NetworkArchetype.BRIDGED:
        bridge = spec.network.bridge or "br0"
        return resolve_bridged(bridge)

    if archetype == NetworkArchetype.TAILSCALE:
        bridge = spec.network.bridge or "br0"
        return resolve_tailscale(bridge)

    if archetype == NetworkArchetype.VPN_EXIT:
        return _resolve_vpn_exit(spec, state_path)

    if archetype == NetworkArchetype.TOR_VPN_EXIT:
        return _resolve_tor_vpn_exit(spec, state_path)

    raise ValueError(f"Unknown archetype: {archetype_str}")


def _resolve_vpn_exit(
    spec: ContainerSpec, state_path: Path | None = None
) -> ResolvedNetworkPlan:
    """Resolve VPN_EXIT archetype from spec + privacy config."""
    if not spec.privacy or not spec.privacy.vpn:
        raise ValueError(
            "vpn-exit archetype requires privacy.vpn configuration "
            "with at minimum config_file set"
        )

    vpn = spec.privacy.vpn
    vpn_name = f"svc-{spec.name}-vpn"

    # Allocate subnet for main container
    alloc = allocate(spec.name, state_path=state_path)
    veth = to_veth_config(alloc)

    return resolve_vpn_exit(
        container_name=spec.name,
        veth=veth,
        table_id=alloc.table_id,
        priority=alloc.priority,
        vpn_container_name=vpn_name,
        vpn_config_file=vpn.config_file,
        vpn_dns=vpn.dns_servers if vpn.dns_servers else None,
    )


def _resolve_tor_vpn_exit(
    spec: ContainerSpec, state_path: Path | None = None
) -> ResolvedNetworkPlan:
    """Resolve TOR_VPN_EXIT archetype from spec + privacy config."""
    if not spec.privacy or not spec.privacy.vpn:
        raise ValueError(
            "tor-vpn-exit archetype requires privacy.vpn configuration"
        )

    vpn = spec.privacy.vpn
    tor_name = f"svc-{spec.name}-tor"
    vpn_name = f"svc-{spec.name}-vpn"

    # Allocate subnets for both container and tor companion
    container_alloc = allocate(spec.name, state_path=state_path)
    tor_alloc = allocate(tor_name, state_path=state_path)

    return resolve_tor_vpn_exit(
        container_name=spec.name,
        container_veth=to_veth_config(container_alloc),
        tor_veth=to_veth_config(tor_alloc),
        container_table_id=container_alloc.table_id,
        tor_table_id=tor_alloc.table_id,
        container_priority=container_alloc.priority,
        tor_priority=tor_alloc.priority,
        tor_container_name=tor_name,
        vpn_container_name=vpn_name,
        vpn_config_file=vpn.config_file,
    )


def setup_network(
    spec: ContainerSpec,
    state_path: Path | None = None,
) -> tuple[ResolvedNetworkPlan, ExecutionResult]:
    """Resolve and apply networking for a container spec.

    This is the main entry point for both CLI and MCP tools.

    Args:
        spec: Container specification.
        state_path: Override for subnet allocation state (testing).

    Returns:
        Tuple of (resolved plan, execution result).
    """
    plan = resolve_from_spec(spec, state_path=state_path)
    result = apply_plan(plan)
    return plan, result


def teardown_network(
    spec: ContainerSpec,
    state_path: Path | None = None,
) -> tuple[ResolvedNetworkPlan, ExecutionResult]:
    """Resolve and tear down networking for a container spec.

    Args:
        spec: Container specification.
        state_path: Override for subnet allocation state (testing).

    Returns:
        Tuple of (resolved plan, execution result).
    """
    plan = resolve_from_spec(spec, state_path=state_path)
    result = teardown_plan(plan)

    # Release subnet allocations for chain archetypes
    if plan.archetype in (NetworkArchetype.VPN_EXIT, NetworkArchetype.TOR_VPN_EXIT):
        release(spec.name, state_path=state_path)
        for companion in plan.companions:
            if companion.veth:
                release(companion.name, state_path=state_path)

    return plan, result
