"""Tests for cpz.execution.risk_guard — pre-trade risk validation."""

from __future__ import annotations

import time

import pytest

from cpz.execution.enums import OrderSide, OrderType, TimeInForce
from cpz.execution.models import OrderSubmitRequest
from cpz.execution.risk_guard import RiskGuard, RiskGuardViolation


def _order(symbol="AAPL", side="buy", qty=100, order_type="market", strategy_id="test"):
    return OrderSubmitRequest(
        symbol=symbol,
        side=OrderSide.BUY if side == "buy" else OrderSide.SELL,
        qty=qty,
        order_type=OrderType(order_type),
        time_in_force=TimeInForce.DAY,
        strategy_id=strategy_id,
    )


class TestRiskGuardBlocking:
    def test_blocked_symbol(self):
        guard = RiskGuard(blocked_symbols=["GME", "AMC"])
        with pytest.raises(RiskGuardViolation, match="blocked"):
            guard.validate(_order(symbol="GME"), equity=100_000)

    def test_allowed_symbol(self):
        guard = RiskGuard(allowed_symbols=["AAPL", "GOOG"])
        guard.validate(_order(symbol="AAPL"), equity=100_000)  # ok
        with pytest.raises(RiskGuardViolation, match="not in allowed"):
            guard.validate(_order(symbol="TSLA"), equity=100_000)

    def test_case_insensitive_blocking(self):
        guard = RiskGuard(blocked_symbols=["gme"])
        with pytest.raises(RiskGuardViolation):
            guard.validate(_order(symbol="GME"), equity=100_000)


class TestRiskGuardLimits:
    def test_max_order_qty(self):
        guard = RiskGuard(max_order_qty=50)
        guard.validate(_order(qty=50), equity=100_000)
        with pytest.raises(RiskGuardViolation, match="qty"):
            guard.validate(_order(qty=51), equity=100_000)

    def test_max_order_value(self):
        guard = RiskGuard(max_order_value=10_000)
        guard.validate(_order(qty=10), equity=100_000, current_price=150)
        with pytest.raises(RiskGuardViolation, match="value"):
            guard.validate(_order(qty=100), equity=100_000, current_price=150)

    def test_max_position_pct(self):
        guard = RiskGuard(max_position_pct=0.10)
        guard.validate(_order(qty=10), equity=100_000, current_price=100)
        with pytest.raises(RiskGuardViolation, match="position"):
            guard.validate(_order(qty=200), equity=100_000, current_price=100)

    def test_max_open_orders(self):
        guard = RiskGuard(max_open_orders=2)
        guard.record_order_opened()
        guard.record_order_opened()
        with pytest.raises(RiskGuardViolation, match="open orders"):
            guard.validate(_order(), equity=100_000)

    def test_max_open_orders_after_close(self):
        guard = RiskGuard(max_open_orders=2)
        guard.record_order_opened()
        guard.record_order_opened()
        guard.record_order_closed()
        guard.validate(_order(), equity=100_000)  # should work now


class TestRiskGuardDailyLoss:
    def test_daily_loss_halt(self):
        guard = RiskGuard(max_daily_loss=-1000)
        guard.record_fill(pnl=-1001)
        with pytest.raises(RiskGuardViolation, match="daily P&L"):
            guard.validate(_order(), equity=100_000)

    def test_halted_stays_halted(self):
        guard = RiskGuard(max_daily_loss=-1000)
        guard.record_fill(pnl=-1001)
        try:
            guard.validate(_order(), equity=100_000)
        except RiskGuardViolation:
            pass
        with pytest.raises(RiskGuardViolation, match="halted"):
            guard.validate(_order(), equity=100_000)

    def test_reset_halt(self):
        guard = RiskGuard(max_daily_loss=-1000)
        guard.record_fill(pnl=-1001)
        try:
            guard.validate(_order(), equity=100_000)
        except RiskGuardViolation:
            pass
        guard.reset_halt()
        guard.reset_daily()
        guard.validate(_order(), equity=100_000)


class TestRiskGuardCooldown:
    def test_cooldown(self):
        guard = RiskGuard(cooldown_seconds=1.0)
        guard.validate(_order(), equity=100_000)
        with pytest.raises(RiskGuardViolation, match="cooldown"):
            guard.validate(_order(), equity=100_000)

    def test_cooldown_per_symbol(self):
        guard = RiskGuard(cooldown_seconds=1.0)
        guard.validate(_order(symbol="AAPL"), equity=100_000)
        guard.validate(_order(symbol="GOOG"), equity=100_000)  # different symbol is ok


class TestRiskGuardRateLimit:
    def test_max_orders_per_minute(self):
        guard = RiskGuard(max_orders_per_minute=3)
        guard.validate(_order(symbol="A"), equity=100_000)
        guard.validate(_order(symbol="B"), equity=100_000)
        guard.validate(_order(symbol="C"), equity=100_000)
        with pytest.raises(RiskGuardViolation, match="orders in last 60s"):
            guard.validate(_order(symbol="D"), equity=100_000)


class TestRiskGuardDisabled:
    def test_disabled_passes_all(self):
        guard = RiskGuard(max_order_qty=1, enabled=False)
        guard.validate(_order(qty=10000), equity=100_000)


class TestRiskGuardViolationTracking:
    def test_violations_recorded(self):
        guard = RiskGuard(blocked_symbols=["GME"])
        try:
            guard.validate(_order(symbol="GME"), equity=100_000)
        except RiskGuardViolation:
            pass
        assert len(guard.violations) == 1
        assert guard.violations[0].rule == "blocked_symbol"
