"""Tests for XAI Suite orchestrator."""

import pytest
import pandas as pd

from xai_suite import XAIOrchestrator


class TestOrchestratorInitialization:
    """Tests for XAIOrchestrator initialization."""

    def test_initialization(self, trained_classifier, iris_data):
        """Test basic orchestrator initialization."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        assert orchestrator is not None
        assert orchestrator.model == trained_classifier

    def test_requires_dataframe(self, trained_classifier, iris_data):
        """Test that orchestrator requires pandas DataFrame."""
        X, _ = iris_data
        X_array = X.values  # Convert to numpy array

        with pytest.raises(ValueError, match="pandas DataFrame"):
            XAIOrchestrator(trained_classifier, X_array)

    def test_requires_column_names(self, trained_classifier, iris_data):
        """Test that DataFrame must have column names."""
        X, _ = iris_data
        # Create DataFrame without proper column names
        X_no_names = pd.DataFrame(X.values)

        # This should work since pandas assigns integer column names by default
        orchestrator = XAIOrchestrator(trained_classifier, X_no_names)
        assert orchestrator is not None


class TestOrchestratorExplainers:
    """Tests for adding and using explainers."""

    def test_add_shap_explainer(self, trained_classifier, iris_data):
        """Test adding SHAP explainer."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")

        assert "shap" in orchestrator.explainers

    def test_add_lime_explainer(self, trained_classifier, iris_data):
        """Test adding LIME explainer."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("lime", {"mode": "classification"})

        assert "lime" in orchestrator.explainers

    def test_add_multiple_explainers(self, trained_classifier, iris_data):
        """Test adding multiple explainers."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")
        orchestrator.add_explainer("lime", {"mode": "classification"})

        assert "shap" in orchestrator.explainers
        assert "lime" in orchestrator.explainers
        assert len(orchestrator.explainers) == 2

    def test_add_invalid_explainer(self, trained_classifier, iris_data):
        """Test adding invalid explainer raises error."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)

        with pytest.raises(ValueError):
            orchestrator.add_explainer("invalid_explainer")

    def test_remove_explainer(self, trained_classifier, iris_data):
        """Test removing explainer."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")
        orchestrator.remove_explainer("shap")

        assert "shap" not in orchestrator.explainers


class TestOrchestratorExplain:
    """Tests for explain method."""

    def test_explain_with_shap(self, trained_classifier, iris_data, sample_instance):
        """Test explain with SHAP."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")

        result = orchestrator.explain(sample_instance)

        assert "shap" in result
        assert "feature_names" in result["shap"]
        assert "importance_scores" in result["shap"]

    def test_explain_with_lime(self, trained_classifier, iris_data, sample_instance):
        """Test explain with LIME."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("lime", {"mode": "classification"})

        result = orchestrator.explain(sample_instance)

        assert "lime" in result
        assert "feature_names" in result["lime"]
        assert "importance_scores" in result["lime"]

    def test_explain_with_multiple_explainers(self, trained_classifier, iris_data, sample_instance):
        """Test explain with multiple explainers."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")
        orchestrator.add_explainer("lime", {"mode": "classification"})

        result = orchestrator.explain(sample_instance)

        assert "shap" in result
        assert "lime" in result

    def test_explain_specific_methods(self, trained_classifier, iris_data, sample_instance):
        """Test explain with specific methods only."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")
        orchestrator.add_explainer("lime", {"mode": "classification"})

        result = orchestrator.explain(sample_instance, methods=["shap"])

        assert "shap" in result
        assert "lime" not in result

    def test_explain_without_explainers_returns_empty(self, trained_classifier, iris_data, sample_instance):
        """Test that explain without explainers returns empty result."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)

        result = orchestrator.explain(sample_instance)
        # Without explainers, result should be empty or minimal
        assert isinstance(result, dict)


class TestOrchestratorIA:
    """Tests for IA integration."""

    def test_enable_ia(self, trained_classifier, iris_data):
        """Test enabling IA."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.enable_ia(
            endpoint="http://localhost:8000",
            api_key="test-key"
        )

        assert orchestrator._ia_client is not None

    def test_enable_ia_with_domain(self, trained_classifier, iris_data):
        """Test enabling IA with domain."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.enable_ia(
            endpoint="http://localhost:8000",
            api_key="test-key",
            domain="cybersecurity"
        )

        assert orchestrator._ia_client is not None
        assert orchestrator._ia_client.domain == "cybersecurity"

    def test_disable_ia(self, trained_classifier, iris_data):
        """Test disabling IA."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.enable_ia(
            endpoint="http://localhost:8000",
            api_key="test-key"
        )
        orchestrator.disable_ia()

        assert orchestrator._ia_client is None

    def test_set_domain(self, trained_classifier, iris_data):
        """Test setting domain."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.enable_ia(
            endpoint="http://localhost:8000",
            api_key="test-key",
            domain="generic"
        )

        orchestrator.set_domain("cybersecurity")

        assert orchestrator._ia_client.domain == "cybersecurity"

    def test_set_domain_without_ia_stores_locally(self, trained_classifier, iris_data):
        """Test setting domain without IA stores domain locally."""
        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)

        orchestrator.set_domain("cybersecurity")
        # Domain is stored locally even without IA
        assert orchestrator._domain == "cybersecurity"
