"""
KPI calculation service.
Implements all hotel industry performance metrics.
"""
from typing import Optional
from sqlalchemy.orm import Session


class CalculatorService:
    """Service for calculating hotel KPIs."""

    def __init__(self, db: Session):
        self.db = db

    def calculate_occupancy_rate(
        self,
        rooms_sold: int,
        rooms_available: int,
        days: int = 1
    ) -> float:
        """
        Calculate occupancy rate (Taux d'Occupation).

        Formula: TO = (Chambres vendues / (Capacité × Jours)) × 100

        Args:
            rooms_sold: Number of rooms sold
            rooms_available: Total rooms available (capacity)
            days: Number of days in period

        Returns:
            Occupancy rate as percentage
        """
        if rooms_available == 0 or days == 0:
            return 0.0

        total_room_nights = rooms_available * days
        return (rooms_sold / total_room_nights) * 100

    def calculate_bed_occupancy_rate(
        self,
        nights_sold: int,
        bed_capacity: int,
        days: int = 1
    ) -> float:
        """
        Calculate bed occupancy rate (Taux d'Occupation Lits).

        Formula: TO Lits = (Nuitées / (Capacité lits × Jours)) × 100

        Args:
            nights_sold: Number of guest nights
            bed_capacity: Total bed capacity
            days: Number of days in period

        Returns:
            Bed occupancy rate as percentage
        """
        if bed_capacity == 0 or days == 0:
            return 0.0

        total_bed_nights = bed_capacity * days
        return (nights_sold / total_bed_nights) * 100

    def calculate_frequency_index(
        self,
        nights_sold: int,
        rooms_sold: int
    ) -> float:
        """
        Calculate frequency index (Indice de Fréquentation).

        Formula: IF = Nuitées / Chambres vendues

        Represents average guests per room.

        Args:
            nights_sold: Number of guest nights
            rooms_sold: Number of rooms sold

        Returns:
            Frequency index
        """
        if rooms_sold == 0:
            return 0.0

        return nights_sold / rooms_sold

    def calculate_adr(
        self,
        accommodation_revenue: float,
        rooms_sold: int
    ) -> float:
        """
        Calculate Average Daily Rate (ADR).

        Formula: ADR = CA Hébergement / Chambres vendues

        Args:
            accommodation_revenue: Total accommodation revenue
            rooms_sold: Number of rooms sold

        Returns:
            ADR value
        """
        if rooms_sold == 0:
            return 0.0

        return accommodation_revenue / rooms_sold

    def calculate_revpar(
        self,
        accommodation_revenue: float,
        rooms_available: int,
        days: int = 1
    ) -> float:
        """
        Calculate Revenue Per Available Room (RevPAR).

        Formula: RevPAR = CA Hébergement / (Capacité × Jours)

        Can also be calculated as: ADR × Taux d'occupation

        Args:
            accommodation_revenue: Total accommodation revenue
            rooms_available: Total rooms available
            days: Number of days in period

        Returns:
            RevPAR value
        """
        if rooms_available == 0 or days == 0:
            return 0.0

        total_room_nights = rooms_available * days
        return accommodation_revenue / total_room_nights

    def calculate_average_guest_revenue(
        self,
        total_revenue: float,
        nights_sold: int
    ) -> float:
        """
        Calculate average revenue per guest (Recette Moyenne Client).

        Formula: RMC = CA Total / Nuitées

        Args:
            total_revenue: Total revenue (all departments)
            nights_sold: Number of guest nights

        Returns:
            Average revenue per guest
        """
        if nights_sold == 0:
            return 0.0

        return total_revenue / nights_sold

    def calculate_fb_ratio(
        self,
        food_cost_ht: float,
        fb_revenue_ht: float
    ) -> float:
        """
        Calculate Food & Beverage ratio.

        Formula: Ratio F&B = (Coût alimentaire HT / CA F&B HT) × 100

        Args:
            food_cost_ht: Food cost before tax
            fb_revenue_ht: F&B revenue before tax

        Returns:
            F&B ratio as percentage
        """
        if fb_revenue_ht == 0:
            return 0.0

        return (food_cost_ht / fb_revenue_ht) * 100

    def calculate_trevpar(
        self,
        total_revenue: float,
        rooms_available: int,
        days: int = 1
    ) -> float:
        """
        Calculate Total Revenue Per Available Room (TRevPAR).

        Formula: TRevPAR = CA Total / (Capacité × Jours)

        Args:
            total_revenue: Total revenue (all departments)
            rooms_available: Total rooms available
            days: Number of days in period

        Returns:
            TRevPAR value
        """
        if rooms_available == 0 or days == 0:
            return 0.0

        total_room_nights = rooms_available * days
        return total_revenue / total_room_nights

    def calculate_goppar(
        self,
        gross_operating_profit: float,
        rooms_available: int,
        days: int = 1
    ) -> float:
        """
        Calculate Gross Operating Profit Per Available Room (GOPPAR).

        Formula: GOPPAR = GOP / (Capacité × Jours)

        Args:
            gross_operating_profit: GOP amount
            rooms_available: Total rooms available
            days: Number of days in period

        Returns:
            GOPPAR value
        """
        if rooms_available == 0 or days == 0:
            return 0.0

        total_room_nights = rooms_available * days
        return gross_operating_profit / total_room_nights

    def calculate_double_occupancy_rate(
        self,
        nights_sold: int,
        rooms_sold: int
    ) -> float:
        """
        Calculate double occupancy rate.

        Formula: ((Nuitées - Chambres vendues) / Chambres vendues) × 100

        Args:
            nights_sold: Number of guest nights
            rooms_sold: Number of rooms sold

        Returns:
            Double occupancy rate as percentage
        """
        if rooms_sold == 0:
            return 0.0

        return ((nights_sold - rooms_sold) / rooms_sold) * 100

    def calculate_length_of_stay(
        self,
        total_nights: int,
        total_reservations: int
    ) -> float:
        """
        Calculate average length of stay (ALOS).

        Args:
            total_nights: Total nights from all reservations
            total_reservations: Number of reservations

        Returns:
            Average length of stay in nights
        """
        if total_reservations == 0:
            return 0.0

        return total_nights / total_reservations

    def calculate_all_kpis(
        self,
        rooms_sold: int,
        rooms_available: int,
        nights_sold: int,
        bed_capacity: int,
        accommodation_revenue: float,
        total_revenue: float,
        days: int = 1
    ) -> dict:
        """
        Calculate all KPIs at once.

        Returns:
            Dictionary with all KPI values
        """
        return {
            "occupancy_rate": round(
                self.calculate_occupancy_rate(rooms_sold, rooms_available, days), 2
            ),
            "bed_occupancy_rate": round(
                self.calculate_bed_occupancy_rate(nights_sold, bed_capacity, days), 2
            ),
            "frequency_index": round(
                self.calculate_frequency_index(nights_sold, rooms_sold), 2
            ),
            "adr": round(
                self.calculate_adr(accommodation_revenue, rooms_sold), 2
            ),
            "revpar": round(
                self.calculate_revpar(accommodation_revenue, rooms_available, days), 2
            ),
            "average_guest_revenue": round(
                self.calculate_average_guest_revenue(total_revenue, nights_sold), 2
            ),
            "trevpar": round(
                self.calculate_trevpar(total_revenue, rooms_available, days), 2
            ),
            "double_occupancy_rate": round(
                self.calculate_double_occupancy_rate(nights_sold, rooms_sold), 2
            )
        }
