"""Payment model — one row per payment attempt (provider-agnostic).

Deliberately separate from :class:`~app.models.subscription.Subscription`: a
payment records *how* premium was purchased (gateway order/payment ids, amount,
status) while the subscription records the *entitlement*. A verified capture links
the two via ``subscription_id``. Storing every attempt (including failures) gives
a complete, auditable payment history without overloading the subscription model.

The model lives in the payments module (not ``app/models``) to keep the payment
integration self-contained; it is imported at app creation so the table registers
with SQLAlchemy metadata for ``create_all`` and migrations.
"""

from __future__ import annotations

from app.extensions import db
from app.models.base import BaseModel, BigIntegerType
from app.models.enums import PaymentStatus
from app.models.mixins import PublicIdMixin, TimestampMixin


def _enum_values(enum_cls):
    """Persist the ENUM member *values* (e.g. ``"captured"``) rather than names."""
    return [member.value for member in enum_cls]


class Payment(BaseModel, PublicIdMixin, TimestampMixin):
    """A single payment attempt for a premium purchase.

    ``provider_order_id`` is unique — one payment row per gateway order — which is
    the anchor for idempotent verification and webhook processing. ``amount`` is
    stored in major currency units (e.g. rupees) to match ``PremiumPlan.price``;
    the gateway's minor-unit amount (paise) is derived when creating the order.
    """

    __tablename__ = "payments"
    __table_args__ = (
        db.Index("ix_payments_user_id", "user_id"),
        db.Index("ix_payments_status", "status"),
        db.Index("ix_payments_provider_payment_id", "provider_payment_id"),
        db.Index("ix_payments_created_at", "created_at"),
    )

    user_id = db.Column(BigIntegerType, db.ForeignKey("users.id"), nullable=False)
    # Set once a captured payment activates a subscription (nullable until then).
    subscription_id = db.Column(
        BigIntegerType, db.ForeignKey("subscriptions.id"), nullable=True
    )
    plan_name = db.Column(db.String(50), nullable=False)

    # Provider abstraction: ``provider`` names the gateway so a second provider
    # can coexist; the ``provider_*`` ids/signature are gateway-specific tokens.
    provider = db.Column(db.String(20), nullable=False, default="razorpay")
    provider_order_id = db.Column(db.String(120), nullable=False, unique=True)
    provider_payment_id = db.Column(db.String(120), nullable=True)
    provider_signature = db.Column(db.String(255), nullable=True)

    amount = db.Column(db.Numeric(10, 2), nullable=False)
    currency = db.Column(db.String(8), nullable=False, default="INR")
    status = db.Column(
        db.Enum(PaymentStatus, values_callable=_enum_values, name="payment_status"),
        nullable=False,
        default=PaymentStatus.CREATED,
    )

    # Free-form JSON metadata (gateway notes, failure reason, etc.) as text.
    notes = db.Column(db.Text, nullable=True)
    # When the payment was captured (settled); null until then.
    paid_at = db.Column(db.DateTime, nullable=True)

    user = db.relationship("User", foreign_keys=[user_id])
    subscription = db.relationship("Subscription", foreign_keys=[subscription_id])

    def __repr__(self) -> str:
        return (
            f"<Payment id={self.id} user_id={self.user_id} "
            f"order={self.provider_order_id!r} status={self.status.value!r}>"
        )
