"""
Expense Calculator Module
Handles expense calculations including splits and settlements.
"""

from decimal import Decimal
from typing import List, Dict, Optional
from enum import Enum


class SplitMethod(Enum):
    EQUAL = "equal"
    PERCENTAGE = "percentage"
    EXACT = "exact"


class ExpenseCalculator:
    """Main calculator class for expense operations."""
    
    @staticmethod
    def calculate_split(
        total_amount: Decimal,
        participants: List[str],
        split_method: SplitMethod = SplitMethod.EQUAL,
        participant_data: Optional[Dict[str, Dict]] = None
    ) -> Dict[str, Decimal]:
    
        if not participants:
            return {}
        
        participant_data = participant_data or {}
        
        if split_method == SplitMethod.EQUAL:
            return ExpenseCalculator._calculate_equal_split(total_amount, participants)
        elif split_method == SplitMethod.PERCENTAGE:
            return ExpenseCalculator._calculate_percentage_split(
                total_amount, participants, participant_data
            )
        elif split_method == SplitMethod.EXACT:
            return ExpenseCalculator._calculate_exact_split(
                total_amount, participants, participant_data
            )
        else:
            raise ValueError(f"Unknown split method: {split_method}")
    
    @staticmethod
    def _calculate_equal_split(
        total_amount: Decimal,
        participants: List[str]
    ) -> Dict[str, Decimal]:
        """Calculate equal split among all participants."""
        num_participants = Decimal(len(participants))
        amount_per_person = total_amount / num_participants
        
        return {
            participant: ExpenseCalculator._round_decimal(amount_per_person)
            for participant in participants
        }
    
    @staticmethod
    def _calculate_percentage_split(
        total_amount: Decimal,
        participants: List[str],
        participant_data: Dict[str, Dict]
    ) -> Dict[str, Decimal]:
        """Calculate split based on percentages."""
        # Get percentages and normalize if needed
        percentages = {}
        total_percentage = Decimal(0)
        
        for participant in participants:
            pct = Decimal(str(participant_data.get(participant, {}).get("percentage", 0)))
            percentages[participant] = pct
            total_percentage += pct
        
        # Normalize if total doesn't equal 100
        if total_percentage != Decimal(100) and total_percentage > 0:
            for participant in percentages:
                percentages[participant] = (percentages[participant] / total_percentage) * Decimal(100)
        
        # Calculate amounts
        result = {}
        for participant in participants:
            pct = percentages.get(participant, Decimal(0))
            amount = (total_amount * pct) / Decimal(100)
            result[participant] = ExpenseCalculator._round_decimal(amount)
        
        return result
    
    @staticmethod
    def _calculate_exact_split(
        total_amount: Decimal,
        participants: List[str],
        participant_data: Dict[str, Dict]
    ) -> Dict[str, Decimal]:
        """Calculate split based on exact amounts."""
        amounts = {}
        total_specified = Decimal(0)
        
        for participant in participants:
            amt = Decimal(str(participant_data.get(participant, {}).get("amount", 0)))
            amounts[participant] = amt
            total_specified += amt
        
        # Normalize if total doesn't match expense amount
        if total_specified != total_amount and total_specified > 0:
            factor = total_amount / total_specified
            for participant in amounts:
                amounts[participant] = amounts[participant] * factor
        
        return {
            participant: ExpenseCalculator._round_decimal(amounts.get(participant, Decimal(0)))
            for participant in participants
        }
    
    @staticmethod
    def _round_decimal(value: Decimal, places: int = 2) -> Decimal:
        """Round decimal to specified places."""
        return value.quantize(Decimal('0.01'))
    
    @staticmethod
    def validate_split(
        total_amount: Decimal,
        participants: List[str],
        split_method: SplitMethod,
        participant_data: Optional[Dict[str, Dict]] = None
    ) -> tuple[bool, Optional[str]]:
        """
        Validate that split configuration is correct.
        
        Returns:
            Tuple of (is_valid, error_message)
        """
        if not participants:
            return False, "At least one participant is required"
        
        if total_amount <= 0:
            return False, "Total amount must be greater than 0"
        
        participant_data = participant_data or {}
        
        if split_method == SplitMethod.PERCENTAGE:
            total_percentage = Decimal(0)
            for participant in participants:
                pct = Decimal(str(participant_data.get(participant, {}).get("percentage", 0)))
                total_percentage += pct
            
            if abs(total_percentage - Decimal(100)) > Decimal("0.01"):
                return False, f"Total percentage must equal 100% (currently {total_percentage}%)"
        
        elif split_method == SplitMethod.EXACT:
            total_specified = Decimal(0)
            for participant in participants:
                amt = Decimal(str(participant_data.get(participant, {}).get("amount", 0)))
                total_specified += amt
            
            if abs(total_specified - total_amount) > Decimal("0.01"):
                return False, f"Total amount must equal expense amount ${total_amount} (currently ${total_specified})"
        
        return True, None

