from __future__ import annotations

import sys
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from src.quant_engine.exit_decisions import compute_timing_decision


class TestScoreParityV1(unittest.TestCase):
    def test_pullback_wait_is_selected_for_borderline_entry_scores(self):
        res = compute_timing_decision(
            {
                "priceStatus": "PRICE_OK",
                "atr20": 100.0,
                "entryModeGate": "PASS",
                "entryMode": "PULLBACK",
                "leaderTotal": 3,
                "leaderGate": "PASS",
                "flowCredit": 0.4,
                "acGate": "CAUTION",
                "ma20Slope": 1.0,
                "disparity": 4.5,
                "rsi14": 68.0,
                "avgTradeValue5D": 100.0,
                "spreadPct": 0.5,
            }
        )
        self.assertEqual(res["action"], "BUY_PULLBACK_WAIT")
        self.assertGreaterEqual(res["entry_score"], 60)
        self.assertLess(res["entry_score"], 100)

    def test_entry_score_boosts_with_leader_flow_and_clear_gate(self):
        res = compute_timing_decision(
            {
                "priceStatus": "PRICE_OK",
                "atr20": 100.0,
                "entryModeGate": "PASS",
                "entryMode": "BREAKOUT",
                "leaderTotal": 4,
                "leaderGate": "PASS",
                "flowCredit": 0.8,
                "acGate": "CLEAR",
                "ma20Slope": 1.0,
                "disparity": 2.0,
                "rsi14": 55.0,
                "avgTradeValue5D": 100.0,
                "spreadPct": 0.5,
            }
        )
        self.assertEqual(res["action"], "BUY_BREAKOUT_PILOT_ONLY")
        self.assertGreaterEqual(res["entry_score"], 75)
        self.assertLessEqual(res["exit_score"], 20)

    def test_exit_review_is_selected_before_forced_exit_threshold(self):
        res = compute_timing_decision(
            {
                "priceStatus": "PRICE_OK",
                "atr20": 100.0,
                "entryModeGate": "PASS",
                "entryMode": "PULLBACK",
                "leaderTotal": 3,
                "leaderGate": "PASS",
                "flowCredit": 0.6,
                "acGate": "CAUTION",
                "ma20Slope": -1.0,
                "disparity": 8.5,
                "rsi14": 70.0,
                "avgTradeValue5D": 100.0,
                "spreadPct": 0.5,
                "rwPartial": 2,
            }
        )
        self.assertEqual(res["action"], "EXIT_REVIEW")
        self.assertGreaterEqual(res["exit_score"], 50)
        self.assertLess(res["exit_score"], 75)

    def test_data_missing_short_circuits_timing_action(self):
        res = compute_timing_decision(
            {
                "priceStatus": "PRICE_OK",
                "atr20": None,
                "entryModeGate": "PASS",
                "entryMode": "BREAKOUT",
                "leaderTotal": 4,
                "leaderGate": "PASS",
                "flowCredit": 0.8,
                "acGate": "CLEAR",
                "ma20Slope": 1.0,
                "disparity": 2.0,
                "rsi14": 55.0,
                "avgTradeValue5D": 100.0,
                "spreadPct": 0.5,
            }
        )
        self.assertEqual(res["action"], "OBSERVE_DATA_MISSING")

    def test_exit_score_dominates_with_risk_off_and_time_stop(self):
        res = compute_timing_decision(
            {
                "priceStatus": "PRICE_OK",
                "atr20": 100.0,
                "entryModeGate": "PASS",
                "entryMode": "PULLBACK",
                "leaderTotal": 3,
                "leaderGate": "PASS",
                "flowCredit": 0.6,
                "acGate": "BLOCK",
                "ma20Slope": -1.0,
                "disparity": 13.0,
                "rsi14": 80.0,
                "avgTradeValue5D": 100.0,
                "spreadPct": 0.5,
                "rwPartial": 4,
                "daysToTimeStop": 3,
                "profitPct": 12.0,
            }
        )
        self.assertEqual(res["action"], "STOP_OR_TIME_EXIT_READY")
        self.assertGreaterEqual(res["exit_score"], 75)
        self.assertGreaterEqual(res["entry_score"], 0)


if __name__ == "__main__":
    unittest.main()
