"""
Tests pour le RulesManager.

Ce module teste le chargement, la validation et la gestion des règles de codage.
"""

import json
from pathlib import Path

import pytest
import yaml

from pipeline_mco_pmsi.rules.rules_manager import CodingRule, RuleSet, RulesManager


def test_rules_manager_initialization():
    """Test l'initialisation du RulesManager."""
    manager = RulesManager()
    
    assert manager is not None
    assert manager.current_ruleset is None
    assert manager.rules_hash is None


def test_load_rules_yaml(tmp_path):
    """Test le chargement de règles depuis un fichier YAML."""
    # Créer un fichier YAML de test
    rules_file = tmp_path / "test_rules.yaml"
    rules_data = {
        "version": "1.0.0",
        "name": "Test Rules",
        "description": "Test ruleset",
        "mode": "conservateur",
        "rules": [
            {
                "rule_id": "test_001",
                "name": "Test Rule",
                "description": "A test rule",
                "category": "dp",
                "condition": {"type": "required"},
                "action": "reject_if_missing",
                "severity": "bloquant",
                "enabled": True,
            }
        ],
    }
    
    with open(rules_file, "w") as f:
        yaml.dump(rules_data, f)
    
    # Charger les règles
    manager = RulesManager()
    ruleset = manager.load_rules(rules_file)
    
    # Vérifications
    assert ruleset is not None
    assert ruleset.version == "1.0.0"
    assert ruleset.name == "Test Rules"
    assert ruleset.mode == "conservateur"
    assert len(ruleset.rules) == 1
    assert ruleset.rules[0].rule_id == "test_001"
    assert manager.rules_hash is not None
    assert len(manager.rules_hash) == 64  # SHA-256


def test_load_rules_json(tmp_path):
    """Test le chargement de règles depuis un fichier JSON."""
    # Créer un fichier JSON de test
    rules_file = tmp_path / "test_rules.json"
    rules_data = {
        "version": "1.0.0",
        "name": "Test Rules JSON",
        "description": "Test ruleset in JSON",
        "mode": "agressif",
        "rules": [
            {
                "rule_id": "test_002",
                "name": "Test Rule 2",
                "description": "Another test rule",
                "category": "das",
                "condition": {"min_evidence": 1},
                "action": "reject_if_insufficient",
                "severity": "à_revoir",
                "enabled": True,
            }
        ],
    }
    
    with open(rules_file, "w") as f:
        json.dump(rules_data, f)
    
    # Charger les règles
    manager = RulesManager()
    ruleset = manager.load_rules(rules_file)
    
    # Vérifications
    assert ruleset is not None
    assert ruleset.version == "1.0.0"
    assert ruleset.mode == "agressif"
    assert len(ruleset.rules) == 1
    assert ruleset.rules[0].rule_id == "test_002"


def test_load_rules_file_not_found():
    """Test le chargement avec un fichier inexistant."""
    manager = RulesManager()
    
    with pytest.raises(FileNotFoundError):
        manager.load_rules(Path("nonexistent.yaml"))


def test_load_rules_invalid_format(tmp_path):
    """Test le chargement avec un format invalide."""
    # Créer un fichier avec extension non supportée
    rules_file = tmp_path / "test_rules.txt"
    rules_file.write_text("invalid content")
    
    manager = RulesManager()
    
    with pytest.raises(ValueError, match="Format de fichier non supporté"):
        manager.load_rules(rules_file)


def test_generate_hash():
    """Test la génération de hash SHA-256."""
    manager = RulesManager()
    
    content1 = "test content"
    content2 = "test content"
    content3 = "different content"
    
    hash1 = manager._generate_hash(content1)
    hash2 = manager._generate_hash(content2)
    hash3 = manager._generate_hash(content3)
    
    # Même contenu = même hash
    assert hash1 == hash2
    
    # Contenu différent = hash différent
    assert hash1 != hash3
    
    # Hash SHA-256 = 64 caractères hex
    assert len(hash1) == 64
    assert all(c in "0123456789abcdef" for c in hash1)


def test_get_rules_by_category(tmp_path):
    """Test la récupération de règles par catégorie."""
    # Créer un fichier avec plusieurs règles
    rules_file = tmp_path / "test_rules.yaml"
    rules_data = {
        "version": "1.0.0",
        "name": "Test Rules",
        "mode": "conservateur",
        "rules": [
            {
                "rule_id": "dp_001",
                "name": "DP Rule",
                "description": "DP rule",
                "category": "dp",
                "condition": {},
                "action": "test",
                "enabled": True,
            },
            {
                "rule_id": "das_001",
                "name": "DAS Rule",
                "description": "DAS rule",
                "category": "das",
                "condition": {},
                "action": "test",
                "enabled": True,
            },
            {
                "rule_id": "dp_002",
                "name": "DP Rule 2",
                "description": "Another DP rule",
                "category": "dp",
                "condition": {},
                "action": "test",
                "enabled": False,  # Désactivée
            },
        ],
    }
    
    with open(rules_file, "w") as f:
        yaml.dump(rules_data, f)
    
    manager = RulesManager()
    manager.load_rules(rules_file)
    
    # Récupérer les règles DP (seulement les activées)
    dp_rules = manager.get_rules_by_category("dp")
    assert len(dp_rules) == 1
    assert dp_rules[0].rule_id == "dp_001"
    
    # Récupérer les règles DAS
    das_rules = manager.get_rules_by_category("das")
    assert len(das_rules) == 1
    assert das_rules[0].rule_id == "das_001"
    
    # Catégorie inexistante
    ccam_rules = manager.get_rules_by_category("ccam")
    assert len(ccam_rules) == 0


def test_get_rule_by_id(tmp_path):
    """Test la récupération d'une règle par ID."""
    rules_file = tmp_path / "test_rules.yaml"
    rules_data = {
        "version": "1.0.0",
        "name": "Test Rules",
        "mode": "conservateur",
        "rules": [
            {
                "rule_id": "test_001",
                "name": "Test Rule",
                "description": "Test",
                "category": "dp",
                "condition": {},
                "action": "test",
            }
        ],
    }
    
    with open(rules_file, "w") as f:
        yaml.dump(rules_data, f)
    
    manager = RulesManager()
    manager.load_rules(rules_file)
    
    # Règle existante
    rule = manager.get_rule_by_id("test_001")
    assert rule is not None
    assert rule.rule_id == "test_001"
    
    # Règle inexistante
    rule = manager.get_rule_by_id("nonexistent")
    assert rule is None


def test_is_conservative_mode(tmp_path):
    """Test la détection du mode conservateur."""
    rules_file = tmp_path / "test_rules.yaml"
    rules_data = {
        "version": "1.0.0",
        "name": "Test Rules",
        "mode": "conservateur",
        "rules": [],
    }
    
    with open(rules_file, "w") as f:
        yaml.dump(rules_data, f)
    
    manager = RulesManager()
    
    # Par défaut (pas de ruleset chargé)
    assert manager.is_conservative_mode() is True
    
    # Après chargement
    manager.load_rules(rules_file)
    assert manager.is_conservative_mode() is True
    assert manager.is_aggressive_mode() is False


def test_is_aggressive_mode(tmp_path):
    """Test la détection du mode agressif."""
    rules_file = tmp_path / "test_rules.yaml"
    rules_data = {
        "version": "1.0.0",
        "name": "Test Rules",
        "mode": "agressif",
        "rules": [],
    }
    
    with open(rules_file, "w") as f:
        yaml.dump(rules_data, f)
    
    manager = RulesManager()
    manager.load_rules(rules_file)
    
    assert manager.is_aggressive_mode() is True
    assert manager.is_conservative_mode() is False


def test_get_version_info(tmp_path):
    """Test la récupération des informations de version."""
    rules_file = tmp_path / "test_rules.yaml"
    rules_data = {
        "version": "2.0.0",
        "name": "Test Rules",
        "mode": "conservateur",
        "rules": [
            {
                "rule_id": "test_001",
                "name": "Test",
                "description": "Test",
                "category": "dp",
                "condition": {},
                "action": "test",
                "enabled": True,
            },
            {
                "rule_id": "test_002",
                "name": "Test 2",
                "description": "Test 2",
                "category": "das",
                "condition": {},
                "action": "test",
                "enabled": False,
            },
        ],
    }
    
    with open(rules_file, "w") as f:
        yaml.dump(rules_data, f)
    
    manager = RulesManager()
    
    # Sans ruleset chargé
    info = manager.get_version_info()
    assert info["version"] == "none"
    assert info["rules_count"] == 0
    
    # Avec ruleset chargé
    manager.load_rules(rules_file)
    info = manager.get_version_info()
    
    assert info["version"] == "2.0.0"
    assert info["mode"] == "conservateur"
    assert info["rules_count"] == 2
    assert info["enabled_rules_count"] == 1
    assert "hash" in info
    assert len(info["hash"]) == 64


def test_create_default_ruleset():
    """Test la création d'un jeu de règles par défaut."""
    manager = RulesManager()
    ruleset = manager.create_default_ruleset()
    
    assert ruleset is not None
    assert ruleset.version == "1.0.0"
    assert ruleset.mode == "conservateur"
    assert len(ruleset.rules) > 0
    assert manager.rules_hash is not None
    
    # Vérifier quelques règles par défaut
    rule_ids = [rule.rule_id for rule in ruleset.rules]
    assert "dp_001" in rule_ids
    assert "dp_002" in rule_ids
    assert "neg_001" in rule_ids
    assert "ccam_001" in rule_ids


def test_save_rules_yaml(tmp_path):
    """Test la sauvegarde de règles en YAML."""
    manager = RulesManager()
    manager.create_default_ruleset()
    
    output_file = tmp_path / "saved_rules.yaml"
    manager.save_rules(output_file, format="yaml")
    
    assert output_file.exists()
    
    # Recharger et vérifier
    manager2 = RulesManager()
    ruleset2 = manager2.load_rules(output_file)
    
    assert ruleset2.version == "1.0.0"
    assert len(ruleset2.rules) == len(manager.current_ruleset.rules)


def test_save_rules_json(tmp_path):
    """Test la sauvegarde de règles en JSON."""
    manager = RulesManager()
    manager.create_default_ruleset()
    
    output_file = tmp_path / "saved_rules.json"
    manager.save_rules(output_file, format="json")
    
    assert output_file.exists()
    
    # Recharger et vérifier
    manager2 = RulesManager()
    ruleset2 = manager2.load_rules(output_file)
    
    assert ruleset2.version == "1.0.0"
    assert len(ruleset2.rules) == len(manager.current_ruleset.rules)


def test_save_rules_without_ruleset():
    """Test la sauvegarde sans ruleset chargé."""
    manager = RulesManager()
    
    with pytest.raises(ValueError, match="Aucun jeu de règles chargé"):
        manager.save_rules(Path("output.yaml"))


def test_coding_rule_validation():
    """Test la validation des règles de codage."""
    # Règle valide
    rule = CodingRule(
        rule_id="test_001",
        name="Test",
        description="Test rule",
        category="dp",
        condition={},
        action="test",
        severity="bloquant",
    )
    assert rule.severity == "bloquant"
    
    # Sévérité invalide
    with pytest.raises(ValueError, match="Sévérité invalide"):
        CodingRule(
            rule_id="test_002",
            name="Test",
            description="Test",
            category="dp",
            condition={},
            action="test",
            severity="invalid",
        )
    
    # Catégorie invalide
    with pytest.raises(ValueError, match="Catégorie invalide"):
        CodingRule(
            rule_id="test_003",
            name="Test",
            description="Test",
            category="invalid",
            condition={},
            action="test",
        )


def test_ruleset_validation():
    """Test la validation des jeux de règles."""
    # RuleSet valide
    ruleset = RuleSet(
        version="1.0.0",
        name="Test",
        mode="conservateur",
        rules=[],
    )
    assert ruleset.mode == "conservateur"
    
    # Mode invalide
    with pytest.raises(ValueError, match="Mode invalide"):
        RuleSet(
            version="1.0.0",
            name="Test",
            mode="invalid",
            rules=[],
        )
