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

from sqlalchemy import 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 OrderStatus:
    DRAFT = "draft"
    WAITING_PAYMENT = "waiting_payment"
    PAID = "paid"
    PROCESSING = "processing"
    COMPLETED = "completed"
    CANCELLED = "cancelled"
    FAILED = "failed"
    REFUNDED = "refunded"

    ALL = (
        DRAFT,
        WAITING_PAYMENT,
        PAID,
        PROCESSING,
        COMPLETED,
        CANCELLED,
        FAILED,
        REFUNDED,
    )
    OPEN = (DRAFT, WAITING_PAYMENT, PAID, PROCESSING)


class PaymentStatus:
    PENDING = "pending"
    PAID = "paid"
    FAILED = "failed"
    EXPIRED = "expired"
    REJECTED = "rejected"
    WAITING_APPROVAL = "waiting_approval"

    ALL = (PENDING, PAID, FAILED, EXPIRED, REJECTED, WAITING_APPROVAL)


class PaymentMethod:
    WALLET = "wallet"
    TRON = "tron"
    BANK = "bank"

    ALL = (WALLET, TRON, BANK)


class Order(BaseModel):
    __tablename__ = "orders"

    order_number: Mapped[str] = mapped_column(String(24), unique=True, index=True, nullable=False)
    user_id: Mapped[uuid.UUID] = mapped_column(
        GUID, ForeignKey("users.id", ondelete="RESTRICT"), index=True, nullable=False
    )
    product_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID, ForeignKey("products.id", ondelete="SET NULL"), index=True
    )
    coupon_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID, ForeignKey("coupons.id", ondelete="SET NULL")
    )

    status: Mapped[str] = mapped_column(
        String(24), default=OrderStatus.DRAFT, index=True, nullable=False
    )
    payment_status: Mapped[str] = mapped_column(
        String(24), default=PaymentStatus.PENDING, index=True, nullable=False
    )
    payment_method: Mapped[str | None] = mapped_column(String(16))

    quantity: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    price: Mapped[Decimal] = mapped_column(Numeric(18, 2), default=Decimal("0"), nullable=False)
    discount: Mapped[Decimal] = mapped_column(Numeric(18, 2), default=Decimal("0"), nullable=False)
    final_price: Mapped[Decimal] = mapped_column(Numeric(18, 2), default=Decimal("0"), nullable=False)
    currency: Mapped[str] = mapped_column(String(8), default="IRT", nullable=False)

    input_data: Mapped[dict[str, Any]] = mapped_column(JSONColumn, default=dict, nullable=False)
    product_snapshot: Mapped[dict[str, Any]] = mapped_column(JSONColumn, default=dict, nullable=False)
    result_data: Mapped[dict[str, Any]] = mapped_column(JSONColumn, default=dict, nullable=False)
    error_message: Mapped[str | None] = mapped_column(Text)
    completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))

    payments: Mapped[list["Payment"]] = relationship(
        back_populates="order", lazy="selectin", order_by="Payment.created_at"
    )

    @property
    def flow(self) -> str:
        return str(self.product_snapshot.get("flow", ""))

    @property
    def title(self) -> str:
        return str(self.product_snapshot.get("title", ""))


class Payment(BaseModel):
    """One payment attempt / invoice belonging to exactly one order."""

    __tablename__ = "payments"

    order_id: Mapped[uuid.UUID] = mapped_column(
        GUID, ForeignKey("orders.id", ondelete="CASCADE"), index=True, nullable=False
    )
    method: Mapped[str] = mapped_column(String(16), index=True, nullable=False)
    status: Mapped[str] = mapped_column(
        String(24), default=PaymentStatus.PENDING, index=True, nullable=False
    )

    amount: Mapped[Decimal] = mapped_column(Numeric(18, 2), nullable=False)
    currency: Mapped[str] = mapped_column(String(8), default="IRT", nullable=False)

    original_amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 6))
    payable_amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 6), index=True)
    pay_currency: Mapped[str | None] = mapped_column(String(8))

    wallet_address: Mapped[str | None] = mapped_column(String(128))
    tx_hash: Mapped[str | None] = mapped_column(String(128), unique=True)
    receipt_image: Mapped[str | None] = mapped_column(String(255))
    expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
    paid_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    note: Mapped[str | None] = mapped_column(Text)

    order: Mapped["Order"] = relationship(back_populates="payments")
