"""Generate docker-compose.yml for a simulation topology."""

from __future__ import annotations

import os

from jinja2 import Template

from .scenario import Scenario
from .topology import SimTopology

# Image name for the pre-built FIPS test image.
# The runner builds this once before starting containers.
FIPS_SIM_IMAGE = "fips-test:latest"

# Jinja2 template for the compose file.
# Uses a pre-built image instead of per-service build to support large topologies.
_COMPOSE_TEMPLATE = Template(
    """\
networks:
  fips-net:
    driver: bridge
    ipam:
      config:
        - subnet: {{ subnet }}

x-fips-common: &fips-common
  image: {{ image }}
  cap_add:
    - NET_ADMIN
    - NET_RAW
  devices:
    - /dev/net/tun:/dev/net/tun
  sysctls:
    - net.ipv6.conf.all.disable_ipv6=0
  restart: "no"
  env_file:
    - ./npubs.env
  environment:
    - RUST_LOG={{ rust_log }}
    - RUST_BACKTRACE=1
    - FIPS_TEST_MODE=chaos

services:
{% for node in nodes %}
  {{ node.node_id }}:
    <<: *fips-common
    container_name: fips-node-{{ node.node_id }}
    hostname: {{ node.node_id }}
    volumes:
      - ./{{ node.node_id }}.yaml:/etc/fips/fips.yaml:ro
      - {{ resolv_conf }}:/etc/resolv.conf:ro
    networks:
      fips-net:
        ipv4_address: {{ node.docker_ip }}
{% endfor %}
"""
)


def generate_compose(
    topology: SimTopology,
    scenario: Scenario,
    output_dir: str,
) -> str:
    """Render docker-compose.yml and write to output_dir. Returns the file path."""
    os.makedirs(output_dir, exist_ok=True)

    nodes = [topology.nodes[nid] for nid in sorted(topology.nodes)]

    # Absolute path to the shared resolv.conf (bind-mounted into containers
    # so Docker's runtime resolv.conf generation doesn't overwrite it).
    resolv_conf = os.path.normpath(
        os.path.join(os.path.dirname(__file__), "..", "..", "docker", "resolv.conf")
    )

    content = _COMPOSE_TEMPLATE.render(
        subnet=scenario.topology.subnet,
        rust_log=scenario.logging.rust_log,
        image=FIPS_SIM_IMAGE,
        nodes=nodes,
        resolv_conf=resolv_conf,
    )

    path = os.path.join(output_dir, "docker-compose.yml")
    with open(path, "w") as f:
        f.write(content)

    return path
