"""Tests for Scope exploration module."""

from __future__ import annotations

import polars as pl
from tradepose_client.analysis.trades import TradesAnalyzer
from tradepose_client.analysis.trades.scope import (
    AvailableOptions,
    OptionSummary,
    ScopeConfig,
    ScopeResult,
    available_directions,
    available_exit_reasons,
    available_strategies,
)


class TestAvailableOptions:
    """Tests for available options exploration."""

    def test_available_strategies(self, sample_trades_df: pl.DataFrame) -> None:
        """Test listing available strategies."""
        options = available_strategies(sample_trades_df)

        assert isinstance(options, AvailableOptions)
        assert options.category == "Strategies"
        assert len(options.options) > 0
        assert options.total_trades == len(sample_trades_df)

    def test_available_exit_reasons(self, sample_trades_df: pl.DataFrame) -> None:
        """Test listing available exit reasons."""
        options = available_exit_reasons(sample_trades_df)

        assert isinstance(options, AvailableOptions)
        assert options.category == "Exit Reasons"
        # Should have exit reason labels
        for opt in options.options:
            assert opt.label != ""

    def test_available_directions(self, sample_trades_df: pl.DataFrame) -> None:
        """Test listing available directions."""
        options = available_directions(sample_trades_df)

        assert isinstance(options, AvailableOptions)
        assert options.category == "Directions"
        # Should have Long and Short labels
        labels = {opt.label for opt in options.options}
        assert labels.issubset({"Long", "Short"})

    def test_option_summary_str(self) -> None:
        """Test OptionSummary string representation."""
        opt = OptionSummary(
            value="Strategy_A",
            label="Strategy_A",
            count=100,
            pct=0.5,
            total_pnl=1000.0,
            avg_pnl=10.0,
            total_pnl_pct=0.05,
            avg_pnl_pct=0.0005,
            win_rate=0.6,
        )

        str_repr = str(opt)
        assert "Strategy_A" in str_repr
        assert "100" in str_repr
        assert "50.0%" in str_repr
        assert "60.0%" in str_repr
        assert "5.00%" in str_repr  # total_pnl_pct

    def test_available_options_str(self, sample_trades_df: pl.DataFrame) -> None:
        """Test AvailableOptions string representation."""
        options = available_strategies(sample_trades_df)

        str_repr = str(options)
        assert "Strategies" in str_repr
        assert "options" in str_repr

    def test_available_options_to_dataframe(self, sample_trades_df: pl.DataFrame) -> None:
        """Test AvailableOptions to_dataframe method."""
        options = available_strategies(sample_trades_df)

        df = options.to_dataframe()

        assert isinstance(df, pl.DataFrame)
        assert len(df) == len(options.options)
        assert "value" in df.columns
        assert "label" in df.columns
        assert "count" in df.columns
        assert "pct" in df.columns
        assert "total_pnl" in df.columns
        assert "total_pnl_pct" in df.columns
        assert "win_rate" in df.columns


class TestBuildScope:
    """Tests for build_scope API."""

    def test_build_scope_with_selection(self, sample_trades_df: pl.DataFrame) -> None:
        """Test filtering by OptionSummary selection."""
        analyzer = TradesAnalyzer(sample_trades_df)

        # Get available strategies
        strategies = analyzer.available_strategies()
        assert len(strategies.options) > 0

        # Build scope with first strategy selected
        scope = analyzer.build_scope(strategies.options[0])

        assert isinstance(scope, ScopeResult)
        assert scope.n_trades < len(sample_trades_df)
        # All trades should match the selected strategy
        selected_strategy = strategies.options[0].value
        assert all(s == selected_strategy for s in scope.trades["strategy_name"].to_list())


class TestScopeResult:
    """Tests for ScopeResult."""

    def test_scope_result_properties(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult properties."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        assert isinstance(scope.trades, pl.DataFrame)
        assert scope.n_trades == len(sample_trades_df)
        assert isinstance(scope.config, ScopeConfig)

    def test_scope_result_head(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult head preview."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        preview = scope.head(5)

        assert len(preview) == 5
        assert isinstance(preview, pl.DataFrame)

    def test_scope_result_performance(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult performance method."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        perf = scope.performance()

        assert isinstance(perf, pl.DataFrame)
        assert len(perf) == 1  # Single-row DataFrame
        assert perf["trades"][0] == scope.n_trades

    def test_scope_result_plot_scatter(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_scatter method."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        fig = scope.plot_scatter(x="mae", y="mfe")

        # Should return a plotly Figure
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_equity(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_equity method."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        # Basic equity curve
        fig = scope.plot_equity()
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_equity_pct(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_equity with pnl_pct."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        fig = scope.plot_equity(metric_col="pnl_pct")
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_equity_with_ohlcv(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_equity with OHLCV benchmark."""
        from datetime import datetime, timedelta

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        # Create mock OHLCV data
        n_bars = 500
        base_time = datetime(2024, 1, 1)
        ohlcv = pl.DataFrame(
            {
                "ts": [base_time + timedelta(hours=i) for i in range(n_bars)],
                "open": [100 + i * 0.1 for i in range(n_bars)],
            }
        )

        fig = scope.plot_equity(ohlcv=ohlcv)
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_equity_pct_with_ohlcv(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_equity with pnl_pct and OHLCV benchmark."""
        from datetime import datetime, timedelta

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        # Create mock OHLCV data
        n_bars = 500
        base_time = datetime(2024, 1, 1)
        ohlcv = pl.DataFrame(
            {
                "ts": [base_time + timedelta(hours=i) for i in range(n_bars)],
                "open": [100 + i * 0.1 for i in range(n_bars)],
            }
        )

        fig = scope.plot_equity(metric_col="pnl_pct", ohlcv=ohlcv)
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_box(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_box method."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        # Test with exit_reason
        fig = scope.plot_box(group_by="exit_reason", metric="pnl")
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_box_by_blueprint(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_box grouped by blueprint."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        fig = scope.plot_box(group_by="blueprint_name", metric="mae")
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_distribution(self, sample_trades_df: pl.DataFrame) -> None:
        """Test ScopeResult plot_distribution method."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        fig = scope.plot_distribution("pnl", by_outcome=True)
        assert fig is not None
        assert hasattr(fig, "show")

    def test_scope_result_plot_distribution_no_outcome(
        self, sample_trades_df: pl.DataFrame
    ) -> None:
        """Test ScopeResult plot_distribution without outcome split."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        fig = scope.plot_distribution("mae", by_outcome=False, bins=30)
        assert fig is not None
        assert hasattr(fig, "show")


class TestTradesAnalyzerScopeMethods:
    """Tests for TradesAnalyzer scope exploration methods."""

    def test_available_strategies_method(self, sample_trades_df: pl.DataFrame) -> None:
        """Test available_strategies method on analyzer."""
        analyzer = TradesAnalyzer(sample_trades_df)

        options = analyzer.available_strategies()

        assert isinstance(options, AvailableOptions)

    def test_build_scope_method(self, sample_trades_df: pl.DataFrame) -> None:
        """Test build_scope method on analyzer."""
        analyzer = TradesAnalyzer(sample_trades_df)

        scope = analyzer.build_scope()

        assert isinstance(scope, ScopeResult)


class TestScopeWorkflowIntegration:
    """Tests for scope + workflow integration."""

    def test_scope_to_workflow(self, sample_trades_df: pl.DataFrame) -> None:
        """Test full scope to workflow pipeline."""
        analyzer = TradesAnalyzer(sample_trades_df)

        # Build scope with selection
        strategies = analyzer.available_strategies()
        scope = analyzer.build_scope(strategies.options[0])

        # Should be able to get performance directly from scope
        perf = scope.performance()
        assert perf["trades"][0] == scope.n_trades


class TestScopeIntentMethods:
    """Tests for ScopeResult intent methods."""

    def test_volatility_stability(self, sample_trades_df: pl.DataFrame) -> None:
        """Test volatility_stability intent method."""
        from tradepose_client.analysis.trades import IntentReport

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.volatility_stability()

        assert isinstance(report, IntentReport)
        assert report.intent_name == "A. Volatility Stability"
        assert "mae_mean" in report.stats or "error" in report.stats

    def test_entry_timing(self, sample_trades_df: pl.DataFrame) -> None:
        """Test entry_timing intent method."""
        from tradepose_client.analysis.trades import IntentReport

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.entry_timing()

        assert isinstance(report, IntentReport)
        assert report.intent_name == "B. Entry Timing"

    def test_exit_placement(self, sample_trades_df: pl.DataFrame) -> None:
        """Test exit_placement intent method."""
        from tradepose_client.analysis.trades import IntentReport

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.exit_placement()

        assert isinstance(report, IntentReport)
        assert report.intent_name == "C. Exit Placement"

    def test_trailing_breakeven(self, sample_trades_df: pl.DataFrame) -> None:
        """Test trailing_breakeven intent method."""
        from tradepose_client.analysis.trades import IntentReport

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.trailing_breakeven()

        assert isinstance(report, IntentReport)
        assert report.intent_name == "D. Trailing & Breakeven"

    def test_position_sizing(self, sample_trades_df: pl.DataFrame) -> None:
        """Test position_sizing intent method."""
        from tradepose_client.analysis.trades import IntentReport

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.position_sizing()

        assert isinstance(report, IntentReport)
        assert report.intent_name == "E. Position Sizing"

    def test_full_workflow(self, sample_trades_df: pl.DataFrame) -> None:
        """Test full_workflow intent method."""
        from tradepose_client.analysis.trades import FullWorkflowReport

        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.full_workflow()

        assert isinstance(report, FullWorkflowReport)
        assert report.n_trades == scope.n_trades
        assert report.step_a.intent_name == "A. Volatility Stability"
        assert report.step_b.intent_name == "B. Entry Timing"
        assert report.step_c.intent_name == "C. Exit Placement"
        assert report.step_d.intent_name == "D. Trailing & Breakeven"
        assert report.step_e.intent_name == "E. Position Sizing"

    def test_intent_report_summary(self, sample_trades_df: pl.DataFrame) -> None:
        """Test IntentReport summary method."""
        analyzer = TradesAnalyzer(sample_trades_df)
        scope = analyzer.build_scope()

        report = scope.volatility_stability()
        summary = report.summary()

        assert "A. Volatility Stability" in summary
        assert "=" * 60 in summary
