# app/modules/loyalty/models/loyalty_transaction.py
"""
Loyalty transaction database model.

Records all loyalty events including:
- Stamps earned and redeemed
- Points earned and redeemed
- Associated metadata (staff PIN, purchase amount, IP, etc.)
"""

import enum

from sqlalchemy import (
    Column,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
)
from sqlalchemy.orm import relationship

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


class TransactionType(str, enum.Enum):
    """Type of loyalty transaction."""

    # Stamps
    STAMP_EARNED = "stamp_earned"
    STAMP_REDEEMED = "stamp_redeemed"

    # Points
    POINTS_EARNED = "points_earned"
    POINTS_REDEEMED = "points_redeemed"

    # Adjustments (manual corrections by staff)
    STAMP_ADJUSTMENT = "stamp_adjustment"
    POINTS_ADJUSTMENT = "points_adjustment"

    # Card lifecycle
    CARD_CREATED = "card_created"
    CARD_DEACTIVATED = "card_deactivated"


class LoyaltyTransaction(Base, TimestampMixin):
    """
    Loyalty transaction record.

    Immutable audit log of all loyalty operations for fraud
    detection, analytics, and customer support.
    """

    __tablename__ = "loyalty_transactions"

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

    # Core relationships
    card_id = Column(
        Integer,
        ForeignKey("loyalty_cards.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    vendor_id = Column(
        Integer,
        ForeignKey("vendors.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
        comment="Denormalized for query performance",
    )
    staff_pin_id = Column(
        Integer,
        ForeignKey("staff_pins.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="Staff PIN used for this operation",
    )

    # =========================================================================
    # Transaction Details
    # =========================================================================
    transaction_type = Column(
        String(30),
        nullable=False,
        index=True,
    )

    # Delta values (positive for earn, negative for redeem/adjustment)
    stamps_delta = Column(
        Integer,
        default=0,
        nullable=False,
        comment="Change in stamps (+1 for earn, -N for redeem)",
    )
    points_delta = Column(
        Integer,
        default=0,
        nullable=False,
        comment="Change in points (+N for earn, -N for redeem)",
    )

    # Balance after transaction (for historical reference)
    stamps_balance_after = Column(
        Integer,
        nullable=True,
        comment="Stamp count after this transaction",
    )
    points_balance_after = Column(
        Integer,
        nullable=True,
        comment="Points balance after this transaction",
    )

    # =========================================================================
    # Purchase Context (for points earned)
    # =========================================================================
    purchase_amount_cents = Column(
        Integer,
        nullable=True,
        comment="Purchase amount in cents (for points calculation)",
    )
    order_reference = Column(
        String(100),
        nullable=True,
        index=True,
        comment="Reference to order that triggered points",
    )

    # =========================================================================
    # Reward Context (for redemptions)
    # =========================================================================
    reward_id = Column(
        String(50),
        nullable=True,
        comment="ID of redeemed reward (from program.points_rewards)",
    )
    reward_description = Column(
        String(255),
        nullable=True,
        comment="Description of redeemed reward",
    )

    # =========================================================================
    # Audit Fields
    # =========================================================================
    ip_address = Column(
        String(45),
        nullable=True,
        comment="IP address of requester (IPv4 or IPv6)",
    )
    user_agent = Column(
        String(500),
        nullable=True,
        comment="User agent string",
    )
    notes = Column(
        Text,
        nullable=True,
        comment="Additional notes (e.g., reason for adjustment)",
    )

    # =========================================================================
    # Timestamps
    # =========================================================================
    transaction_at = Column(
        DateTime(timezone=True),
        nullable=False,
        index=True,
        comment="When the transaction occurred (may differ from created_at)",
    )

    # =========================================================================
    # Relationships
    # =========================================================================
    card = relationship("LoyaltyCard", back_populates="transactions")
    vendor = relationship("Vendor", backref="loyalty_transactions")
    staff_pin = relationship("StaffPin", backref="transactions")

    # Indexes
    __table_args__ = (
        Index("idx_loyalty_tx_card_type", "card_id", "transaction_type"),
        Index("idx_loyalty_tx_vendor_date", "vendor_id", "transaction_at"),
        Index("idx_loyalty_tx_type_date", "transaction_type", "transaction_at"),
    )

    def __repr__(self) -> str:
        return (
            f"<LoyaltyTransaction(id={self.id}, type='{self.transaction_type}', "
            f"stamps={self.stamps_delta:+d}, points={self.points_delta:+d})>"
        )

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

    @property
    def is_stamp_transaction(self) -> bool:
        """Check if this is a stamp-related transaction."""
        return self.transaction_type in (
            TransactionType.STAMP_EARNED.value,
            TransactionType.STAMP_REDEEMED.value,
            TransactionType.STAMP_ADJUSTMENT.value,
        )

    @property
    def is_points_transaction(self) -> bool:
        """Check if this is a points-related transaction."""
        return self.transaction_type in (
            TransactionType.POINTS_EARNED.value,
            TransactionType.POINTS_REDEEMED.value,
            TransactionType.POINTS_ADJUSTMENT.value,
        )

    @property
    def is_earn_transaction(self) -> bool:
        """Check if this is an earning transaction (stamp or points)."""
        return self.transaction_type in (
            TransactionType.STAMP_EARNED.value,
            TransactionType.POINTS_EARNED.value,
        )

    @property
    def is_redemption_transaction(self) -> bool:
        """Check if this is a redemption transaction."""
        return self.transaction_type in (
            TransactionType.STAMP_REDEEMED.value,
            TransactionType.POINTS_REDEEMED.value,
        )

    @property
    def staff_name(self) -> str | None:
        """Get the name of the staff member who performed this transaction."""
        if self.staff_pin:
            return self.staff_pin.name
        return None
