# app/modules/loyalty/models/loyalty_program.py
"""
Loyalty program database model.

Merchant-based loyalty program configuration:
- Program belongs to Merchant (one program per merchant)
- All stores under a merchant share the same loyalty program
- Customers earn and redeem points at any location (store) within the merchant

Defines:
- Program type (stamps, points, hybrid)
- Stamp configuration (target, reward description)
- Points configuration (per euro rate, rewards catalog)
- Anti-fraud settings (cooldown, daily limits, PIN requirement)
- Branding (card name, color, logo)
- Wallet integration IDs (Google, Apple)
"""

import enum
from datetime import UTC, datetime

from sqlalchemy import (
    Boolean,
    CheckConstraint,
    Column,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
)
from sqlalchemy.dialects.sqlite import JSON
from sqlalchemy.orm import relationship

from app.core.database import Base
from models.database.base import SoftDeleteMixin, TimestampMixin


class LoyaltyType(str, enum.Enum):
    """Type of loyalty program."""

    STAMPS = "stamps"  # Collect N stamps, get reward
    POINTS = "points"  # Earn points per euro, redeem for rewards
    HYBRID = "hybrid"  # Both stamps and points


class LoyaltyProgram(Base, TimestampMixin, SoftDeleteMixin):
    """
    Merchant's loyalty program configuration.

    Program belongs to Merchant (chain-wide shared points).
    All stores under a merchant share the same loyalty program.
    Customers can earn and redeem at any store within the merchant.

    Each merchant can have one loyalty program that defines:
    - Program type and mechanics
    - Stamp or points configuration
    - Anti-fraud rules
    - Branding and wallet integration
    """

    __tablename__ = "loyalty_programs"

    id = Column(Integer, primary_key=True, index=True)

    # Merchant association (one program per merchant)
    merchant_id = Column(
        Integer,
        ForeignKey("merchants.id", ondelete="CASCADE"),
        unique=True,
        nullable=False,
        index=True,
        comment="Merchant that owns this program (chain-wide)",
    )

    # Program type
    loyalty_type = Column(
        String(20),
        default=LoyaltyType.POINTS.value,
        nullable=False,
    )

    # =========================================================================
    # Stamps Configuration
    # =========================================================================
    stamps_target = Column(
        Integer,
        default=10,
        nullable=False,
        comment="Number of stamps needed for reward",
    )
    stamps_reward_description = Column(
        String(255),
        default="Free item",
        nullable=False,
        comment="Description of stamp reward",
    )
    stamps_reward_value_cents = Column(
        Integer,
        nullable=True,
        comment="Value of stamp reward in cents (for analytics)",
    )

    # =========================================================================
    # Points Configuration
    # =========================================================================
    points_per_euro = Column(
        Integer,
        default=1,
        nullable=False,
        comment="Points earned per euro spent (1 euro = X points)",
    )
    points_rewards = Column(
        JSON,
        default=list,
        nullable=False,
        comment="List of point rewards: [{id, name, points_required, description}]",
    )

    # Points expiration and bonus settings
    points_expiration_days = Column(
        Integer,
        nullable=True,
        comment="Days of inactivity before points expire (None = never expire)",
    )
    welcome_bonus_points = Column(
        Integer,
        default=0,
        nullable=False,
        comment="Bonus points awarded on enrollment",
    )
    minimum_redemption_points = Column(
        Integer,
        default=100,
        nullable=False,
        comment="Minimum points required for any redemption",
    )
    minimum_purchase_cents = Column(
        Integer,
        default=0,
        nullable=False,
        comment="Minimum purchase amount (cents) to earn points (0 = no minimum)",
    )

    # Future tier configuration (Bronze/Silver/Gold)
    tier_config = Column(
        JSON,
        nullable=True,
        comment='Future: Tier thresholds {"bronze": 0, "silver": 1000, "gold": 5000}',
    )

    # =========================================================================
    # Anti-Fraud Settings
    # =========================================================================
    cooldown_minutes = Column(
        Integer,
        default=15,
        nullable=False,
        comment="Minutes between stamps for same card",
    )
    max_daily_stamps = Column(
        Integer,
        default=5,
        nullable=False,
        comment="Maximum stamps per card per day",
    )
    require_staff_pin = Column(
        Boolean,
        default=True,
        nullable=False,
        comment="Require staff PIN for stamp/points operations",
    )

    # =========================================================================
    # Branding
    # =========================================================================
    card_name = Column(
        String(100),
        nullable=True,
        comment="Display name for loyalty card",
    )
    card_color = Column(
        String(7),
        default="#4F46E5",
        nullable=False,
        comment="Primary color for card (hex)",
    )
    card_secondary_color = Column(
        String(7),
        nullable=True,
        comment="Secondary color for card (hex)",
    )
    logo_url = Column(
        String(500),
        nullable=True,
        comment="URL to merchant logo for card",
    )
    hero_image_url = Column(
        String(500),
        nullable=True,
        comment="URL to hero image for card",
    )

    # =========================================================================
    # Wallet Integration
    # =========================================================================
    google_issuer_id = Column(
        String(100),
        nullable=True,
        comment="Google Wallet Issuer ID",
    )
    google_class_id = Column(
        String(200),
        nullable=True,
        comment="Google Wallet Loyalty Class ID",
    )
    apple_pass_type_id = Column(
        String(100),
        nullable=True,
        comment="Apple Wallet Pass Type ID",
    )

    # =========================================================================
    # Terms and Conditions
    # =========================================================================
    terms_text = Column(
        Text,
        nullable=True,
        comment="Loyalty program terms and conditions (legacy — use terms_cms_page_slug when available)",
    )
    terms_cms_page_slug = Column(
        String(200),
        nullable=True,
        comment="CMS page slug for full T&C content (overrides terms_text when set)",
    )
    privacy_url = Column(
        String(500),
        nullable=True,
        comment="URL to privacy policy",
    )

    # =========================================================================
    # Status
    # =========================================================================
    is_active = Column(
        Boolean,
        default=True,
        nullable=False,
        index=True,
    )
    activated_at = Column(
        DateTime(timezone=True),
        nullable=True,
        comment="When program was first activated",
    )

    # =========================================================================
    # Relationships
    # =========================================================================
    merchant = relationship("Merchant", backref="loyalty_program")
    cards = relationship(
        "LoyaltyCard",
        back_populates="program",
        cascade="all, delete-orphan",
    )
    staff_pins = relationship(
        "StaffPin",
        back_populates="program",
        cascade="all, delete-orphan",
    )

    # Indexes & integrity constraints
    __table_args__ = (
        Index("idx_loyalty_program_merchant_active", "merchant_id", "is_active"),
        CheckConstraint(
            "minimum_purchase_cents >= 0",
            name="ck_loyalty_programs_min_purchase_nonneg",
        ),
        CheckConstraint(
            "points_per_euro >= 0",
            name="ck_loyalty_programs_points_per_euro_nonneg",
        ),
        CheckConstraint(
            "stamps_target >= 1",
            name="ck_loyalty_programs_stamps_target_positive",
        ),
        CheckConstraint(
            "welcome_bonus_points >= 0",
            name="ck_loyalty_programs_welcome_bonus_nonneg",
        ),
    )

    def __repr__(self) -> str:
        return f"<LoyaltyProgram(id={self.id}, merchant_id={self.merchant_id}, type='{self.loyalty_type}')>"

    # =========================================================================
    # Properties
    # =========================================================================

    @property
    def is_stamps_enabled(self) -> bool:
        """Check if stamps are enabled for this program."""
        return self.loyalty_type in (LoyaltyType.STAMPS.value, LoyaltyType.HYBRID.value)

    @property
    def is_points_enabled(self) -> bool:
        """Check if points are enabled for this program."""
        return self.loyalty_type in (LoyaltyType.POINTS.value, LoyaltyType.HYBRID.value)

    @property
    def display_name(self) -> str:
        """Get display name for the program."""
        return self.card_name or "Loyalty Card"

    def get_points_reward(self, reward_id: str) -> dict | None:
        """Get a specific points reward by ID."""
        rewards = self.points_rewards or []
        for reward in rewards:
            if reward.get("id") == reward_id:
                return reward
        return None

    def activate(self) -> None:
        """Activate the loyalty program."""
        self.is_active = True
        if not self.activated_at:
            self.activated_at = datetime.now(UTC)

    def deactivate(self) -> None:
        """Deactivate the loyalty program."""
        self.is_active = False
