"""Tests for rewards tracker (Python types + pipeline)."""

import pytest

from horizon.rewards import EpochRewards, RewardsTracker, rewards_tracker
from horizon._horizon import RewardConfig, RewardMarket


# ===========================================================================
# Rust types
# ===========================================================================


class TestRewardConfig:
    def test_type_exists(self):
        """RewardConfig is importable from Rust module."""
        assert RewardConfig is not None

    def test_repr(self):
        # RewardConfig is created by Rust, not Python - just check it exists
        assert hasattr(RewardConfig, "__repr__")


class TestRewardMarket:
    def test_type_exists(self):
        """RewardMarket is importable from Rust module."""
        assert RewardMarket is not None

    def test_repr(self):
        assert hasattr(RewardMarket, "__repr__")


# ===========================================================================
# Python: EpochRewards
# ===========================================================================


class TestEpochRewards:
    def test_defaults(self):
        er = EpochRewards()
        assert er.market_id == ""
        assert er.fills_scored == 0
        assert er.estimated_usdc == 0.0
        assert er.total_notional == 0.0

    def test_with_values(self):
        er = EpochRewards(market_id="abc", fills_scored=5, estimated_usdc=1.0, total_notional=5000.0)
        assert er.market_id == "abc"
        assert er.fills_scored == 5

    def test_as_dict(self):
        er = EpochRewards(market_id="test", fills_scored=3, estimated_usdc=0.5, total_notional=2500.0)
        d = er.as_dict()
        assert d["market_id"] == "test"
        assert d["fills_scored"] == 3
        assert d["estimated_usdc"] == 0.5
        assert d["total_notional"] == 2500.0


# ===========================================================================
# Python: RewardsTracker
# ===========================================================================


class TestRewardsTracker:
    def test_basic_fill(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        rebate = tracker.record_fill(
            market_id="market1",
            fill_id="fill1",
            notional=10000.0,
            is_maker=True,
            scoring=True,
        )
        assert rebate == pytest.approx(2.0)  # 10000 * 0.0002
        assert tracker.total_usdc == pytest.approx(2.0)

    def test_dedup(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        r1 = tracker.record_fill("m1", "f1", 10000.0, True, True)
        r2 = tracker.record_fill("m1", "f1", 10000.0, True, True)
        assert r1 > 0.0
        assert r2 == 0.0  # Duplicate fill_id
        assert tracker.total_usdc == pytest.approx(r1)

    def test_not_maker(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        rebate = tracker.record_fill("m1", "f1", 10000.0, is_maker=False, scoring=True)
        assert rebate == 0.0

    def test_not_scoring(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        rebate = tracker.record_fill("m1", "f1", 10000.0, is_maker=True, scoring=False)
        assert rebate == 0.0

    def test_zero_notional(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        rebate = tracker.record_fill("m1", "f1", 0.0, is_maker=True, scoring=True)
        assert rebate == 0.0

    def test_negative_notional(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        rebate = tracker.record_fill("m1", "f1", -100.0, is_maker=True, scoring=True)
        assert rebate == 0.0

    def test_multiple_markets(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        tracker.record_fill("m1", "f1", 10000.0, True, True)
        tracker.record_fill("m2", "f2", 5000.0, True, True)

        epochs = tracker.epoch_rewards
        assert "m1" in epochs
        assert "m2" in epochs
        assert epochs["m1"].fills_scored == 1
        assert epochs["m2"].fills_scored == 1
        assert tracker.total_usdc == pytest.approx(3.0)  # 2.0 + 1.0

    def test_reset_epoch(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        tracker.record_fill("m1", "f1", 10000.0, True, True)
        old = tracker.reset_epoch()
        assert "m1" in old
        assert old["m1"].fills_scored == 1
        assert len(tracker.epoch_rewards) == 0
        # Total USDC persists across epochs
        assert tracker.total_usdc > 0.0

    def test_custom_rebate_rate(self):
        tracker = RewardsTracker(rebate_rate=0.001)  # 10bps
        rebate = tracker.record_fill("m1", "f1", 10000.0, True, True)
        assert rebate == pytest.approx(10.0)

    def test_many_fills(self):
        tracker = RewardsTracker(rebate_rate=0.0002)
        total = 0.0
        for i in range(100):
            r = tracker.record_fill("m1", f"f{i}", 1000.0, True, True)
            total += r
        assert tracker.total_usdc == pytest.approx(total)
        assert tracker.epoch_rewards["m1"].fills_scored == 100


# ===========================================================================
# Python: rewards_tracker pipeline
# ===========================================================================


class TestRewardsTrackerPipeline:
    def test_returns_callable(self):
        fn = rewards_tracker()
        assert callable(fn)
        assert fn.__name__ == "rewards_tracker"

    def test_custom_params(self):
        fn = rewards_tracker(rebate_rate=0.001, check_scoring=True)
        assert callable(fn)


# ===========================================================================
# Import tests
# ===========================================================================


class TestImports:
    def test_import_from_horizon(self):
        """All rewards symbols are importable from top-level."""
        from horizon import (
            RewardConfig,
            RewardMarket,
            fetch_reward_markets,
            EpochRewards,
            RewardsTracker,
            rewards_tracker,
        )
        assert RewardConfig is not None
        assert RewardMarket is not None
        assert callable(fetch_reward_markets)
        assert EpochRewards is not None
        assert RewardsTracker is not None
        assert callable(rewards_tracker)
