"""Tests for RiskMonitor (stateful layer)."""

from __future__ import annotations

import pytest

from pyfortis._types import (
    GateVerdict,
    Order,
    Position,
    Side,
)
from pyfortis.gate import RiskEngine


@pytest.fixture
def monitor(sample_config):
    engine = RiskEngine.from_dict(sample_config)
    return engine.create_monitor()


class TestMonitorPositionManagement:
    def test_update_position(self, monitor):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        monitor.update_position(pos)
        positions = monitor.get_positions()
        assert "AAPL" in positions
        assert positions["AAPL"].quantity == 100

    def test_update_positions_bulk(self, monitor):
        positions = [
            Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0),
            Position(instrument="GOOGL", quantity=50, avg_price=2800.0, current_price=2850.0),
        ]
        monitor.update_positions(positions)
        assert len(monitor.get_positions()) == 2

    def test_remove_position(self, monitor):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        monitor.update_position(pos)
        monitor.remove_position("AAPL")
        assert "AAPL" not in monitor.get_positions()

    def test_exposures_auto_computed(self, monitor):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        monitor.update_position(pos)
        exposures = monitor.get_exposures()
        assert "AAPL" in exposures
        assert exposures["AAPL"].long_notional == 15500.0


class TestMonitorValidateOrder:
    def test_approve(self, monitor):
        order = Order(
            order_id="o-1", instrument="AAPL", side=Side.BUY,
            quantity=100, price=150.0,
        )
        result = monitor.validate_order(order)
        assert result.is_approved

    def test_reject_when_halted(self, monitor):
        # Trip circuit breaker
        monitor.update_pnl(daily_pnl=-60000, current_value=900000)
        monitor.check_circuit_breakers()
        assert monitor.is_halted

        order = Order(
            order_id="o-2", instrument="AAPL", side=Side.BUY,
            quantity=10, price=150.0,
        )
        result = monitor.validate_order(order)
        assert result.is_rejected
        assert "halted" in result.message.lower()

    def test_validate_with_existing_positions(self, monitor):
        monitor.update_position(
            Position(instrument="AAPL", quantity=9000, avg_price=150.0, current_price=150.0)
        )
        order = Order(
            order_id="o-3", instrument="AAPL", side=Side.BUY,
            quantity=2000, price=150.0,
        )
        result = monitor.validate_order(order)
        assert result.is_rejected


class TestMonitorPnl:
    def test_update_pnl(self, monitor):
        monitor.update_pnl(daily_pnl=-10000, current_value=990000)
        assert monitor.daily_pnl == -10000
        assert monitor.current_value == 990000

    def test_peak_tracking(self, monitor):
        monitor.update_pnl(daily_pnl=0, current_value=1000000)
        monitor.update_pnl(daily_pnl=-10000, current_value=990000)
        assert monitor.peak_value == 1000000

    def test_reset_daily(self, monitor):
        monitor.update_pnl(daily_pnl=-60000, current_value=900000)
        monitor.check_circuit_breakers()
        assert monitor.is_halted

        monitor.reset_daily()
        assert not monitor.is_halted
        assert monitor.daily_pnl == 0.0


class TestMonitorCircuitBreakers:
    def test_no_trip(self, monitor):
        monitor.update_pnl(daily_pnl=-10000, current_value=990000)
        tripped = monitor.check_circuit_breakers()
        assert len(tripped) == 0
        assert not monitor.is_halted

    def test_trip(self, monitor):
        monitor.update_pnl(daily_pnl=-60000, current_value=900000)
        tripped = monitor.check_circuit_breakers()
        assert len(tripped) == 1
        assert monitor.is_halted
        assert "circuit breaker" in monitor.halt_reason.lower()


class TestMonitorMetrics:
    def test_compute_metrics(self, monitor, sample_returns):
        results = monitor.compute_metrics(returns_data=sample_returns)
        assert len(results) >= 1
