"""Tests for _types.py — enums, dataclasses, from_config methods."""

from __future__ import annotations

import pytest

from pyfortis._types import (
    BreachAction,
    BreachRecord,
    CircuitBreakerSpec,
    CircuitBreakerType,
    EscalationPolicy,
    Exposure,
    GateResult,
    GateVerdict,
    LimitCheckResult,
    LimitScope,
    LimitSpec,
    LimitType,
    MetricResult,
    MetricSpec,
    MetricType,
    Order,
    OrderType,
    Position,
    Severity,
    Side,
    VaRMethod,
)


class TestOrder:
    def test_signed_quantity_buy(self):
        order = Order(order_id="1", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0)
        assert order.signed_quantity == 100

    def test_signed_quantity_sell(self):
        order = Order(order_id="1", instrument="AAPL", side=Side.SELL, quantity=100, price=150.0)
        assert order.signed_quantity == -100

    def test_notional(self):
        order = Order(order_id="1", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0)
        assert order.notional == 15000.0

    def test_frozen(self):
        order = Order(order_id="1", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0)
        with pytest.raises(AttributeError):
            order.quantity = 200  # type: ignore[misc]


class TestPosition:
    def test_is_long(self):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        assert pos.is_long is True
        assert pos.is_short is False

    def test_is_short(self):
        pos = Position(instrument="AAPL", quantity=-100, avg_price=150.0, current_price=155.0)
        assert pos.is_long is False
        assert pos.is_short is True

    def test_notional(self):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        assert pos.notional == 15500.0

    def test_unrealized_pnl(self):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        assert pos.unrealized_pnl == 500.0

    def test_unrealized_pnl_short(self):
        pos = Position(instrument="AAPL", quantity=-100, avg_price=150.0, current_price=155.0)
        assert pos.unrealized_pnl == -500.0


class TestExposure:
    def test_from_long_position(self):
        pos = Position(instrument="AAPL", quantity=100, avg_price=150.0, current_price=155.0)
        exp = Exposure.from_position(pos)
        assert exp.long_notional == 15500.0
        assert exp.short_notional == 0.0
        assert exp.net_notional == 15500.0
        assert exp.gross_notional == 15500.0

    def test_from_short_position(self):
        pos = Position(instrument="AAPL", quantity=-100, avg_price=150.0, current_price=155.0)
        exp = Exposure.from_position(pos)
        assert exp.long_notional == 0.0
        assert exp.short_notional == 15500.0
        assert exp.net_notional == -15500.0


class TestSeverity:
    def test_rank_ordering(self):
        assert Severity.INFO.rank < Severity.WARNING.rank
        assert Severity.WARNING.rank < Severity.CRITICAL.rank
        assert Severity.CRITICAL.rank < Severity.KILL.rank


class TestLimitSpec:
    def test_from_config(self):
        cfg = {
            "name": "test",
            "type": "position",
            "scope": "instrument",
            "params": {"max_long": 1000},
            "severity": "CRITICAL",
        }
        spec = LimitSpec.from_config(cfg)
        assert spec.name == "test"
        assert spec.type is LimitType.POSITION
        assert spec.scope is LimitScope.INSTRUMENT
        assert spec.params == {"max_long": 1000}
        assert spec.severity is Severity.CRITICAL
        assert spec.enabled is True

    def test_from_config_defaults(self):
        cfg = {"name": "test", "type": "position"}
        spec = LimitSpec.from_config(cfg)
        assert spec.scope is LimitScope.ORDER
        assert spec.severity is Severity.WARNING
        assert spec.enabled is True


class TestMetricSpec:
    def test_from_config(self):
        cfg = {"name": "var", "type": "var", "params": {"confidence": 0.99}}
        spec = MetricSpec.from_config(cfg)
        assert spec.name == "var"
        assert spec.type is MetricType.VAR
        assert spec.params["confidence"] == 0.99
        assert spec.severity is None

    def test_from_config_with_severity(self):
        cfg = {"name": "dd", "type": "drawdown", "severity": "WARNING"}
        spec = MetricSpec.from_config(cfg)
        assert spec.severity is Severity.WARNING


class TestCircuitBreakerSpec:
    def test_from_config(self):
        cfg = {
            "name": "daily_loss",
            "type": "daily_pnl",
            "params": {"max_loss": -50000},
            "action": "halt_trading",
            "cooldown": "2h",
        }
        spec = CircuitBreakerSpec.from_config(cfg)
        assert spec.name == "daily_loss"
        assert spec.type is CircuitBreakerType.DAILY_PNL
        assert spec.action is BreachAction.HALT_TRADING
        assert spec.cooldown == "2h"


class TestEscalationPolicy:
    def test_from_config(self):
        cfg = {
            "default_action": "log",
            "escalation": {
                "WARNING": ["log", "notify"],
                "CRITICAL": ["log", "notify", "block_order"],
            },
        }
        policy = EscalationPolicy.from_config(cfg)
        assert policy.default_action is BreachAction.LOG
        assert len(policy.escalation[Severity.WARNING]) == 2
        assert BreachAction.BLOCK_ORDER in policy.escalation[Severity.CRITICAL]


class TestGateResult:
    def test_approved(self):
        r = GateResult.approved("o-1")
        assert r.is_approved is True
        assert r.is_rejected is False
        assert r.verdict is GateVerdict.APPROVED

    def test_rejected(self):
        r = GateResult.rejected("o-1")
        assert r.is_approved is False
        assert r.is_rejected is True

    def test_approved_with_warning(self):
        r = GateResult.approved_with_warning("o-1")
        assert r.is_approved is True
        assert r.is_rejected is False
        assert r.verdict is GateVerdict.APPROVED_WITH_WARNING
