"""
Parity test for execution_decision_v1.py against GAS source.

F05: Exit/sell action decision logic.
Tests calc_exit_sell_action() with core priorities and edge cases.

Source: src/gas_adapter_parts/gdf_01_price_metrics.gs:calcExitSellAction_
"""

import pytest
from formulas.execution_decision_v1 import calc_exit_sell_action


class TestExitSellActionPriorities:
    """Test exit/sell action priority hierarchy."""

    def test_hold_default(self):
        """Default HOLD when no signals trigger."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 5,
            "rwPartial": 0,
        })
        assert result["action"] == "HOLD"

    def test_priority_1_stop_action(self):
        """Priority 1: STOP_OR_TIME_EXIT_READY → EXIT_100."""
        result = calc_exit_sell_action({
            "close": 100,
            "stopPrice": 95,
            "timingAction": "STOP_OR_TIME_EXIT_READY",
        })
        assert result["action"] == "EXIT_100"
        assert result["ratio_pct"] == 100

    def test_priority_1_strong_rw(self):
        """Priority 1: rwPartial >= 4 → EXIT_100."""
        result = calc_exit_sell_action({
            "close": 100,
            "stopPrice": 95,
            "rwPartial": 4,
        })
        assert result["action"] == "EXIT_100"

    def test_priority_5_profit_50(self):
        """Priority 5: profitPct >= 50 → PROFIT_TRIM_50."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 55,
            "tp2Price": 155,
            "atr20": 2,
        })
        assert result["action"] == "PROFIT_TRIM_50"
        assert result["ratio_pct"] == 50

    def test_priority_5_take_profit_tier1(self):
        """Priority 5: profitPct >= 10 → TAKE_PROFIT_TIER1."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 15,
            "tp1Price": 115,
            "atr20": 2,
        })
        assert result["action"] == "TAKE_PROFIT_TIER1"
        assert result["ratio_pct"] == 25

    def test_priority_4_trailing_stop_breach(self):
        """Priority 4: close <= trailingStop → TRAILING_STOP_BREACH."""
        result = calc_exit_sell_action({
            "close": 95,
            "trailingStop": 98,
            "atr20": 2,
        })
        assert result["action"] == "TRAILING_STOP_BREACH"
        assert result["ratio_pct"] == 70

    def test_priority_4_rw_medium(self):
        """Priority 4: rwPartial >= 2 → TRIM_50."""
        result = calc_exit_sell_action({
            "close": 100,
            "atr20": 2,
            "rwPartial": 2,
        })
        assert result["action"] == "TRIM_50"
        assert result["ratio_pct"] == 50

    def test_priority_6_time_stop_near(self):
        """Priority 6: daysToTimeStop <= 7 → TIME_TRIM_50."""
        result = calc_exit_sell_action({
            "close": 100,
            "atr20": 2,
            "daysToTimeStop": 5,
            "profitPct": 0,
            "rwPartial": 0,
        })
        assert result["action"] == "TIME_TRIM_50"
        assert result["ratio_pct"] == 50

    def test_price_source_tier2(self):
        """When tp2Price available, use it for PROFIT_TRIM_50."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 55,
            "tp2Price": 155,
            "atr20": 2,
        })
        assert result["price_source"] == "TP2_PRICE"
        assert result["price_basis"] == "TAKE_PROFIT_TIER2_PRICE"

    def test_price_fallback_to_close_protect(self):
        """When tp2Price absent, use closeProtectLimit."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 55,
            "atr20": 2,
        })
        assert result["price_source"] == "CLOSE_PROFIT_PROTECT"
        assert result["price_basis"] == "PRIOR_CLOSE_X_0.998"

    def test_validation_confirmed(self):
        """Validation = SIGNAL_CONFIRMED when price valid."""
        result = calc_exit_sell_action({
            "close": 100,
            "stopPrice": 95,
            "timingAction": "STOP_OR_TIME_EXIT_READY",
        })
        assert result["validation"] == "SIGNAL_CONFIRMED"

    def test_validation_hold_no_action(self):
        """Validation = NO_SELL_ACTION when HOLD."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 5,
        })
        assert result["validation"] == "NO_SELL_ACTION"

    def test_rw_early_warning_trim_33(self):
        """Priority 4b: rwPartial >= 1 + timingExitScore >= 30 → TRIM_33."""
        result = calc_exit_sell_action({
            "close": 100,
            "atr20": 2,
            "rwPartial": 1,
            "timingExitScore": 35,
        })
        assert result["action"] == "TRIM_33"
        assert result["ratio_pct"] == 33

    def test_rw_signal_only_trim_25(self):
        """Priority 4c: rwPartial >= 1 only → TRIM_25."""
        result = calc_exit_sell_action({
            "close": 100,
            "atr20": 2,
            "rwPartial": 1,
            "timingExitScore": 0,
        })
        assert result["action"] == "TRIM_25"
        assert result["ratio_pct"] == 25

    def test_profit_trim_35(self):
        """Priority 5: profitPct >= 30 → PROFIT_TRIM_35."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 35,
            "tp2Price": 135,
            "atr20": 2,
        })
        assert result["action"] == "PROFIT_TRIM_35"
        assert result["ratio_pct"] == 35

    def test_profit_trim_25(self):
        """Priority 5: profitPct >= 20 → PROFIT_TRIM_25."""
        result = calc_exit_sell_action({
            "close": 100,
            "profitPct": 25,
            "tp1Price": 125,
            "atr20": 2,
        })
        assert result["action"] == "PROFIT_TRIM_25"
        assert result["ratio_pct"] == 25

    def test_time_stop_approaching(self):
        """Priority 6b: daysToTimeStop <= 14 → TIME_TRIM_25."""
        result = calc_exit_sell_action({
            "close": 100,
            "atr20": 2,
            "daysToTimeStop": 10,
            "profitPct": 0,
            "rwPartial": 0,
        })
        assert result["action"] == "TIME_TRIM_25"
        assert result["ratio_pct"] == 25
