# #region AuthModels [C:5] [TYPE Module] [SEMANTICS sqlalchemy, auth, model, schema, user]
# @BRIEF SQLAlchemy models for multi-user authentication and authorization.
# @LAYER Domain
# @RELATION INHERITS -> [EXT:Library:SQLAlchemy.Base]
#
# @INVARIANT  Usernames and emails must be unique.
# @PRE  Database engine initialized
# @POST  Auth ORM models registered with unique constraints
# @SIDE_EFFECT  Defines auth user tables
# @DATA_CONTRACT  UserData -> UserRecord

from datetime import UTC, datetime
import uuid

from sqlalchemy import Boolean, Column, DateTime, ForeignKey, String, Table
from sqlalchemy.orm import relationship

from .mapping import Base


# #region generate_uuid [TYPE Function]
# @BRIEF Generates a unique UUID string.
# @POST     Returns a string representation of a new UUID.
# @RELATION DEPENDS_ON -> [EXT:Python:uuid]
def generate_uuid():
    return str(uuid.uuid4())


# #endregion generate_uuid

# #region user_roles [TYPE Table]
# @BRIEF Association table for many-to-many relationship between Users and Roles.
# @RELATION DEPENDS_ON -> [EXT:Library:SQLAlchemy.Base]
# @RELATION DEPENDS_ON -> [User]
# @RELATION DEPENDS_ON -> [Role]
user_roles = Table(
    "user_roles",
    Base.metadata,
    Column("user_id", String, ForeignKey("users.id"), primary_key=True),
    Column("role_id", String, ForeignKey("roles.id"), primary_key=True),
)
# #endregion user_roles

# #region role_permissions [TYPE Table]
# @BRIEF Association table for many-to-many relationship between Roles and Permissions.
# @RELATION DEPENDS_ON -> [EXT:Library:SQLAlchemy.Base]
# @RELATION DEPENDS_ON -> [Role]
# @RELATION DEPENDS_ON -> [Permission]
role_permissions = Table(
    "role_permissions",
    Base.metadata,
    Column("role_id", String, ForeignKey("roles.id"), primary_key=True),
    Column("permission_id", String, ForeignKey("permissions.id"), primary_key=True),
)
# #endregion role_permissions


# #region User [TYPE Class]
# @BRIEF Represents an identity that can authenticate to the system.
# @RELATION BINDS_TO -> [Role]
class User(Base):
    __tablename__ = "users"

    id = Column(String, primary_key=True, default=generate_uuid)
    username = Column(String, unique=True, index=True, nullable=False)
    email = Column(String, unique=True, index=True, nullable=True)
    password_hash = Column(String, nullable=True)
    full_name = Column(String, nullable=True)
    auth_source = Column(String, default="LOCAL")  # LOCAL or ADFS
    is_active = Column(Boolean, default=True)
    is_ad_user = Column(Boolean, default=False)
    created_at = Column(DateTime, default=lambda: datetime.now(UTC))
    last_login = Column(DateTime, nullable=True)

    roles = relationship("Role", secondary=user_roles, back_populates="users")


# #endregion User


# #region Role [TYPE Class]
# @BRIEF Represents a collection of permissions.
# @RELATION BINDS_TO -> [User]
# @RELATION BINDS_TO -> [Permission]
class Role(Base):
    __tablename__ = "roles"

    id = Column(String, primary_key=True, default=generate_uuid)
    name = Column(String, unique=True, index=True, nullable=False)
    description = Column(String, nullable=True)
    is_admin = Column(Boolean, default=False, nullable=False)

    users = relationship("User", secondary=user_roles, back_populates="roles")
    permissions = relationship(
        "Permission", secondary=role_permissions, back_populates="roles"
    )


# #endregion Role


# #region Permission [TYPE Class]
# @BRIEF Represents a specific capability within the system.
# @RELATION BINDS_TO -> [Role]
class Permission(Base):
    __tablename__ = "permissions"

    id = Column(String, primary_key=True, default=generate_uuid)
    resource = Column(String, nullable=False)  # e.g. "plugin:backup"
    action = Column(String, nullable=False)  # e.g. "READ", "EXECUTE", "WRITE"

    roles = relationship(
        "Role", secondary=role_permissions, back_populates="permissions"
    )


# #endregion Permission


# #region TokenBlacklist [TYPE Class]
# @BRIEF Stores SHA-256 hashes of revoked JWTs for server-side token blacklisting.
# @INVARIANT  Only the SHA-256 hash of the token is stored — never the raw token.
#             Expired entries are cleaned up by _prune_blacklist().
# @RELATION DEPENDS_ON -> [EXT:Library:SQLAlchemy.Base]
class TokenBlacklist(Base):
    __tablename__ = "token_blacklist"

    jti = Column(String, primary_key=True)  # JWT ID from the token
    token_hash = Column(String(64), nullable=False, index=True)  # SHA-256 of token
    expires_at = Column(DateTime, nullable=False)  # Token's original exp claim
    revoked_at = Column(DateTime, nullable=False, default=lambda: datetime.now(UTC))


# #endregion TokenBlacklist

# #region ADGroupMapping [TYPE Class]
# @BRIEF Maps an Active Directory group to a local System Role.
# @RELATION DEPENDS_ON -> [Role]
class ADGroupMapping(Base):
    __tablename__ = "ad_group_mappings"

    id = Column(String, primary_key=True, default=generate_uuid)
    ad_group = Column(String, unique=True, index=True, nullable=False)
    role_id = Column(String, ForeignKey("roles.id"), nullable=False)

    role = relationship("Role")


# #endregion ADGroupMapping

# #endregion AuthModels
