import uuid
from datetime import datetime
from decimal import Decimal
from typing import Any

from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, Numeric, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.models.types import GUID, JSONColumn

from app.models.base import BaseModel


class Category(BaseModel):
    __tablename__ = "categories"

    title: Mapped[str] = mapped_column(String(128), nullable=False)
    slug: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False)
    icon: Mapped[str | None] = mapped_column(String(16))
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)

    products: Mapped[list["Product"]] = relationship(back_populates="category")


class Product(BaseModel):
    """Every purchasable item. Behaviour comes from `flow` + `data` (JSONColumn)."""

    __tablename__ = "products"

    category_id: Mapped[uuid.UUID] = mapped_column(
        GUID, ForeignKey("categories.id", ondelete="RESTRICT"), index=True, nullable=False
    )
    title: Mapped[str] = mapped_column(String(160), nullable=False)
    slug: Mapped[str] = mapped_column(String(96), unique=True, index=True, nullable=False)
    description: Mapped[str | None] = mapped_column(Text)
    price: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False)
    discount_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 2))
    currency: Mapped[str] = mapped_column(String(8), default="IRT", nullable=False)
    flow: Mapped[str] = mapped_column(String(32), index=True, nullable=False)
    icon: Mapped[str | None] = mapped_column(String(16))
    image: Mapped[str | None] = mapped_column(String(255))
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
    data: Mapped[dict[str, Any]] = mapped_column(JSONColumn, default=dict, nullable=False)

    category: Mapped["Category"] = relationship(back_populates="products", lazy="selectin")

    @property
    def effective_price(self) -> Decimal:
        if self.discount_price is not None and self.discount_price > 0:
            return self.discount_price
        return self.price


class Coupon(BaseModel):
    __tablename__ = "coupons"

    code: Mapped[str] = mapped_column(String(32), unique=True, index=True, nullable=False)
    type: Mapped[str] = mapped_column(String(16), default="percent", nullable=False)
    value: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False)
    max_discount: Mapped[Decimal | None] = mapped_column(Numeric(18, 2))
    minimum_order: Mapped[Decimal] = mapped_column(Numeric(18, 2), default=Decimal("0"), nullable=False)
    usage_limit: Mapped[int | None] = mapped_column(Integer)
    per_user_limit: Mapped[int | None] = mapped_column(Integer)
    used_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
