"""Tests for infra_diagram.parsers.compose."""

from __future__ import annotations

import tempfile
from pathlib import Path

from infra_diagram.models import NodeType, SourceType
from infra_diagram.parsers.compose import _IMAGE_TYPE_MAP, _infer_type_from_image, parse_compose

# ─── Image type inference ────────────────────────────────────────────

class TestImageTypeMap:
    def test_databases(self):
        assert _IMAGE_TYPE_MAP["postgres"] == NodeType.DATABASE
        assert _IMAGE_TYPE_MAP["mysql"] == NodeType.DATABASE
        assert _IMAGE_TYPE_MAP["mongo"] == NodeType.DATABASE

    def test_caches(self):
        assert _IMAGE_TYPE_MAP["redis"] == NodeType.CACHE
        assert _IMAGE_TYPE_MAP["memcached"] == NodeType.CACHE

    def test_queues(self):
        assert _IMAGE_TYPE_MAP["rabbitmq"] == NodeType.QUEUE
        assert _IMAGE_TYPE_MAP["kafka"] == NodeType.QUEUE

    def test_load_balancers(self):
        assert _IMAGE_TYPE_MAP["nginx"] == NodeType.LOAD_BALANCER
        assert _IMAGE_TYPE_MAP["traefik"] == NodeType.LOAD_BALANCER

    def test_monitoring(self):
        assert _IMAGE_TYPE_MAP["grafana"] == NodeType.MONITORING
        assert _IMAGE_TYPE_MAP["prometheus"] == NodeType.MONITORING


class TestInferType:
    def test_postgres_image(self):
        assert _infer_type_from_image("postgres:15") == NodeType.DATABASE

    def test_redis_image(self):
        assert _infer_type_from_image("redis:7-alpine") == NodeType.CACHE

    def test_nginx_image(self):
        assert _infer_type_from_image("nginx:latest") == NodeType.LOAD_BALANCER

    def test_custom_image(self):
        assert _infer_type_from_image("myapp:v1.0") == NodeType.CONTAINER

    def test_empty_image(self):
        assert _infer_type_from_image("") == NodeType.CONTAINER

    def test_registry_path_image(self):
        assert _infer_type_from_image("docker.io/library/redis:latest") == NodeType.CACHE


# ─── Parsing ─────────────────────────────────────────────────────────

SIMPLE_COMPOSE = """version: "3.8"
services:
  web:
    image: node:18
    ports:
      - "3000:3000"
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: mydb
"""

COMPOSE_WITH_DEPS = """version: "3.8"
services:
  app:
    image: myapp:latest
    depends_on:
      - db
      - cache
  db:
    image: postgres:15
  cache:
    image: redis:7
"""

COMPOSE_WITH_VOLUMES = """version: "3.8"
services:
  db:
    image: postgres:15
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:
"""

COMPOSE_WITH_NETWORKS = """version: "3.8"
services:
  web:
    image: nginx:alpine
    networks:
      - frontend
  api:
    image: node:18
    networks:
      - frontend
      - backend
networks:
  frontend:
  backend:
"""


class TestParseCompose:
    def _write_compose(self, tmpdir: str, content: str, name: str = "docker-compose.yml") -> str:
        fpath = Path(tmpdir) / name
        fpath.write_text(content, encoding="utf-8")
        return tmpdir

    def test_basic_services(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE)
            diagram = parse_compose(d)
            assert diagram.source_type == SourceType.DOCKER_COMPOSE
            assert diagram.node_count >= 2
            web = diagram.get_node("service/web")
            assert web is not None
            assert web.provider == "docker"

    def test_image_type_inference(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE)
            diagram = parse_compose(d)
            db = diagram.get_node("service/db")
            assert db is not None
            assert db.node_type == NodeType.DATABASE

    def test_depends_on_edges(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, COMPOSE_WITH_DEPS)
            diagram = parse_compose(d)
            app_edges = [e for e in diagram.edges if e.source == "service/app" and e.label == "depends on"]
            assert len(app_edges) == 2

    def test_volume_nodes(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, COMPOSE_WITH_VOLUMES)
            diagram = parse_compose(d)
            vol = diagram.get_node("volume/pgdata")
            assert vol is not None
            assert vol.node_type == NodeType.VOLUME

    def test_volume_mount_edges(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, COMPOSE_WITH_VOLUMES)
            diagram = parse_compose(d)
            mount_edges = [e for e in diagram.edges if e.label == "mounts"]
            assert len(mount_edges) >= 1

    def test_network_nodes(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, COMPOSE_WITH_NETWORKS)
            diagram = parse_compose(d)
            net = diagram.get_node("network/frontend")
            assert net is not None
            assert net.node_type == NodeType.NETWORK

    def test_network_edges(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, COMPOSE_WITH_NETWORKS)
            diagram = parse_compose(d)
            net_edges = [e for e in diagram.edges if e.label == "in" and "network/" in e.target]
            assert len(net_edges) >= 3  # web(1) + api(2)

    def test_ports_extracted(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE)
            diagram = parse_compose(d)
            web = diagram.get_node("service/web")
            assert web.properties.get("ports") is not None

    def test_environment_counted(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE)
            diagram = parse_compose(d)
            db = diagram.get_node("service/db")
            assert db.properties.get("env_count", 0) >= 1

    def test_empty_directory(self):
        with tempfile.TemporaryDirectory() as d:
            diagram = parse_compose(d)
            assert diagram.node_count == 0

    def test_compose_yaml_filename(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE, "compose.yml")
            diagram = parse_compose(d)
            assert diagram.node_count >= 2

    def test_single_file_mode(self):
        with tempfile.TemporaryDirectory() as d:
            fpath = Path(d) / "docker-compose.yml"
            fpath.write_text(SIMPLE_COMPOSE, encoding="utf-8")
            diagram = parse_compose(str(fpath))
            assert diagram.node_count >= 2

    def test_services_grouped(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE)
            diagram = parse_compose(d)
            assert len(diagram.groups) > 0

    def test_build_service(self):
        compose_build = """version: "3.8"
services:
  app:
    build: ./app
    ports:
      - "8080:8080"
"""
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, compose_build)
            diagram = parse_compose(d)
            app = diagram.get_node("service/app")
            assert app is not None
            assert app.node_type == NodeType.CONTAINER
            assert app.properties.get("build") is not None

    def test_source_files_tracked(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, SIMPLE_COMPOSE)
            diagram = parse_compose(d)
            assert len(diagram.source_files) == 1

    def test_dict_depends_on(self):
        compose_dict_deps = """version: "3.8"
services:
  app:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:15
"""
        with tempfile.TemporaryDirectory() as d:
            self._write_compose(d, compose_dict_deps)
            diagram = parse_compose(d)
            dep_edges = [e for e in diagram.edges if e.source == "service/app" and e.label == "depends on"]
            assert len(dep_edges) == 1
