"""Tests for trigger system (IFTTT-style).

Tests cover:
- Rust types: TriggerCondition, TriggerActionType, TriggerRule, TriggerResult
- Rust functions: evaluate_trigger, create_trigger
- Trigger conditions: above, below, cross_above, cross_below, percent_change,
  absolute_change, between, outside
- Cooldown and hysteresis logic
- Python pipeline functions: trigger, conditional_hedge, oracle
"""

import pytest


# ---------------------------------------------------------------------------
# 1. Trigger conditions
# ---------------------------------------------------------------------------


class TestTriggerConditions:
    def test_above(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Above, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.6, 0.0, 0.0, 0.0, True)
        assert r.fired
        assert r.rule_name == "test"

    def test_above_not_fired(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Above, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.4, 0.0, 0.0, 0.0, True)
        assert not r.fired

    def test_below(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Below, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.3, 0.0, 0.0, 0.0, True)
        assert r.fired

    def test_below_not_fired(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Below, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.7, 0.0, 0.0, 0.0, True)
        assert not r.fired

    def test_cross_above(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.CrossAbove, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.6, 0.4, 0.0, 0.0, True)
        assert r.fired

    def test_cross_above_already_above(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.CrossAbove, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.6, 0.55, 0.0, 0.0, True)
        assert not r.fired

    def test_cross_below(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.CrossBelow, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.4, 0.6, 0.0, 0.0, True)
        assert r.fired

    def test_cross_below_already_below(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.CrossBelow, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.3, 0.4, 0.0, 0.0, True)
        assert not r.fired

    def test_percent_change(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.PercentChange, 0.05, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 1.1, 1.0, 0.0, 0.0, True)
        assert r.fired  # 10% > 5%

    def test_percent_change_below_threshold(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.PercentChange, 0.05, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 1.01, 1.0, 0.0, 0.0, True)
        assert not r.fired  # 1% < 5%

    def test_absolute_change(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.AbsoluteChange, 0.01, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.52, 0.50, 0.0, 0.0, True)
        assert r.fired  # 0.02 > 0.01

    def test_absolute_change_not_fired(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.AbsoluteChange, 0.05, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.51, 0.50, 0.0, 0.0, True)
        assert not r.fired  # 0.01 < 0.05

    def test_between(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Between, 0.3, TriggerActionType.Alert, 0.7)
        r = evaluate_trigger(rule, 0.5, 0.0, 0.0, 0.0, True)
        assert r.fired

    def test_between_outside(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Between, 0.3, TriggerActionType.Alert, 0.7)
        r = evaluate_trigger(rule, 0.1, 0.0, 0.0, 0.0, True)
        assert not r.fired

    def test_outside(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Outside, 0.3, TriggerActionType.Alert, 0.7)
        r = evaluate_trigger(rule, 0.1, 0.0, 0.0, 0.0, True)
        assert r.fired

    def test_outside_not_fired(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Outside, 0.3, TriggerActionType.Alert, 0.7)
        r = evaluate_trigger(rule, 0.5, 0.0, 0.0, 0.0, True)
        assert not r.fired


# ---------------------------------------------------------------------------
# 2. Cooldown
# ---------------------------------------------------------------------------


class TestCooldown:
    def test_active(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger(
            "test", TriggerCondition.Above, 0.5, TriggerActionType.Alert,
            0.0, 60.0, 0.0,
        )
        r = evaluate_trigger(rule, 0.6, 0.0, 100.0, 130.0, True)
        assert not r.fired
        assert "cooldown" in r.reason

    def test_expired(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger(
            "test", TriggerCondition.Above, 0.5, TriggerActionType.Alert,
            0.0, 60.0, 0.0,
        )
        r = evaluate_trigger(rule, 0.6, 0.0, 100.0, 200.0, True)
        assert r.fired


# ---------------------------------------------------------------------------
# 3. Hysteresis
# ---------------------------------------------------------------------------


class TestHysteresis:
    def test_above_with_hysteresis(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger(
            "test", TriggerCondition.Above, 0.5, TriggerActionType.Alert,
            0.0, 0.0, 0.05,
        )
        # Value above threshold: hysteresis lowers effective threshold
        r = evaluate_trigger(rule, 0.52, 0.0, 0.0, 0.0, True)
        assert r.fired  # 0.52 > 0.45 (0.5 - 0.05)

    def test_below_with_hysteresis(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger(
            "test", TriggerCondition.Below, 0.5, TriggerActionType.Alert,
            0.0, 0.0, 0.05,
        )
        # Value below threshold: hysteresis raises effective threshold
        r = evaluate_trigger(rule, 0.48, 0.0, 0.0, 0.0, True)
        assert r.fired  # 0.48 < 0.55 (0.5 + 0.05)


# ---------------------------------------------------------------------------
# 4. create_trigger
# ---------------------------------------------------------------------------


class TestCreateTrigger:
    def test_basic(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, create_trigger,
        )

        rule = create_trigger(
            "my_trigger",
            TriggerCondition.Above,
            0.5,
            TriggerActionType.Alert,
            0.0,
            60.0,
            0.01,
        )
        assert rule.name == "my_trigger"
        assert rule.condition == TriggerCondition.Above
        assert rule.threshold == pytest.approx(0.5, abs=1e-10)
        assert rule.action_type == TriggerActionType.Alert
        assert rule.cooldown_secs == pytest.approx(60.0, abs=1e-10)
        assert rule.hysteresis == pytest.approx(0.01, abs=1e-10)

    def test_not_armed(self):
        from horizon._horizon import (
            TriggerCondition, TriggerActionType, evaluate_trigger, create_trigger,
        )

        rule = create_trigger("test", TriggerCondition.Above, 0.5, TriggerActionType.Alert)
        r = evaluate_trigger(rule, 0.6, 0.0, 0.0, 0.0, False)
        assert not r.fired
        assert "not armed" in r.reason

    def test_all_action_types(self):
        from horizon._horizon import TriggerCondition, TriggerActionType, create_trigger

        for action in [
            TriggerActionType.Alert,
            TriggerActionType.Webhook,
            TriggerActionType.Order,
            TriggerActionType.Hedge,
            TriggerActionType.KillSwitch,
        ]:
            rule = create_trigger("test", TriggerCondition.Above, 0.5, action)
            assert rule.action_type == action


# ---------------------------------------------------------------------------
# 5. Pipeline functions (Ultra tier)
# ---------------------------------------------------------------------------


class TestPipeline:
    def test_trigger(self):
        try:
            from horizon.triggers_pipeline import trigger, TriggerConfig
        except PermissionError:
            pytest.skip("Ultra tier required")

        tc = TriggerConfig(name="test", feed_name="btc", condition="above", threshold=0.5)
        fn = trigger(triggers=[tc])
        assert callable(fn)
        assert fn.__name__ == "trigger"

    def test_conditional_hedge(self):
        try:
            from horizon.triggers_pipeline import conditional_hedge
        except PermissionError:
            pytest.skip("Ultra tier required")

        fn = conditional_hedge("btc", condition="below", threshold=0.3)
        assert callable(fn)
        assert fn.__name__ == "conditional_hedge"

    def test_oracle(self):
        try:
            from horizon.triggers_pipeline import oracle
        except PermissionError:
            pytest.skip("Ultra tier required")

        fn = oracle(port=8099)
        assert callable(fn)
        assert fn.__name__ == "oracle"
