"""End-to-end golden workflow tests.

These tests validate the complete audit workflow from model training through
configuration to final audit output matches golden fixtures. Ensures that
the entire pipeline produces byte-identical results for regulatory compliance.

Golden fixtures are stored in examples/german_credit_golden/ and represent
the expected output for a specific configuration and dataset.
"""


class TestE2EGoldenWorkflow:
    """Test complete audit workflows against golden fixtures."""

    def test_german_credit_e2e_smoke_test(self, tmp_path):
        """Full German Credit workflow completes successfully.

        This test validates the complete audit pipeline works end-to-end:
        1. Load German Credit dataset
        2. Train model
        3. Run audit
        4. Generate HTML report

        Basic smoke test to ensure the pipeline doesn't crash.
        """
        from sklearn.linear_model import LogisticRegression

        from glassalpha.api import from_model
        from glassalpha.datasets import get_german_credit_schema, load_german_credit

        # Load German Credit data and schema
        data = load_german_credit()
        schema = get_german_credit_schema()

        # Prepare data (German Credit has specific columns)
        # Remove protected attributes for model training
        protected_cols = ["gender", "age_group"]
        feature_cols = [col for col in data.columns if col not in protected_cols + ["credit_risk"]]
        target_col = "credit_risk"

        # Convert categorical features to numeric for sklearn compatibility
        X = data[feature_cols].copy()
        # Convert categorical columns to numeric codes
        for col in X.columns:
            if X[col].dtype == "object":
                X[col] = X[col].astype("category").cat.codes

        y = data[target_col]
        protected_attributes = {col: data[col] for col in protected_cols}

        # Train model
        model = LogisticRegression(random_state=42, max_iter=1000)
        model.fit(X, y)

        # Run audit
        result = from_model(
            model=model,
            X=X,
            y=y,
            feature_names=feature_cols,
            protected_attributes=protected_attributes,
            random_seed=42,
            explain=True,
            calibration=True,
        )

        # Verify audit completed successfully
        assert result is not None
        # Check that we have the expected components
        assert hasattr(result, "performance")
        assert hasattr(result, "fairness")
        assert hasattr(result, "explanations")
        # Check that performance has some metrics
        assert len(result.performance) > 0

        # Generate HTML report
        html_content = result._repr_html_()
        assert len(html_content) > 100  # Should have substantial content
        assert "div" in html_content.lower()  # Should contain HTML div tags

    def test_german_credit_manifest_structure(self, tmp_path):
        """German Credit audit manifest has expected structure.

        Validates that the audit manifest contains the expected metadata fields.
        """
        from sklearn.linear_model import LogisticRegression

        from glassalpha.api import from_model
        from glassalpha.datasets import get_german_credit_schema, load_german_credit

        # Load German Credit data and schema
        data = load_german_credit()
        schema = get_german_credit_schema()

        # Prepare data
        protected_cols = ["gender", "age_group"]
        feature_cols = [col for col in data.columns if col not in protected_cols + ["credit_risk"]]
        target_col = "credit_risk"

        # Convert categorical features to numeric for sklearn compatibility
        X = data[feature_cols].copy()
        # Convert categorical columns to numeric codes
        for col in X.columns:
            if X[col].dtype == "object":
                X[col] = X[col].astype("category").cat.codes

        y = data[target_col]
        protected_attributes = {col: data[col] for col in protected_cols}

        # Train model
        model = LogisticRegression(random_state=42, max_iter=1000)
        model.fit(X, y)

        # Run audit
        result = from_model(
            model=model,
            X=X,
            y=y,
            feature_names=feature_cols,
            protected_attributes=protected_attributes,
            random_seed=42,
            explain=True,
            calibration=True,
        )

        # Verify manifest structure
        manifest = result.manifest
        assert "glassalpha_version" in manifest
        assert "schema_version" in manifest
        assert "random_seed" in manifest
        assert "model_type" in manifest
        assert "n_samples" in manifest
        assert "n_features" in manifest
        assert manifest["random_seed"] == 42
        assert manifest["model_type"] == "logistic_regression"

    def test_german_credit_audit_deterministic_across_runs(self, tmp_path):
        """German Credit audit produces identical results across multiple runs.

        Validates that the audit pipeline is fully deterministic - same inputs
        produce identical outputs across runs, platforms, and time.
        """
        from sklearn.linear_model import LogisticRegression

        from glassalpha.api import from_model
        from glassalpha.datasets import get_german_credit_schema, load_german_credit

        # Load German Credit data and schema
        data = load_german_credit()
        schema = get_german_credit_schema()

        # Prepare data
        protected_cols = ["gender", "age_group"]
        feature_cols = [col for col in data.columns if col not in protected_cols + ["credit_risk"]]
        target_col = "credit_risk"

        # Convert categorical features to numeric for sklearn compatibility
        X = data[feature_cols].copy()
        # Convert categorical columns to numeric codes
        for col in X.columns:
            if X[col].dtype == "object":
                X[col] = X[col].astype("category").cat.codes

        y = data[target_col]
        protected_attributes = {col: data[col] for col in protected_cols}

        # Train model once
        model = LogisticRegression(random_state=42, max_iter=1000)
        model.fit(X, y)

        # Run audit multiple times
        results = []
        for i in range(3):
            result = from_model(
                model=model,
                X=X,
                y=y,
                feature_names=feature_cols,
                protected_attributes=protected_attributes,
                random_seed=42,  # Same seed each time
                explain=True,
                calibration=True,
            )
            results.append(result)

        # All results should have identical IDs (deterministic)
        result_ids = [result.id for result in results]
        assert len(set(result_ids)) == 1, f"Non-deterministic results: {result_ids}"

        # Note: Manifests contain timestamps, so they won't be identical across runs
        # but the audit results (IDs) should be deterministic
        manifests = [result.manifest for result in results]
        # Just verify that manifests exist and have the expected structure
        for manifest in manifests:
            assert "glassalpha_version" in manifest
            assert "random_seed" in manifest
            assert manifest["random_seed"] == 42

        # All HTML outputs should be identical
        html_outputs = [result._repr_html_() for result in results]
        assert len(set(html_outputs)) == 1, "Non-deterministic HTML output"

    def test_german_credit_audit_seed_consistency(self, tmp_path):
        """German Credit audit with same seed produces identical results.

        Validates that the audit pipeline is deterministic for the same seed.
        """
        from sklearn.linear_model import LogisticRegression

        from glassalpha.api import from_model
        from glassalpha.datasets import get_german_credit_schema, load_german_credit

        # Load German Credit data and schema
        data = load_german_credit()
        schema = get_german_credit_schema()

        # Prepare data
        protected_cols = ["gender", "age_group"]
        feature_cols = [col for col in data.columns if col not in protected_cols + ["credit_risk"]]
        target_col = "credit_risk"

        # Convert categorical features to numeric for sklearn compatibility
        X = data[feature_cols].copy()
        # Convert categorical columns to numeric codes
        for col in X.columns:
            if X[col].dtype == "object":
                X[col] = X[col].astype("category").cat.codes

        y = data[target_col]
        protected_attributes = {col: data[col] for col in protected_cols}

        # Train model once
        model = LogisticRegression(random_state=42, max_iter=1000)
        model.fit(X, y)

        # Run audits with same seed multiple times
        result_1 = from_model(
            model=model,
            X=X,
            y=y,
            feature_names=feature_cols,
            protected_attributes=protected_attributes,
            random_seed=42,
            explain=True,
            calibration=True,
        )

        result_2 = from_model(
            model=model,
            X=X,
            y=y,
            feature_names=feature_cols,
            protected_attributes=protected_attributes,
            random_seed=42,  # Same seed
            explain=True,
            calibration=True,
        )

        # Results should be identical for same seed
        assert result_1.id == result_2.id, "Same seed should produce identical results"

        # Manifests should have same structure and key fields
        assert result_1.manifest["random_seed"] == result_2.manifest["random_seed"]
        assert result_1.manifest["model_type"] == result_2.manifest["model_type"]
