"""
Price Calculator Module

Handles all price calculations including base pricing, discounts, and total cost.
"""

from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP


class PriceCalculator:
    """
    Calculate booking prices with automatic discounts.
    
    Features:
    - Calculate number of nights between dates
    - Apply automatic discounts (10% for 7+ nights, 20% for 30+ nights)
    - Calculate total price with all discounts applied
    """
    
    def __init__(self):
        """Initialize PriceCalculator"""
        self.weekly_discount = Decimal('0.10')   # 10% for 7+ nights
        self.monthly_discount = Decimal('0.20')  # 20% for 30+ nights
    
    def calculate_nights(self, check_in_date, check_out_date):
        """
        Calculate number of nights between check-in and check-out dates.
        
        Args:
            check_in_date (str or datetime): Check-in date (YYYY-MM-DD format if string)
            check_out_date (str or datetime): Check-out date (YYYY-MM-DD format if string)
        
        Returns:
            int: Number of nights
        
        Raises:
            ValueError: If check-out is before or same as check-in
        
        Example:
            >>> calc = PriceCalculator()
            >>> calc.calculate_nights('2025-12-01', '2025-12-05')
            4
        """
        # Convert strings to datetime if needed
        if isinstance(check_in_date, str):
            check_in = datetime.strptime(check_in_date, '%Y-%m-%d')
        else:
            check_in = check_in_date
        
        if isinstance(check_out_date, str):
            check_out = datetime.strptime(check_out_date, '%Y-%m-%d')
        else:
            check_out = check_out_date
        
        # Validate dates
        if check_out <= check_in:
            raise ValueError("Check-out date must be after check-in date")
        
        # Calculate nights
        nights = (check_out - check_in).days
        
        return nights
    
    def apply_discount(self, base_price, num_nights):
        """
        Apply automatic discounts based on number of nights.
        
        Discount structure:
        - 7-29 nights: 10% discount
        - 30+ nights: 20% discount
        
        Args:
            base_price (float or Decimal): Base price before discount
            num_nights (int): Number of nights
        
        Returns:
            dict: Dictionary containing discount_rate, discount_amount, and final_price
        
        Example:
            >>> calc = PriceCalculator()
            >>> calc.apply_discount(1000, 10)
            {'discount_rate': 0.10, 'discount_amount': 100.0, 'final_price': 900.0}
        """
        base = Decimal(str(base_price))
        discount_rate = Decimal('0')
        
        # Apply discount based on stay duration
        if num_nights >= 30:
            discount_rate = self.monthly_discount
        elif num_nights >= 7:
            discount_rate = self.weekly_discount
        
        # Calculate discount amount
        discount_amount = (base * discount_rate).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        final_price = base - discount_amount
        
        return {
            'discount_rate': float(discount_rate),
            'discount_amount': float(discount_amount),
            'final_price': float(final_price)
        }
    
    def calculate_total(self, price_per_night, check_in_date, check_out_date):
        """
        Calculate total booking price with automatic discounts.
        
        Args:
            price_per_night (float): Price per night
            check_in_date (str or datetime): Check-in date
            check_out_date (str or datetime): Check-out date
        
        Returns:
            dict: Complete pricing breakdown including:
                - num_nights: Number of nights
                - price_per_night: Nightly rate
                - subtotal: Base price before discount
                - discount_rate: Applied discount rate
                - discount_amount: Discount in currency
                - total_price: Final price after discount
        
        Example:
            >>> calc = PriceCalculator()
            >>> result = calc.calculate_total(100, '2025-12-01', '2025-12-10')
            >>> result['total_price']
            810.0  # 9 nights * 100 = 900, minus 10% discount = 810
        """
        # Calculate number of nights
        num_nights = self.calculate_nights(check_in_date, check_out_date)
        
        # Calculate base price
        price_per_night_decimal = Decimal(str(price_per_night))
        subtotal = (price_per_night_decimal * num_nights).quantize(
            Decimal('0.01'), 
            rounding=ROUND_HALF_UP
        )
        
        # Apply discount
        discount_info = self.apply_discount(subtotal, num_nights)
        
        return {
            'num_nights': num_nights,
            'price_per_night': float(price_per_night_decimal),
            'subtotal': float(subtotal),
            'discount_rate': discount_info['discount_rate'],
            'discount_amount': discount_info['discount_amount'],
            'total_price': discount_info['final_price']
        }
    
    def calculate_refund(self, total_paid, days_until_checkin):
        """
        Calculate refund amount based on cancellation policy.
        
        Refund policy:
        - 14+ days before: 100% refund
        - 7-13 days before: 50% refund
        - Less than 7 days: No refund
        
        Args:
            total_paid (float): Total amount paid
            days_until_checkin (int): Days until check-in date
        
        Returns:
            dict: Refund information including refund_rate and refund_amount
        
        Example:
            >>> calc = PriceCalculator()
            >>> calc.calculate_refund(500, 10)
            {'refund_rate': 0.50, 'refund_amount': 250.0}
        """
        total = Decimal(str(total_paid))
        
        if days_until_checkin >= 14:
            refund_rate = Decimal('1.00')  # 100% refund
        elif days_until_checkin >= 7:
            refund_rate = Decimal('0.50')  # 50% refund
        else:
            refund_rate = Decimal('0.00')  # No refund
        
        refund_amount = (total * refund_rate).quantize(
            Decimal('0.01'), 
            rounding=ROUND_HALF_UP
        )
        
        return {
            'refund_rate': float(refund_rate),
            'refund_amount': float(refund_amount)
        }