"""Tests for new architecture components (Interpreter, Signal Generator, Report).

Tests the refactored pipeline with clean separation of concerns:
- Model: Pure math (HMM)
- Interpreter: Domain knowledge (regime labels)
- Signal Generator: Trading logic (position sizing)
- Report: Metrics and visualizations
"""

import pytest

# Skip entire module - reporting.comprehensive_report module doesn't exist
pytest.skip("Skipping legacy test - reporting module not implemented", allow_module_level=True)
import pandas as pd
import numpy as np

from hidden_regime.config.interpreter import InterpreterConfiguration
from hidden_regime.config.signal_generation import SignalGenerationConfiguration
from hidden_regime.interpreter.financial import FinancialInterpreter
from hidden_regime.signal_generation.financial import FinancialSignalGenerator
from hidden_regime.reporting.comprehensive_report import ComprehensiveReport
from hidden_regime.pipeline.schemas import (
    validate_component_output,
    assert_valid_output,
    MODEL_OUTPUT_SCHEMA,
    INTERPRETER_OUTPUT_SCHEMA,
    SIGNAL_OUTPUT_SCHEMA,
)


@pytest.fixture
def model_output():
    """Create mock HMM model output."""
    dates = pd.date_range("2023-01-01", periods=100)
    return pd.DataFrame(
        {
            "state": np.random.randint(0, 3, 100),
            "confidence": np.random.uniform(0.6, 1.0, 100),
            "state_probabilities": [np.random.dirichlet([1, 1, 1]) for _ in range(100)],
            "emission_means": [np.array([-0.001, 0.0005, 0.002]) for _ in range(100)],
            "emission_stds": [np.array([0.01, 0.015, 0.02]) for _ in range(100)],
        },
        index=dates,
    )


@pytest.fixture
def interpreter_output(model_output):
    """Create mock interpreter output."""
    output = model_output.copy()
    output["regime_label"] = output["state"].map({0: "Bear", 1: "Sideways", 2: "Bull"})
    output["regime_type"] = output["regime_label"].map(
        {"Bear": "bearish", "Sideways": "sideways", "Bull": "bullish"}
    )
    output["regime_color"] = output["regime_label"].map(
        {"Bear": "#C62828", "Sideways": "#F57F17", "Bull": "#2E7D32"}
    )
    output["regime_strength"] = output["confidence"]
    output["regime_return"] = np.random.uniform(-0.10, 0.20, len(output))
    output["regime_volatility"] = np.random.uniform(0.10, 0.40, len(output))
    return output


@pytest.fixture
def price_data():
    """Create mock price data."""
    dates = pd.date_range("2023-01-01", periods=100)
    prices = 100 * np.exp(np.cumsum(np.random.normal(0.001, 0.02, 100)))
    return pd.DataFrame({"close": prices}, index=dates)


class TestInterpreter:
    """Tests for Interpreter component."""

    def test_interpreter_initialization(self):
        """Test interpreter can be created."""
        config = InterpreterConfiguration(n_states=3)
        interpreter = FinancialInterpreter(config)
        assert interpreter.config.n_states == 3

    def test_interpreter_output_schema(self, model_output):
        """Test interpreter produces valid schema."""
        config = InterpreterConfiguration(n_states=3)
        interpreter = FinancialInterpreter(config)
        output = interpreter.update(model_output)

        # Validate output matches schema
        result = validate_component_output("interpreter", output, strict=False)
        assert result["valid"], f"Validation errors: {result['errors']}"

    def test_interpreter_adds_regime_labels(self, model_output):
        """Test interpreter correctly adds regime labels."""
        config = InterpreterConfiguration(n_states=3)
        interpreter = FinancialInterpreter(config)
        output = interpreter.update(model_output)

        assert "regime_label" in output.columns
        assert "regime_type" in output.columns
        assert "regime_color" in output.columns
        assert "regime_strength" in output.columns

    def test_interpreter_manual_labels(self, model_output):
        """Test interpreter with manual label override."""
        config = InterpreterConfiguration(
            n_states=3,
            force_regime_labels=["Down", "Neutral", "Up"],
            acknowledge_override=True,
        )
        interpreter = FinancialInterpreter(config)
        output = interpreter.update(model_output)

        # Check that manual labels are used
        unique_labels = output["regime_label"].unique()
        assert set(unique_labels).issubset({"Down", "Neutral", "Up"})


class TestSignalGenerator:
    """Tests for Signal Generator component."""

    def test_signal_generator_initialization(self):
        """Test signal generator can be created."""
        config = SignalGenerationConfiguration(strategy_type="regime_following")
        generator = FinancialSignalGenerator(config)
        assert generator.config.strategy_type == "regime_following"

    def test_signal_generator_output_schema(self, interpreter_output):
        """Test signal generator produces valid schema."""
        config = SignalGenerationConfiguration(strategy_type="regime_following")
        generator = FinancialSignalGenerator(config)
        output = generator.update(interpreter_output)

        # Validate output matches schema
        result = validate_component_output("signals", output, strict=False)
        assert result["valid"], f"Validation errors: {result['errors']}"

    def test_signal_generator_creates_signals(self, interpreter_output):
        """Test signal generator creates position signals."""
        config = SignalGenerationConfiguration(strategy_type="regime_following")
        generator = FinancialSignalGenerator(config)
        output = generator.update(interpreter_output)

        assert "base_signal" in output.columns
        assert "signal_strength" in output.columns
        assert "position_size" in output.columns
        assert "signal_valid" in output.columns

        # Check signal ranges
        assert output["base_signal"].min() >= -1.0
        assert output["base_signal"].max() <= 1.0

    def test_signal_generator_strategies(self, interpreter_output):
        """Test different signal generation strategies."""
        strategies = ["regime_following", "regime_contrarian", "confidence_weighted"]

        for strategy in strategies:
            config = SignalGenerationConfiguration(strategy_type=strategy)
            generator = FinancialSignalGenerator(config)
            output = generator.update(interpreter_output)

            assert len(output) == len(interpreter_output)
            assert "position_size" in output.columns


class TestReport:
    """Tests for Report component."""

    def test_report_initialization(self):
        """Test report can be created."""
        from hidden_regime.config.report import ReportConfiguration

        config = ReportConfiguration()
        report = ComprehensiveReport(config)
        assert report.config.output_format == "markdown"

    def test_report_generation(self, interpreter_output, price_data):
        """Test report generates output."""
        from hidden_regime.config.report import ReportConfiguration

        config = ReportConfiguration()
        report = ComprehensiveReport(config)

        # Generate report
        output = report.update(
            interpreter_output=interpreter_output,
            raw_data=price_data,
        )

        assert isinstance(output, str)
        assert len(output) > 0
        assert "Regime" in output

    def test_report_with_signals(self, interpreter_output, price_data):
        """Test report including signal information."""
        from hidden_regime.config.report import ReportConfiguration
        from hidden_regime.config.signal_generation import SignalGenerationConfiguration

        # Create signal output
        sig_config = SignalGenerationConfiguration()
        generator = FinancialSignalGenerator(sig_config)
        signals = generator.update(interpreter_output)

        # Generate report with signals
        rep_config = ReportConfiguration()
        report = ComprehensiveReport(rep_config)
        output = report.update(
            interpreter_output=interpreter_output,
            signals=signals,
            raw_data=price_data,
        )

        assert isinstance(output, str)
        assert "Signal" in output


class TestPipelineConfiguration:
    """Tests for PipelineConfiguration master object."""

    def test_pipeline_config_creation(self):
        """Test PipelineConfiguration can be created."""
        from hidden_regime.config.pipeline import PipelineConfiguration

        config = PipelineConfiguration()
        assert config.model_config.n_states == 3
        assert config.interpreter_config.n_states == 3

    def test_pipeline_config_validation(self):
        """Test PipelineConfiguration validates state counts."""
        from hidden_regime.config.pipeline import PipelineConfiguration
        from hidden_regime.config.model import HMMConfig

        # Create config with mismatched state counts
        with pytest.raises(ValueError, match="must match"):
            PipelineConfiguration(
                model_config=HMMConfig(n_states=5),
                interpreter_config=InterpreterConfiguration(n_states=3),
            )

    def test_pipeline_config_serialization(self):
        """Test PipelineConfiguration can be serialized."""
        from hidden_regime.config.pipeline import PipelineConfiguration

        config = PipelineConfiguration()
        config_dict = config.to_dict()

        # Recreate from dict
        config2 = PipelineConfiguration.from_dict(config_dict)
        assert config2.model_config.n_states == config.model_config.n_states


class TestOutputSchemas:
    """Tests for output schema validation."""

    def test_model_schema_validation(self, model_output):
        """Test model output schema validation."""
        result = validate_component_output("model", model_output)
        assert result["valid"]

    def test_interpreter_schema_validation(self, interpreter_output):
        """Test interpreter output schema validation."""
        result = validate_component_output("interpreter", interpreter_output)
        assert result["valid"]

    def test_schema_missing_columns(self):
        """Test schema validation catches missing columns."""
        df = pd.DataFrame({"state": [0, 1, 2]})

        result = validate_component_output("model", df)
        assert not result["valid"]
        assert len(result["errors"]) > 0

    def test_assert_valid_output(self, model_output):
        """Test assert_valid_output raises on invalid."""
        # Should not raise
        assert_valid_output("model", model_output)

        # Should raise
        invalid_df = pd.DataFrame({"wrong_column": [1, 2, 3]})
        with pytest.raises(ValueError):
            assert_valid_output("model", invalid_df)


class TestBackwardCompatibility:
    """Tests for backward compatibility with old Analysis component."""

    def test_interpreter_output_has_analysis_columns(self, interpreter_output):
        """Test interpreter output has columns expected by old code."""
        # Old code might expect these columns
        expected_cols = ["regime_label", "confidence", "state"]
        for col in expected_cols:
            assert col in interpreter_output.columns


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
