# app/modules/loyalty/models/terminal_device.py
"""
Terminal device model.

A TerminalDevice represents a paired Android (or other) POS tablet bound
to a single store. Each row owns the unique `jti` that we encode in the
device's long-lived JWT — that's the only handle we keep on the issued
token, since the JWT itself is shown once at pairing time and then never
stored. Two states render a device's token unusable: explicit `revoked_at`
(merchant action, row stays for audit) or `expires_at < now` (1y default).

Multiple devices per store are allowed — a store can have several cashiers
each with their own tablet — so there is no unique constraint across
(store_id), only an index for lookups.
"""

from datetime import UTC, datetime

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

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


class TerminalDevice(Base, TimestampMixin):
    """A paired POS terminal device (typically an Android tablet)."""

    __tablename__ = "loyalty_terminal_devices"

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

    merchant_id = Column(
        Integer,
        ForeignKey("merchants.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    store_id = Column(
        Integer,
        ForeignKey("stores.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    label = Column(
        String(100),
        nullable=False,
        comment="Merchant-facing device name e.g. 'Counter 1'",
    )

    # The only handle we keep on the issued JWT. The token itself is shown
    # once at creation and never stored.
    jti = Column(String(36), nullable=False, unique=True, index=True)

    expires_at = Column(DateTime(timezone=True), nullable=False)
    last_seen_at = Column(DateTime(timezone=True), nullable=True)
    last_seen_ip = Column(String(45), nullable=True)

    revoked_at = Column(DateTime(timezone=True), nullable=True, index=True)
    revoked_by_id = Column(
        Integer,
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )
    revoked_reason = Column(String(255), nullable=True)

    # The user who paired the device. Used as the principal of record when
    # the device performs operations on the store API.
    created_by_id = Column(
        Integer,
        ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )

    merchant = relationship("Merchant", backref="terminal_devices")
    store = relationship("Store", backref="terminal_devices")
    revoked_by = relationship("User", foreign_keys=[revoked_by_id])
    created_by = relationship("User", foreign_keys=[created_by_id])

    __table_args__ = (
        CheckConstraint(
            "expires_at > created_at",
            name="ck_loyalty_terminal_devices_expiry_after_creation",
        ),
        Index(
            "idx_loyalty_terminal_devices_store_active",
            "store_id",
            "revoked_at",
        ),
    )

    def __repr__(self) -> str:
        return (
            f"<TerminalDevice(id={self.id}, label='{self.label}', "
            f"store_id={self.store_id}, revoked={self.revoked_at is not None})>"
        )

    @property
    def is_revoked(self) -> bool:
        return self.revoked_at is not None

    @property
    def is_expired(self) -> bool:
        if not self.expires_at:
            return False
        expiry = self.expires_at
        if expiry.tzinfo is None:
            expiry = expiry.replace(tzinfo=UTC)
        return datetime.now(UTC) >= expiry

    @property
    def status(self) -> str:
        """One of: active, revoked, expired."""
        if self.is_revoked:
            return "revoked"
        if self.is_expired:
            return "expired"
        return "active"
