"""
Modelos de permisos multi-tenant para el sistema.
Este módulo define el sistema de permisos granular por compañia/tenant.
"""

from datetime import datetime, timezone
from typing import Optional
from sqlalchemy import (
    String,
    Integer,
    ForeignKey,
    DateTime,
    Boolean,
    UniqueConstraint,
    Index,
)
from sqlalchemy.orm import relationship, Mapped, mapped_column
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base


# Modelo para permisos del sistema
# Representa acciones específicas como "invoice.view", "invoice.edit", etc.
class Permission(Base, TimestampMixin):
    __tablename__ = "permissions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    code: Mapped[str] = mapped_column(
        String(100), unique=True, nullable=False, index=True
    )
    # Código único del permiso (ej: "invoice.view", "user.edit")

    description: Mapped[Optional[str]] = mapped_column(String(255))
    # Descripción legible del permiso

    module: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    # Módulo al que pertenece (ej: "invoice", "user", "report")

    action: Mapped[str] = mapped_column(String(50), nullable=False)
    # Acción específica (ej: "view", "edit", "delete", "create")

    is_active: Mapped[bool] = mapped_column(
        Boolean, default=True, server_default="true", nullable=False
    )
    # Permite desactivar permisos sin eliminarlos

    __table_args__ = {"schema": "core", "extend_existing": True}

    # Relaciones
    role_permissions: Mapped[list["RolePermission"]] = relationship(
        "RolePermission", back_populates="permission", cascade="all, delete-orphan"
    )
    user_company_permissions: Mapped[list["UserCompanyPermission"]] = relationship(
        "UserCompanyPermission",
        back_populates="permission",
        cascade="all, delete-orphan",
    )


# Modelo para roles personalizados por compañia/tenant
# Cada compañia puede definir sus propios roles con nombres personalizados
class CompanyRole(Base, TenantScopedMixin, TimestampMixin):
    __tablename__ = "company_roles"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)

    name: Mapped[str] = mapped_column(String(100), nullable=False)
    # Nombre del rol (ej: "Administrador", "Contador", "Vendedor")

    code: Mapped[str] = mapped_column(String(100), nullable=False)
    # Código único del rol dentro del compañia (ej: "admin", "accountant")

    description: Mapped[Optional[str]] = mapped_column(String(255))
    # Descripción del rol

    is_active: Mapped[bool] = mapped_column(
        Boolean, default=True, server_default="true", nullable=False
    )
    # Permite desactivar roles sin eliminarlos

    # Restricción: el código del rol debe ser único por compañia
    __table_args__ = (
        UniqueConstraint(
            "company_id", "tenant_id", "code", name="uq_company_role_code"
        ),
        Index(
            "ix_company_roles_company_id_is_active",
            "company_id",
            "tenant_id",
            "is_active",
        ),
        {"schema": "core", "extend_existing": True},
    )

    # Relaciones
    role_permissions: Mapped[list["RolePermission"]] = relationship(
        "RolePermission", back_populates="company_role", cascade="all, delete-orphan"
    )
    user_company_roles: Mapped[list["UserCompanyRole"]] = relationship(
        "UserCompanyRole",
        back_populates="company_role",
        cascade="all, delete-orphan",
    )


# Tabla de relación entre roles de compañia y permisos
# Define qué permisos tiene cada rol
class RolePermission(Base, TenantScopedMixin, TimestampMixin):
    __tablename__ = "role_permissions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)

    company_role_id: Mapped[int] = mapped_column(
        Integer,
        ForeignKey("core.company_roles.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    permission_id: Mapped[int] = mapped_column(
        Integer,
        ForeignKey("core.permissions.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    # Restricción: un permiso no puede estar duplicado en el mismo rol
    __table_args__ = (
        UniqueConstraint("company_role_id", "permission_id", name="uq_role_permission"),
        Index("ix_role_permissions_composite", "company_role_id", "permission_id"),
        {"schema": "core", "extend_existing": True},
    )

    # Relaciones
    company_role: Mapped["CompanyRole"] = relationship(
        "CompanyRole", back_populates="role_permissions"
    )
    permission: Mapped["Permission"] = relationship(
        "Permission", back_populates="role_permissions"
    )


# Tabla de relación entre usuarios y roles de compañia
# Define qué roles tiene cada usuario en cada compañia
class UserCompanyRole(Base, TenantScopedMixin, TimestampMixin):
    __tablename__ = "user_company_roles"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)

    user_id: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    # ID del usuario (puede ser UUID de Keycloak u otro identificador)

    company_role_id: Mapped[int] = mapped_column(
        Integer,
        ForeignKey("core.company_roles.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    is_active: Mapped[bool] = mapped_column(
        Boolean, default=True, server_default="true", nullable=False
    )
    # Permite desactivar asignaciones sin eliminarlas

    assigned_by: Mapped[Optional[str]] = mapped_column(String(100))
    # ID del usuario que asignó este rol

    # Restricción: un usuario no puede tener el mismo rol duplicado en un compañia
    __table_args__ = (
        UniqueConstraint(
            "user_id",
            "company_id",
            "tenant_id",
            "company_role_id",
            name="uq_user_company_role",
        ),
        Index(
            "ix_user_company_roles_user_company",
            "user_id",
            "company_id",
            "tenant_id",
            "is_active",
        ),
        {"schema": "core", "extend_existing": True},
    )

    # Relaciones
    company_role: Mapped["CompanyRole"] = relationship(
        "CompanyRole", back_populates="user_company_roles"
    )


# Tabla para permisos directos de usuario por compañia (opcional)
# Permite asignar permisos específicos a un usuario sin necesidad de un rol
# Útil para casos excepcionales o permisos temporales
class UserCompanyPermission(Base, TenantScopedMixin, TimestampMixin):
    __tablename__ = "user_company_permissions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)

    user_id: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    # ID del usuario

    permission_id: Mapped[int] = mapped_column(
        Integer,
        ForeignKey("core.permissions.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )

    is_granted: Mapped[bool] = mapped_column(
        Boolean, default=True, server_default="true", nullable=False
    )
    # True = permiso concedido, False = permiso revocado explícitamente
    # Permite revocar permisos que vienen de roles

    is_active: Mapped[bool] = mapped_column(
        Boolean, default=True, server_default="true", nullable=False
    )

    assigned_by: Mapped[Optional[str]] = mapped_column(String(100))
    expires_at: Mapped[Optional[datetime]] = mapped_column(DateTime)
    # Permite permisos temporales con fecha de expiración

    # Restricción: un usuario no puede tener el mismo permiso duplicado en un compañia
    __table_args__ = (
        UniqueConstraint(
            "user_id",
            "company_id",
            "tenant_id",
            "permission_id",
            name="uq_user_company_permission",
        ),
        Index(
            "ix_user_company_permissions_composite",
            "user_id",
            "company_id",
            "tenant_id",
            "is_active",
        ),
        {"schema": "core", "extend_existing": True},
    )

    # Relaciones
    permission: Mapped["Permission"] = relationship(
        "Permission", back_populates="user_company_permissions"
    )
