"""Tests for built-in validators."""

from __future__ import annotations

import pytest

from pyfortis._types import (
    Exposure,
    LimitScope,
    LimitSpec,
    LimitType,
    Order,
    Position,
    Severity,
    Side,
)
from pyfortis._validators import (
    validate_concentration,
    validate_notional,
    validate_order_rate,
    validate_position,
    validate_price_tolerance,
    validate_self_trade,
)


class TestValidatePosition:
    def test_within_limits(self, sample_order, sample_positions, sample_exposures, position_limit):
        result = validate_position(
            position_limit, sample_order, sample_positions, sample_exposures, {}
        )
        assert result.passed is True

    def test_exceeds_max_long(self, sample_positions, sample_exposures, position_limit):
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY,
            quantity=6000, price=150.0,
        )
        result = validate_position(
            position_limit, order, sample_positions, sample_exposures, {}
        )
        assert result.passed is False
        assert "max_long" in result.message

    def test_exceeds_max_short(self, sample_exposures, position_limit):
        order = Order(
            order_id="x", instrument="AAPL", side=Side.SELL,
            quantity=6000, price=150.0,
        )
        result = validate_position(position_limit, order, {}, sample_exposures, {})
        assert result.passed is False
        assert "max_short" in result.message

    def test_no_existing_position(self, sample_exposures, position_limit):
        order = Order(
            order_id="x", instrument="MSFT", side=Side.BUY,
            quantity=100, price=300.0,
        )
        result = validate_position(position_limit, order, {}, sample_exposures, {})
        assert result.passed is True


class TestValidateConcentration:
    def test_within_limits(self, sample_exposures, concentration_limit):
        # Build diversified portfolio where AAPL is small fraction
        positions = {
            "AAPL": Position(instrument="AAPL", quantity=10, avg_price=150.0, current_price=150.0),
            "GOOGL": Position(instrument="GOOGL", quantity=50, avg_price=150.0, current_price=150.0),
            "MSFT": Position(instrument="MSFT", quantity=50, avg_price=150.0, current_price=150.0),
            "AMZN": Position(instrument="AMZN", quantity=50, avg_price=150.0, current_price=150.0),
            "TSLA": Position(instrument="TSLA", quantity=50, avg_price=150.0, current_price=150.0),
        }
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY,
            quantity=5, price=150.0, sector="technology",
        )
        result = validate_concentration(
            concentration_limit, order, positions, sample_exposures, {}
        )
        assert result.passed is True

    def test_empty_portfolio(self, sample_order, sample_exposures, concentration_limit):
        result = validate_concentration(
            concentration_limit, sample_order, {}, sample_exposures, {}
        )
        assert result.passed is True  # Skipped due to no positions

    def test_exceeds_single_asset(self, sample_exposures, concentration_limit):
        # Create positions where one asset dominates
        positions = {
            "AAPL": Position(
                instrument="AAPL", quantity=1000, avg_price=150.0, current_price=150.0,
            ),
        }
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY,
            quantity=10000, price=150.0,
        )
        # The limit is max_single_asset_pct=0.15 — AAPL will be ~100%
        result = validate_concentration(
            concentration_limit, order, positions, sample_exposures, {}
        )
        assert result.passed is False
        assert "concentration" in result.message.lower()


class TestValidatePriceTolerance:
    def test_within_tolerance(self, sample_positions, sample_exposures, price_tolerance_limit):
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY,
            quantity=100, price=151.0,  # ~0.7% deviation from 150
        )
        result = validate_price_tolerance(
            price_tolerance_limit, order, sample_positions, sample_exposures, {}
        )
        assert result.passed is True

    def test_exceeds_tolerance(self, sample_positions, sample_exposures, price_tolerance_limit):
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY,
            quantity=100, price=200.0,  # 33% deviation from 150
        )
        result = validate_price_tolerance(
            price_tolerance_limit, order, sample_positions, sample_exposures, {}
        )
        assert result.passed is False
        assert "deviates" in result.message

    def test_no_reference_price(self, sample_exposures, price_tolerance_limit):
        order = Order(
            order_id="x", instrument="UNKNOWN", side=Side.BUY,
            quantity=100, price=100.0,
        )
        result = validate_price_tolerance(
            price_tolerance_limit, order, {}, sample_exposures, {}
        )
        assert result.passed is True  # Skipped

    def test_reference_from_context(self, sample_exposures, price_tolerance_limit):
        order = Order(
            order_id="x", instrument="MSFT", side=Side.BUY,
            quantity=100, price=350.0,
        )
        result = validate_price_tolerance(
            price_tolerance_limit, order, {}, sample_exposures,
            {"reference_price": 300.0},
        )
        assert result.passed is False  # 16.7% deviation


class TestValidateOrderRate:
    def test_within_rate(self):
        limit = LimitSpec(
            name="rate", type=LimitType.ORDER_RATE, scope=LimitScope.ORDER,
            params={"max_orders": 100}, severity=Severity.WARNING,
        )
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0,
        )
        result = validate_order_rate(limit, order, {}, {}, {"recent_order_count": 50})
        assert result.passed is True

    def test_exceeds_rate(self):
        limit = LimitSpec(
            name="rate", type=LimitType.ORDER_RATE, scope=LimitScope.ORDER,
            params={"max_orders": 100}, severity=Severity.WARNING,
        )
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0,
        )
        result = validate_order_rate(limit, order, {}, {}, {"recent_order_count": 100})
        assert result.passed is False


class TestValidateNotional:
    def test_within_limit(self):
        limit = LimitSpec(
            name="notional", type=LimitType.NOTIONAL, scope=LimitScope.ORDER,
            params={"max_notional": 100000}, severity=Severity.CRITICAL,
        )
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0,
        )
        result = validate_notional(limit, order, {}, {}, {})
        assert result.passed is True

    def test_exceeds_limit(self):
        limit = LimitSpec(
            name="notional", type=LimitType.NOTIONAL, scope=LimitScope.ORDER,
            params={"max_notional": 10000}, severity=Severity.CRITICAL,
        )
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0,
        )
        result = validate_notional(limit, order, {}, {}, {})
        assert result.passed is False


class TestValidateSelfTrade:
    def test_no_conflict(self):
        limit = LimitSpec(
            name="self_trade", type=LimitType.SELF_TRADE, scope=LimitScope.INSTRUMENT,
            severity=Severity.CRITICAL,
        )
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0,
        )
        result = validate_self_trade(limit, order, {}, {}, {"pending_orders": []})
        assert result.passed is True

    def test_conflict_detected(self):
        limit = LimitSpec(
            name="self_trade", type=LimitType.SELF_TRADE, scope=LimitScope.INSTRUMENT,
            severity=Severity.CRITICAL,
        )
        pending = Order(
            order_id="p-1", instrument="AAPL", side=Side.SELL, quantity=50, price=150.0,
        )
        order = Order(
            order_id="x", instrument="AAPL", side=Side.BUY, quantity=100, price=150.0,
        )
        result = validate_self_trade(
            limit, order, {}, {}, {"pending_orders": [pending]}
        )
        assert result.passed is False
        assert "self-trade" in result.message.lower()
