import numpy as np
import pandas as pd
import pytest

from strategies.mlp_score_structure import confirmed_score_structure
from strategies.strategy_mlp_scores import _score_to_signals


def test_lower_high_is_available_only_after_the_right_hand_confirmation_bar():
    # Highs at indices 1 (5) and 4 (3); with right=1, the second pivot is
    # knowable on index 5, not index 4.
    score = np.array([0.0, 5.0, 0.0, 0.0, 3.0, 0.0])

    structure = confirmed_score_structure(score, left=1, right=1)

    assert structure.lower_high.tolist() == [False, False, False, False, False, True]


def test_higher_low_is_available_only_after_the_right_hand_confirmation_bar():
    # Lows at indices 1 (0) and 4 (2); the latter is a higher low.
    score = np.array([5.0, 0.0, 5.0, 5.0, 2.0, 5.0])

    structure = confirmed_score_structure(score, left=1, right=1)

    assert structure.higher_low.tolist() == [False, False, False, False, False, True]


def test_structure_state_turns_bullish_on_higher_low_and_bearish_on_lower_high():
    # Confirm a higher low at index 5, then a lower high at index 8.
    score = np.array([5.0, 0.0, 5.0, 5.0, 2.0, 5.0, 4.0, 3.0, 4.0, 2.0])

    structure = confirmed_score_structure(score, left=1, right=1)

    assert structure.bullish_state.tolist() == [False, False, False, False, False, True, True, True, True, False]


def test_score_structure_rejects_invalid_windows_and_ignores_plateaus_and_nan():
    with pytest.raises(ValueError, match="at least 1"):
        confirmed_score_structure(np.array([1.0, 2.0]), left=0, right=1)

    structure = confirmed_score_structure(
        np.array([2.0, 2.0, 2.0, np.nan, 1.0, 2.0]), left=1, right=1,
    )

    assert not structure.higher_low.any()
    assert not structure.lower_high.any()
    assert not structure.bullish_state.any()


def test_asymmetric_pivot_is_emitted_on_the_requested_right_hand_bar():
    score = np.array([0.0, 5.0, 0.0, 0.0, 3.0, 0.0, 0.0])

    structure = confirmed_score_structure(score, left=1, right=2)

    assert structure.lower_high.tolist() == [False, False, False, False, False, False, True]


def test_structure_overlay_gates_entries_and_closes_existing_positions():
    df = pd.DataFrame({
        "activation_score": [200.0, 50.0, 50.0],
        "close": [100.0, 101.0, 102.0],
        "high": [100.0, 101.0, 102.0],
    })
    params = {
        "i_long_entry_activation_threshold": 100.0,
        "i_long_exit_activation_threshold": 900.0,
        "i_long_exit_activation_confirmation_threshold": -900.0,
    }

    signals = _score_to_signals(
        df.copy(), params, np.zeros(3), df["close"].to_numpy(), df["high"].to_numpy(),
        entry_gate=np.array([True, True, True]),
        additional_exit=np.array([False, False, True]),
    )
    blocked = _score_to_signals(
        df.copy(), params, np.zeros(3), df["close"].to_numpy(), df["high"].to_numpy(),
        entry_gate=np.array([True, False, True]),
    )

    assert signals["execute_entry"].tolist() == [False, True, False]
    assert signals["execute_exit"].tolist() == [False, False, True]
    assert blocked["execute_entry"].tolist() == [False, False, False]


def test_overlay_validates_array_lengths_and_respects_the_pine_time_gate():
    df = pd.DataFrame({
        "time": pd.to_datetime(["2020-01-01 00:00", "2020-01-01 06:00", "2020-01-01 12:00"]),
        "activation_score": [200.0, 50.0, 50.0],
        "close": [100.0, 101.0, 102.0],
        "high": [100.0, 101.0, 102.0],
    })
    params = {
        "i_long_entry_activation_threshold": 100.0,
        "i_long_exit_activation_threshold": 900.0,
        "i_long_exit_activation_confirmation_threshold": -900.0,
        "_pine_time_end": "2020-01-01 06:00",
    }

    with pytest.raises(ValueError, match="entry_gate length"):
        _score_to_signals(df.copy(), params, np.zeros(3), df["close"].to_numpy(), df["high"].to_numpy(),
                          entry_gate=np.array([True, True]))
    with pytest.raises(ValueError, match="additional_exit length"):
        _score_to_signals(df.copy(), params, np.zeros(3), df["close"].to_numpy(), df["high"].to_numpy(),
                          additional_exit=np.array([False, True]))

    signals = _score_to_signals(
        df.copy(), params, np.zeros(3), df["close"].to_numpy(), df["high"].to_numpy(),
        additional_exit=np.array([False, False, True]),
    )
    assert signals["execute_entry"].tolist() == [False, True, False]
    assert signals["execute_exit"].tolist() == [False, False, False]
