"""
Folio and FolioItem models for guest accounts and charges.
"""
import enum
from sqlalchemy import Column, String, Integer, Float, Enum, ForeignKey, DateTime, Text, Boolean
from sqlalchemy.orm import relationship

from app.models.base import BaseModel


class FolioStatus(str, enum.Enum):
    """Folio status."""
    OPEN = "open"
    CLOSED = "closed"
    SETTLED = "settled"


class Folio(BaseModel):
    """
    Guest folio (account) for tracking charges and payments.

    Each reservation has one folio that tracks all charges
    (room, restaurant, minibar, etc.) and payments.

    Attributes:
        reservation_id: Associated reservation
        status: Folio status (open/closed/settled)
        folio_number: Unique folio number
        total_charges: Total charges amount
        total_payments: Total payments received
        balance: Outstanding balance
        closed_at: When folio was closed
        closed_by_id: User who closed the folio
    """
    __tablename__ = "folios"

    reservation_id = Column(
        Integer,
        ForeignKey("reservations.id", ondelete="CASCADE"),
        nullable=False,
        unique=True,
        index=True
    )
    status = Column(
        Enum(FolioStatus),
        default=FolioStatus.OPEN,
        nullable=False,
        index=True
    )
    folio_number = Column(String(50), unique=True, nullable=False, index=True)
    total_charges = Column(Float, default=0.0, nullable=False)
    total_payments = Column(Float, default=0.0, nullable=False)
    balance = Column(Float, default=0.0, nullable=False)
    currency = Column(String(3), default="XOF", nullable=False)
    closed_at = Column(DateTime, nullable=True)
    closed_by_id = Column(
        Integer,
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True
    )
    notes = Column(Text, nullable=True)

    # Relationships
    reservation = relationship("Reservation", back_populates="folio")
    items = relationship(
        "FolioItem",
        back_populates="folio",
        cascade="all, delete-orphan",
        order_by="FolioItem.created_at"
    )
    payments = relationship(
        "Payment",
        back_populates="folio",
        cascade="all, delete-orphan",
        order_by="Payment.payment_date"
    )
    notifications = relationship("Notification", back_populates="folio")

    def recalculate_totals(self) -> None:
        """Recalculate folio totals from items and payments."""
        self.total_charges = round(sum(item.total_ttc for item in self.items if not item.is_voided), 2)
        self.total_payments = round(sum(payment.amount for payment in self.payments), 2)
        self.balance = round(self.total_charges - self.total_payments, 2)

    @property
    def is_settled(self) -> bool:
        """Check if folio is fully paid."""
        return self.balance <= 0

    def __repr__(self) -> str:
        return f"<Folio {self.folio_number}>"


class FolioItem(BaseModel):
    """
    Individual charge/item on a folio.

    Attributes:
        folio_id: Parent folio
        department_code: Department code for categorization
        description: Item description
        quantity: Quantity
        unit_price: Unit price before tax
        tax_rate: Tax rate percentage
        total_ht: Total before tax
        total_ttc: Total including tax
        posted_by_id: User who posted the charge
        reference: External reference (e.g., restaurant bill number)
    """
    __tablename__ = "folio_items"

    folio_id = Column(
        Integer,
        ForeignKey("folios.id", ondelete="CASCADE"),
        nullable=False,
        index=True
    )
    department_code = Column(String(20), nullable=False, index=True)
    description = Column(String(500), nullable=False)
    quantity = Column(Float, default=1.0, nullable=False)
    unit_price = Column(Float, nullable=False)
    tax_rate = Column(Float, default=0.0, nullable=False)
    total_ht = Column(Float, nullable=False)
    total_ttc = Column(Float, nullable=False)
    posted_by_id = Column(
        Integer,
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True
    )
    reference = Column(String(100), nullable=True)
    is_voided = Column(Boolean, default=False, nullable=False)
    voided_at = Column(DateTime, nullable=True)
    voided_by_id = Column(
        Integer,
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True
    )
    voided_reason = Column(String(500), nullable=True)

    # Relationships
    folio = relationship("Folio", back_populates="items")

    def calculate_totals(self) -> None:
        """Calculate totals based on quantity, unit price, and tax rate."""
        self.total_ht = round(self.quantity * self.unit_price, 2)
        tax_amount = round(self.total_ht * (self.tax_rate / 100), 2)
        self.total_ttc = round(self.total_ht + tax_amount, 2)

    def __repr__(self) -> str:
        return f"<FolioItem {self.description}: {self.total_ttc}>"
