"""Tests for fee calculator functionality."""

import pytest
from decimal import Decimal
from unittest.mock import Mock, patch
from src.trading.fee_calculator import FeeCalculator, get_fee_calculator
from src.core.database import OrderType


class TestFeeCalculator:
    """Tests for FeeCalculator class."""
    
    @pytest.fixture
    def calculator(self):
        """Create fee calculator instance."""
        return FeeCalculator()
    
    def test_get_fee_calculator_singleton(self):
        """Test that get_fee_calculator returns singleton."""
        calc1 = get_fee_calculator()
        calc2 = get_fee_calculator()
        assert calc1 is calc2
    
    def test_calculate_fee_basic(self, calculator):
        """Test basic fee calculation."""
        fee = calculator.calculate_fee(
            quantity=Decimal('1.0'),
            price=Decimal('100.0'),
            order_type=OrderType.MARKET
        )
        assert fee > 0
        assert isinstance(fee, Decimal)
    
    def test_calculate_fee_zero_quantity(self, calculator):
        """Test fee calculation with zero quantity."""
        fee = calculator.calculate_fee(
            quantity=Decimal('0'),
            price=Decimal('100.0'),
            order_type=OrderType.MARKET
        )
        assert fee == Decimal('0')
    
    def test_calculate_fee_maker_vs_taker(self, calculator):
        """Test maker fees are typically lower than taker fees."""
        maker_fee = calculator.calculate_fee(
            quantity=Decimal('1.0'),
            price=Decimal('1000.0'),
            order_type=OrderType.LIMIT,
            is_maker=True
        )
        taker_fee = calculator.calculate_fee(
            quantity=Decimal('1.0'),
            price=Decimal('1000.0'),
            order_type=OrderType.MARKET,
            is_maker=False
        )
        # Maker fees should be <= taker fees
        assert maker_fee <= taker_fee


class TestFeeCalculatorPaperTrading:
    """Tests for paper trading fee calculation."""
    
    @pytest.fixture
    def calculator(self):
        """Create fee calculator instance."""
        return FeeCalculator()
    
    def test_get_fee_structure_by_exchange_name_coinbase(self, calculator):
        """Test getting Coinbase fee structure."""
        with patch.object(calculator, 'config') as mock_config:
            mock_config.get.side_effect = lambda key, default=None: {
                'trading.default_fees': {'maker': 0.001, 'taker': 0.001},
                'trading.exchanges.coinbase.fees': {'maker': 0.004, 'taker': 0.006}
            }.get(key, default)
            
            fees = calculator.get_fee_structure_by_exchange_name('coinbase')
            assert fees['maker'] == 0.004
            assert fees['taker'] == 0.006
    
    def test_get_fee_structure_by_exchange_name_unknown(self, calculator):
        """Test getting fee structure for unknown exchange returns defaults."""
        with patch.object(calculator, 'config') as mock_config:
            mock_config.get.side_effect = lambda key, default=None: {
                'trading.default_fees': {'maker': 0.001, 'taker': 0.001},
                'trading.exchanges.unknown.fees': None
            }.get(key, default)
            
            fees = calculator.get_fee_structure_by_exchange_name('unknown')
            assert fees['maker'] == 0.001
            assert fees['taker'] == 0.001
    
    def test_calculate_fee_for_paper_trading(self, calculator):
        """Test paper trading fee calculation."""
        with patch.object(calculator, 'config') as mock_config:
            mock_config.get.side_effect = lambda key, default=None: {
                'paper_trading.fee_exchange': 'coinbase',
                'trading.exchanges.coinbase.fees': {'maker': 0.004, 'taker': 0.006},
                'trading.default_fees': {'maker': 0.001, 'taker': 0.001}
            }.get(key, default)
            
            fee = calculator.calculate_fee_for_paper_trading(
                quantity=Decimal('1.0'),
                price=Decimal('1000.0'),
                order_type=OrderType.MARKET,
                is_maker=False
            )
            # Taker fee at 0.6% of $1000 = $6
            expected = Decimal('1000.0') * Decimal('0.006')
            assert fee == expected
    
    def test_calculate_fee_for_paper_trading_zero(self, calculator):
        """Test paper trading fee with zero values."""
        fee = calculator.calculate_fee_for_paper_trading(
            quantity=Decimal('0'),
            price=Decimal('100.0'),
            order_type=OrderType.MARKET
        )
        assert fee == Decimal('0')


class TestRoundTripFees:
    """Tests for round-trip fee calculations."""
    
    @pytest.fixture
    def calculator(self):
        """Create fee calculator instance."""
        return FeeCalculator()
    
    def test_estimate_round_trip_fee(self, calculator):
        """Test round-trip fee estimation."""
        fee = calculator.estimate_round_trip_fee(
            quantity=Decimal('1.0'),
            price=Decimal('1000.0')
        )
        # Round-trip should include buy and sell fees
        assert fee > 0
        
        single_fee = calculator.calculate_fee(
            quantity=Decimal('1.0'),
            price=Decimal('1000.0'),
            order_type=OrderType.MARKET
        )
        # Round-trip should be approximately 2x single fee
        assert fee >= single_fee
    
    def test_get_minimum_profit_threshold(self, calculator):
        """Test minimum profit threshold calculation."""
        threshold = calculator.get_minimum_profit_threshold(
            quantity=Decimal('1.0'),
            price=Decimal('1000.0'),
            multiplier=2.0
        )
        assert threshold > 0
        
        round_trip_fee = calculator.estimate_round_trip_fee(
            quantity=Decimal('1.0'),
            price=Decimal('1000.0')
        )
        # Threshold should be 2x round-trip fee
        assert threshold == round_trip_fee * Decimal('2.0')
