"""
Loyalty program models for customer retention and rewards.
"""
import enum
from sqlalchemy import Column, Integer, String, Enum, ForeignKey, Text, Boolean, Float, Date
from sqlalchemy.orm import relationship

from app.models.base import BaseModel


class LoyaltyTier(str, enum.Enum):
    """Loyalty program tiers."""
    BRONZE = "bronze"
    SILVER = "silver"
    GOLD = "gold"
    PLATINUM = "platinum"


class PointTransactionType(str, enum.Enum):
    """Types of point transactions."""
    EARN = "earn"  # Points earned
    REDEEM = "redeem"  # Points redeemed
    EXPIRE = "expire"  # Points expired
    ADJUSTMENT = "adjustment"  # Manual adjustment
    BONUS = "bonus"  # Bonus points


class LoyaltyMembership(BaseModel):
    """
    Loyalty program membership for clients.

    Tracks customer loyalty tier, points balance, and membership status.

    Attributes:
        client_id: Associated client
        establishment_id: Associated establishment
        tier: Current tier level
        points_balance: Current points balance
        points_earned_lifetime: Total points earned all-time
        points_redeemed_lifetime: Total points redeemed all-time
        tier_points: Points for current tier calculation
        member_since: Membership start date
        tier_achieved_at: Date current tier was achieved
        is_active: Whether membership is active
        notes: Staff notes
    """
    __tablename__ = "loyalty_memberships"

    client_id = Column(Integer, ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True, unique=True)
    establishment_id = Column(Integer, ForeignKey("establishments.id", ondelete="CASCADE"), nullable=False, index=True)

    # Tier and points
    tier = Column(Enum(LoyaltyTier), default=LoyaltyTier.BRONZE, nullable=False, index=True)
    points_balance = Column(Integer, default=0, nullable=False)
    points_earned_lifetime = Column(Integer, default=0, nullable=False)
    points_redeemed_lifetime = Column(Integer, default=0, nullable=False)
    tier_points = Column(Integer, default=0, nullable=False)  # Points counting towards tier

    # Membership dates
    member_since = Column(Date, nullable=False)
    tier_achieved_at = Column(Date, nullable=True)

    # Status
    is_active = Column(Boolean, default=True, nullable=False)
    notes = Column(Text, nullable=True)

    # Relationships
    client = relationship("Client", back_populates="loyalty_membership")
    establishment = relationship("Establishment")
    transactions = relationship("PointTransaction", back_populates="membership", cascade="all, delete-orphan")

    @property
    def points_to_next_tier(self) -> int:
        """Calculate points needed to reach next tier."""
        tier_thresholds = {
            LoyaltyTier.BRONZE: 0,
            LoyaltyTier.SILVER: 1000,
            LoyaltyTier.GOLD: 5000,
            LoyaltyTier.PLATINUM: 10000
        }

        current_threshold = tier_thresholds[self.tier]
        tiers_list = list(LoyaltyTier)
        current_index = tiers_list.index(self.tier)

        if current_index >= len(tiers_list) - 1:
            return 0  # Already at highest tier

        next_tier = tiers_list[current_index + 1]
        next_threshold = tier_thresholds[next_tier]

        return max(0, next_threshold - self.tier_points)

    def __repr__(self) -> str:
        return f"<LoyaltyMembership {self.client_id} ({self.tier})>"


class PointTransaction(BaseModel):
    """
    Point transaction history for loyalty program.

    Records all point earning, redemption, and adjustment activities.

    Attributes:
        membership_id: Associated membership
        transaction_type: Type of transaction
        points: Number of points (positive for earn, negative for redeem)
        balance_after: Points balance after transaction
        reservation_id: Associated reservation (if applicable)
        description: Transaction description
        reference: External reference (e.g., invoice number)
    """
    __tablename__ = "point_transactions"

    membership_id = Column(Integer, ForeignKey("loyalty_memberships.id", ondelete="CASCADE"), nullable=False, index=True)
    transaction_type = Column(Enum(PointTransactionType), nullable=False, index=True)

    # Points
    points = Column(Integer, nullable=False)  # Positive for earn, negative for redeem
    balance_after = Column(Integer, nullable=False)

    # References
    reservation_id = Column(Integer, ForeignKey("reservations.id", ondelete="SET NULL"), nullable=True)
    description = Column(String(500), nullable=True)
    reference = Column(String(100), nullable=True, index=True)

    # Relationships
    membership = relationship("LoyaltyMembership", back_populates="transactions")
    reservation = relationship("Reservation")

    def __repr__(self) -> str:
        return f"<PointTransaction {self.transaction_type}: {self.points} pts>"


class LoyaltyReward(BaseModel):
    """
    Loyalty rewards catalog.

    Defines available rewards that members can redeem with points.

    Attributes:
        establishment_id: Associated establishment
        name: Reward name
        name_en: English name
        description: Reward description
        points_required: Points needed to redeem
        category: Reward category (e.g., "Room Upgrade", "F&B Credit")
        is_active: Whether reward is currently available
        terms: Terms and conditions
        validity_days: Days the reward is valid after redemption
    """
    __tablename__ = "loyalty_rewards"

    establishment_id = Column(Integer, ForeignKey("establishments.id", ondelete="CASCADE"), nullable=False, index=True)

    # Reward details
    name = Column(String(200), nullable=False)
    name_en = Column(String(200), nullable=True)
    description = Column(Text, nullable=True)
    points_required = Column(Integer, nullable=False, index=True)
    category = Column(String(100), nullable=True, index=True)

    # Status and conditions
    is_active = Column(Boolean, default=True, nullable=False)
    terms = Column(Text, nullable=True)
    validity_days = Column(Integer, default=30, nullable=False)

    # Relationships
    establishment = relationship("Establishment")

    def __repr__(self) -> str:
        return f"<LoyaltyReward {self.name} ({self.points_required} pts)>"
