from sqlalchemy import BigInteger, String, JSON, Integer, Text, UniqueConstraint, Index
from sqlalchemy.dialects.mysql import TINYINT
from sqlalchemy.orm import Mapped, mapped_column

from app.core.models.base import Base, TimestampMixin, TenantMixin

_TABLE_ARGS = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"}


class PosStaff(Base, TenantMixin, TimestampMixin):
    __tablename__ = "pos_staff"
    __table_args__ = (
        UniqueConstraint("tenant_id", "user_id", name="uk_pos_staff_tenant_user"),
        Index("ix_pos_staff_enabled", "tenant_id", "pos_enabled"),
        _TABLE_ARGS,
    )
    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    user_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
    pos_enabled: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=0)
    pin_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
    store_ids: Mapped[list | None] = mapped_column(JSON, nullable=True)
    credential_version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    locked_until: Mapped[str | None] = mapped_column(String(40), nullable=True)
    failed_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=0)


class StorePosRules(Base, TenantMixin, TimestampMixin):
    __tablename__ = "store_pos_rules"
    __table_args__ = (
        UniqueConstraint("tenant_id", "store_id", name="uk_store_pos_rules_tenant_store"),
        _TABLE_ARGS,
    )
    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    store_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
    minimum_age_default: Mapped[int] = mapped_column(Integer, nullable=False, default=18)
    photo_id_required: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    age_second_approval_required: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    returns_online_only: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    return_window_days: Mapped[int] = mapped_column(Integer, nullable=False, default=30)
    receiptless_returns_enabled: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    receiptless_refund_method: Mapped[str] = mapped_column(String(20), nullable=False, default="store_credit")
    all_returns_require_second_approval: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    refund_original_tender: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    price_override_approval_required: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=1)
    # 小票与钱柜：全部默认关闭（opt-in），迁移不改变任何现有门店行为。
    receipt_print_enabled: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=0)
    receipt_auto_print: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=0)
    receipt_print_eftpos_slip: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=0)
    cash_drawer_kick_on_print: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=0)
    receipt_paper_width: Mapped[str] = mapped_column(String(8), nullable=False, default="80mm")
    rules_version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)


class PosReturn(Base, TenantMixin, TimestampMixin):
    """POS 退货的统一业务记录（有小票 / 无小票共用一张表）。

    有小票且需原路退款时关联核心 RefundRequest；无小票退货 source_order_id 与
    refund_request_id 均为空，直接关联钱包流水——因此不需要放宽核心
    refund_requests.order_id 的非空约束。
    """

    __tablename__ = "pos_returns"
    __table_args__ = (
        UniqueConstraint("tenant_id", "idempotency_key", name="uk_pos_returns_idem"),
        Index("ix_pos_returns_order", "tenant_id", "source_order_id"),
        Index("ix_pos_returns_status", "tenant_id", "fund_status"),
        _TABLE_ARGS,
    )
    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    store_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
    lane_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
    idempotency_key: Mapped[str] = mapped_column(String(120), nullable=False)

    source_type: Mapped[str] = mapped_column(String(20), nullable=False)  # receipt | receiptless
    source_order_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
    refund_request_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
    customer_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)

    # 退货明细快照：[{productId, variantId, name, quantity, unitPriceCents, lineTotalCents}]
    items: Mapped[list] = mapped_column(JSON, nullable=False)
    refund_total_cents: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    # 实际拆分结果：[{paymentMethod, amountCents, status, providerTxnRef}]
    refund_splits: Mapped[list | None] = mapped_column(JSON, nullable=True)
    refund_method: Mapped[str] = mapped_column(String(20), nullable=False, default="original")

    # 无小票退货的定价审计
    reference_price_cents: Mapped[int | None] = mapped_column(Integer, nullable=True)
    override_price_cents: Mapped[int | None] = mapped_column(Integer, nullable=True)
    override_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    reason: Mapped[str | None] = mapped_column(Text, nullable=True)
    stock_disposition: Mapped[str] = mapped_column(String(20), nullable=False, default="resellable")
    stock_applied: Mapped[int] = mapped_column(TINYINT(1), nullable=False, default=0)

    operator_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
    approver_user_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
    # pending → completed / failed。资金未成功前不得回补库存或发放 Store Credit。
    fund_status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending")
    rules_version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)


class PosStaffRevision(Base, TenantMixin, TimestampMixin):
    """租户级员工快照修订号。

    原先用 max(credential_version) 当 revision：删掉持有最高版本的员工后 revision 会
    倒退，而 Agent 只在 revision 变大时才替换快照——结果是快照永久卡住，被停用的
    收银员仍能在收银机上登录。这里改用单调递增的计数器。
    """

    __tablename__ = "pos_staff_revision"
    __table_args__ = (
        UniqueConstraint("tenant_id", name="uk_pos_staff_revision_tenant"),
        _TABLE_ARGS,
    )
    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    revision: Mapped[int] = mapped_column(Integer, nullable=False, default=1)


class PosQuickButtonConfig(Base, TenantMixin, TimestampMixin):
    """Per-store quick-button layout, managed in Admin and synced to POS agents."""
    __tablename__ = "pos_quick_button_configs"
    __table_args__ = (
        UniqueConstraint("tenant_id", "store_id", name="uk_pos_qb_tenant_store"),
        _TABLE_ARGS,
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    store_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
    buttons_json: Mapped[list] = mapped_column(JSON, nullable=False)
    buttons_version: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0)
