from __future__ import annotations

from datetime import datetime

from sqlalchemy import BigInteger, DateTime, ForeignKey, Index, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.core.models.base import Base, TenantMixin, TimestampMixin


class AdminRole(Base, TenantMixin, TimestampMixin):
    __tablename__ = "admin_roles"
    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uk_admin_roles_tenant_code"),
        Index("ix_admin_roles_tenant_active", "tenant_id", "is_active"),
        {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"},
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    code: Mapped[str] = mapped_column(String(64), nullable=False)
    name: Mapped[str] = mapped_column(String(120), nullable=False)
    description: Mapped[str | None] = mapped_column(String(500), nullable=True)
    is_system: Mapped[int] = mapped_column(default=0, nullable=False)
    is_active: Mapped[int] = mapped_column(default=1, nullable=False)

    permissions = relationship("AdminRolePermission", back_populates="role", cascade="all, delete-orphan", lazy="selectin")
    user_roles = relationship("AdminUserRole", back_populates="role", cascade="all, delete-orphan", lazy="noload")


class AdminRolePermission(Base, TenantMixin, TimestampMixin):
    __tablename__ = "admin_role_permissions"
    __table_args__ = (
        UniqueConstraint("tenant_id", "role_id", "permission_key", name="uk_admin_role_permissions_key"),
        Index("ix_admin_role_permissions_tenant_key", "tenant_id", "permission_key"),
        {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"},
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    role_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("admin_roles.id", ondelete="CASCADE"), nullable=False)
    permission_key: Mapped[str] = mapped_column(String(120), nullable=False)
    effect: Mapped[str] = mapped_column(String(16), nullable=False, default="allow")
    data_scope: Mapped[str] = mapped_column(String(32), nullable=False, default="all")

    role = relationship("AdminRole", back_populates="permissions", lazy="noload")


class AdminUserRole(Base, TenantMixin, TimestampMixin):
    __tablename__ = "admin_user_roles"
    __table_args__ = (
        UniqueConstraint("tenant_id", "user_id", "role_id", name="uk_admin_user_roles_user_role"),
        {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"},
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
    role_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("admin_roles.id", ondelete="CASCADE"), nullable=False)

    user = relationship("User", back_populates="admin_roles", lazy="noload")
    role = relationship("AdminRole", back_populates="user_roles", lazy="selectin")


class AdminUserPermissionOverride(Base, TenantMixin, TimestampMixin):
    __tablename__ = "admin_user_permission_overrides"
    __table_args__ = (
        UniqueConstraint("tenant_id", "user_id", "permission_key", name="uk_admin_user_permission_overrides_key"),
        Index("ix_admin_user_permission_overrides_user", "tenant_id", "user_id"),
        {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci"},
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
    permission_key: Mapped[str] = mapped_column(String(120), nullable=False)
    effect: Mapped[str] = mapped_column(String(16), nullable=False)
    data_scope: Mapped[str] = mapped_column(String(32), nullable=False, default="all")
    reason: Mapped[str | None] = mapped_column(Text, nullable=True)
    expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    created_by: Mapped[int | None] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)

    user = relationship("User", foreign_keys=[user_id], back_populates="permission_overrides", lazy="noload")
