import importlib.util
import unittest
from pathlib import Path


MODULE_PATH = (
    Path(__file__).resolve().parents[1] / "entry_pricing_compare.py"
)
SPEC = importlib.util.spec_from_file_location("entry_pricing_compare", MODULE_PATH)
entry_pricing_compare = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
SPEC.loader.exec_module(entry_pricing_compare)


def attempt(
    *,
    pair="BTC/USDT:USDT",
    side="short",
    status="filled",
    submitted_at="2026-07-20 10:00:00.000000",
    fill_price=100.0,
    latency=1.0,
    is_open=False,
    pnl=1.0,
):
    return {
        "trade_id": 1,
        "pair": pair,
        "side": side,
        "status": status,
        "submitted_at": submitted_at,
        "submitted_at_provenance": "test",
        "fill_latency_s": latency if status == "filled" else None,
        "fill_price": fill_price if status == "filled" else None,
        "intended_price": fill_price,
        "is_open": is_open,
        "close_profit_abs": pnl,
    }


class EntryPricingCompareTest(unittest.TestCase):
    def test_matches_same_pair_side_within_tolerance(self):
        same = [attempt()]
        other = [attempt(submitted_at="2026-07-20 10:00:05.000000")]
        matched, left, right = entry_pricing_compare.match_attempts(same, other, 10)
        self.assertEqual(len(matched), 1)
        self.assertEqual(left, [])
        self.assertEqual(right, [])

    def test_does_not_match_different_side(self):
        same = [attempt(side="short")]
        other = [attempt(side="long")]
        matched, left, right = entry_pricing_compare.match_attempts(same, other, 10)
        self.assertEqual(matched, [])
        self.assertEqual(len(left), 1)
        self.assertEqual(len(right), 1)

    def test_short_crossing_cost_is_adverse_when_other_fill_is_lower(self):
        cost = entry_pricing_compare.adverse_bps(
            attempt(side="short", fill_price=100),
            attempt(side="short", fill_price=99.9),
        )
        self.assertAlmostEqual(cost, 10)

    def test_long_crossing_cost_is_adverse_when_other_fill_is_higher(self):
        cost = entry_pricing_compare.adverse_bps(
            attempt(side="long", fill_price=100),
            attempt(side="long", fill_price=100.1),
        )
        self.assertAlmostEqual(cost, 10)

    def test_summary_waits_for_minimum_matched_opportunities(self):
        matched = [{"same": attempt(), "other": attempt(), "submitted_delta_s": 0}]
        summary = entry_pricing_compare.summarize(matched, [], [], 20)
        self.assertEqual(summary["decision"], "CONTINUE")
        self.assertFalse(summary["ready_for_decision"])

    def test_summary_counts_cancelled_same_as_recovered(self):
        matched = [
            {
                "same": attempt(status="cancelled"),
                "other": attempt(status="filled"),
                "submitted_delta_s": 0,
            }
        ]
        summary = entry_pricing_compare.summarize(matched, [], [], 1)
        self.assertEqual(summary["recovered_by_other"], 1)
        self.assertEqual(summary["matched_fill_rate_same"], 0)
        self.assertEqual(summary["matched_fill_rate_other"], 1)


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