import uuid
from datetime import datetime, timezone
from decimal import Decimal

from sqlalchemy import Boolean, CheckConstraint, DateTime, ForeignKey, Integer, Numeric, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column

from app.db.base import Base


class LoyaltySettings(Base):
    __tablename__ = "loyalty_settings"
    __table_args__ = (
        UniqueConstraint("business_id", name="uq_loyalty_settings_business"),
        CheckConstraint("earn_amount > 0", name="ck_loyalty_earn_amount_positive"),
        CheckConstraint("earn_points > 0", name="ck_loyalty_earn_points_positive"),
        CheckConstraint("redemption_points >= 0", name="ck_loyalty_redemption_points_nonnegative"),
        CheckConstraint("redemption_value IS NULL OR redemption_value >= 0", name="ck_loyalty_redemption_value_nonnegative"),
    )
    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    business_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("businesses.id", ondelete="RESTRICT"), nullable=False, index=True)
    enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    earn_amount: Mapped[Decimal] = mapped_column(Numeric(14, 2), default=Decimal("1000"), nullable=False)
    earn_points: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    redemption_points: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    redemption_value: Mapped[Decimal | None] = mapped_column(Numeric(14, 2), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False)


class LoyaltyTransaction(Base):
    __tablename__ = "loyalty_transactions"
    __table_args__ = (
        UniqueConstraint("sale_id", name="uq_loyalty_transaction_sale_earn"),
        CheckConstraint("transaction_type IN ('EARN', 'REDEEM', 'ADJUSTMENT')", name="ck_loyalty_transaction_type"),
        CheckConstraint("(transaction_type = 'EARN' AND points > 0) OR (transaction_type = 'REDEEM' AND points < 0) OR transaction_type = 'ADJUSTMENT'", name="ck_loyalty_transaction_sign"),
    )
    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    business_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("businesses.id", ondelete="RESTRICT"), nullable=False, index=True)
    customer_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("customers.id", ondelete="RESTRICT"), nullable=False, index=True)
    sale_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("sales.id", ondelete="RESTRICT"), nullable=True, index=True)
    transaction_type: Mapped[str] = mapped_column(String(20), nullable=False)
    points: Mapped[int] = mapped_column(Integer, nullable=False)
    reference: Mapped[str | None] = mapped_column(String(150), nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_by: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="RESTRICT"), nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
