"""
Tests for automatic dependency generation.

Tests ontology-based, pattern-based, and domain pack rules.
"""

import os
import sys
import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from services.dependency_generator import (
    generate_ontology_dependencies,
    generate_pattern_dependencies,
    generate_domain_dependencies,
    generate_all_dependencies,
    _parse_merge_key,
    _title_matches,
    PATTERN_RULES,
)


# ============================================================================
# Helpers
# ============================================================================

def _ctrl(id_: str, merge_key: str, title: str = "") -> dict:
    return {
        "id": id_,
        "control_id": id_,
        "title": title or id_,
        "generation_metadata": {"merge_group_hint": merge_key},
    }


class TestParseMergeKey:

    def test_full_key(self):
        r = _parse_merge_key("implement:api_rate_limiting:implementation:api_endpoints")
        assert r["action_type"] == "implement"
        assert r["normalized_object"] == "api_rate_limiting"
        assert r["phase"] == "implementation"
        assert r["asset_scope"] == "api_endpoints"

    def test_short_key(self):
        r = _parse_merge_key("define:access_policy:definition")
        assert r["action_type"] == "define"
        assert r["normalized_object"] == "access_policy"
        assert r["phase"] == "definition"
        assert r["asset_scope"] == ""


# ============================================================================
# Ontology Dependencies
# ============================================================================

class TestOntologyDependencies:

    def test_same_object_phase_sequence(self):
        """define:policy (phase 2) -> implement:policy (phase 4) generates prerequisite."""
        controls = [
            _ctrl("A", "define:access_policy:definition"),
            _ctrl("B", "implement:access_policy:implementation"),
        ]
        deps = generate_ontology_dependencies(controls)
        assert len(deps) == 1
        assert deps[0].source_control_id == "A"
        assert deps[0].target_control_id == "B"
        assert deps[0].dependency_type == "prerequisite"

    def test_different_objects_no_dependency(self):
        controls = [
            _ctrl("A", "define:access_policy:definition"),
            _ctrl("B", "implement:rate_limiting:implementation"),
        ]
        deps = generate_ontology_dependencies(controls)
        assert len(deps) == 0

    def test_same_phase_no_dependency(self):
        """Controls in the same phase should not generate prerequisite."""
        controls = [
            _ctrl("A", "implement:access_policy:implementation"),
            _ctrl("B", "enforce:access_policy:implementation"),
        ]
        deps = generate_ontology_dependencies(controls)
        assert len(deps) == 0

    def test_three_phase_chain(self):
        """define -> implement -> test generates 3 dependencies (A->B, A->C, B->C)."""
        controls = [
            _ctrl("A", "define:access_policy:definition"),
            _ctrl("B", "implement:access_policy:implementation"),
            _ctrl("C", "test:access_policy:testing"),
        ]
        deps = generate_ontology_dependencies(controls)
        assert len(deps) == 3
        # A -> B, A -> C, B -> C
        pairs = {(d.source_control_id, d.target_control_id) for d in deps}
        assert ("A", "B") in pairs
        assert ("A", "C") in pairs
        assert ("B", "C") in pairs


# ============================================================================
# Pattern Dependencies
# ============================================================================

class TestPatternDependencies:

    def test_define_before_implement(self):
        controls = [
            _ctrl("A", "define:firewall_policy:definition"),
            _ctrl("B", "implement:firewall_policy:implementation"),
        ]
        deps = generate_pattern_dependencies(controls)
        matching = [d for d in deps if d.source_control_id == "A" and d.target_control_id == "B"]
        assert len(matching) >= 1
        assert matching[0].dependency_type == "prerequisite"

    def test_implement_before_monitor(self):
        controls = [
            _ctrl("A", "implement:logging:implementation"),
            _ctrl("B", "monitor:logging:monitoring"),
        ]
        deps = generate_pattern_dependencies(controls)
        matching = [d for d in deps if d.source_control_id == "A" and d.target_control_id == "B"]
        assert len(matching) >= 1

    def test_no_match_different_objects(self):
        controls = [
            _ctrl("A", "define:access_policy:definition"),
            _ctrl("B", "implement:encryption:implementation"),
        ]
        deps = generate_pattern_dependencies(controls)
        assert len(deps) == 0


# ============================================================================
# Domain Packs
# ============================================================================

class TestDomainPacks:

    def test_ghv_supersedes_training(self):
        pack_dir = os.path.join(os.path.dirname(__file__), "..", "data", "domain_packs")
        controls = [
            {"id": "MC-001", "title": "GHV-Klausel im Arbeitsvertrag enthalten"},
            {"id": "MC-002", "title": "Mitarbeiter zur Vertraulichkeit geschult"},
        ]
        deps = generate_domain_dependencies(controls, pack_dir)
        matching = [d for d in deps if d.source_control_id == "MC-001" and d.target_control_id == "MC-002"]
        assert len(matching) == 1
        assert matching[0].dependency_type == "supersedes"

    def test_vvt_before_dsfa(self):
        pack_dir = os.path.join(os.path.dirname(__file__), "..", "data", "domain_packs")
        controls = [
            {"id": "GDPR-001", "title": "Verarbeitungsverzeichnis erstellt"},
            {"id": "GDPR-002", "title": "Datenschutz-Folgenabschaetzung durchgefuehrt"},
        ]
        deps = generate_domain_dependencies(controls, pack_dir)
        matching = [d for d in deps if d.source_control_id == "GDPR-001"]
        assert len(matching) == 1
        assert matching[0].dependency_type == "prerequisite"

    def test_title_matches_helper(self):
        assert _title_matches("GHV-Klausel vorhanden", ["GHV-Klausel"])
        assert not _title_matches("MFA aktiviert", ["GHV-Klausel"])


# ============================================================================
# Combined Generator
# ============================================================================

class TestGenerateAll:

    def test_deduplication(self):
        """Same dependency from ontology and pattern should be deduplicated."""
        controls = [
            _ctrl("A", "define:access_policy:definition"),
            _ctrl("B", "implement:access_policy:implementation"),
        ]
        deps, stats = generate_all_dependencies(
            controls, enable_ontology=True, enable_patterns=True, enable_domain_packs=False,
        )
        # Both generators find the same pair, dedup removes one
        assert stats["duplicates_removed"] >= 0
        assert stats["total_unique"] <= stats["total_before_dedup"]
        # At least one A->B prerequisite
        matching = [d for d in deps if d.source_control_id == "A" and d.target_control_id == "B"]
        assert len(matching) == 1

    def test_stats_populated(self):
        controls = [
            _ctrl("A", "define:policy:definition"),
            _ctrl("B", "implement:policy:implementation"),
        ]
        _, stats = generate_all_dependencies(controls, enable_domain_packs=False)
        assert "ontology_generated" in stats
        assert "pattern_generated" in stats
        assert "total_unique" in stats
