"""通用行为事件、风险事件与主体限制。

这三张表不是“风控监控页面专用表”：

- ``behavior_event`` 保存服务端权威的关键行为流水，后续可复用于安全审计、
  漏斗核查和客诉排查；
- ``risk_incident`` 保存规则命中后的可处置事件，负责“待处理/忽略/封禁/解除”
  生命周期；
- ``subject_restriction`` 保存当前生效的主体限制，统一承载设备、账号等主体的
  业务拦截状态。
"""
from __future__ import annotations

from datetime import datetime

from sqlalchemy import (
    JSON,
    Boolean,
    DateTime,
    Index,
    Integer,
    String,
    UniqueConstraint,
    func,
    true,
)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column

from app.db.base import Base

_JSON = JSON().with_variant(JSONB(), "postgresql")


class BehaviorEvent(Base):
    """服务端权威行为流水；只增不改。"""

    __tablename__ = "behavior_event"
    __table_args__ = (
        Index("ix_behavior_event_type_time", "event_type", "occurred_at"),
        Index(
            "ix_behavior_event_subject_time",
            "subject_type",
            "subject_id",
            "occurred_at",
        ),
        Index("ix_behavior_event_user_time", "user_id", "occurred_at"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    event_type: Mapped[str] = mapped_column(String(64), nullable=False)
    subject_type: Mapped[str] = mapped_column(String(32), nullable=False)
    subject_id: Mapped[str] = mapped_column(String(128), nullable=False)

    user_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    device_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
    device_model: Mapped[str | None] = mapped_column(String(128), nullable=True)
    phone: Mapped[str | None] = mapped_column(String(20), nullable=True)
    client_ip: Mapped[str | None] = mapped_column(String(64), nullable=True)

    # success / failed / rejected / attempted
    outcome: Mapped[str] = mapped_column(String(24), nullable=False, default="success")
    reason: Mapped[str | None] = mapped_column(String(256), nullable=True)
    details: Mapped[dict | None] = mapped_column(_JSON, nullable=True)

    occurred_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, index=True
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )


class RiskIncident(Base):
    """规则命中后形成的可处置风险事件。"""

    __tablename__ = "risk_incident"
    __table_args__ = (
        UniqueConstraint(
            "rule_code",
            "subject_type",
            "subject_id",
            "window_key",
            name="uq_risk_incident_rule_subject_window",
        ),
        Index("ix_risk_incident_rule_status", "rule_code", "status", "triggered_at"),
        Index(
            "ix_risk_incident_subject",
            "subject_type",
            "subject_id",
            "triggered_at",
        ),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    rule_code: Mapped[str] = mapped_column(String(64), nullable=False)
    event_type: Mapped[str] = mapped_column(String(64), nullable=False)
    subject_type: Mapped[str] = mapped_column(String(32), nullable=False)
    subject_id: Mapped[str] = mapped_column(String(128), nullable=False)
    window_key: Mapped[str] = mapped_column(String(64), nullable=False)

    window_start: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    window_end: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    first_event_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    triggered_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    last_event_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    event_count: Mapped[int] = mapped_column(Integer, nullable=False)

    # open / ignored / blocked / resolved
    status: Mapped[str] = mapped_column(String(24), nullable=False, default="open")
    action_reason: Mapped[str | None] = mapped_column(String(256), nullable=True)
    handled_by: Mapped[int | None] = mapped_column(Integer, nullable=True)
    handled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    details: Mapped[dict | None] = mapped_column(_JSON, nullable=True)

    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class SubjectRestriction(Base):
    """主体当前限制状态；同一主体同一作用域只有一条，可封禁后再解除/重启。"""

    __tablename__ = "subject_restriction"
    __table_args__ = (
        UniqueConstraint(
            "subject_type",
            "subject_id",
            "scope",
            name="uq_subject_restriction_subject_scope",
        ),
        Index(
            "ix_subject_restriction_lookup",
            "subject_type",
            "subject_id",
            "scope",
            "active",
        ),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    subject_type: Mapped[str] = mapped_column(String(32), nullable=False)
    subject_id: Mapped[str] = mapped_column(String(128), nullable=False)
    # auth_device / economic_account / all
    scope: Mapped[str] = mapped_column(String(32), nullable=False)
    active: Mapped[bool] = mapped_column(
        Boolean, nullable=False, default=True, server_default=true()
    )
    reason: Mapped[str | None] = mapped_column(String(256), nullable=True)
    incident_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    created_by: Mapped[int | None] = mapped_column(Integer, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    revoked_by: Mapped[int | None] = mapped_column(Integer, nullable=True)
    revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    details: Mapped[dict | None] = mapped_column(_JSON, nullable=True)
