"""
Portal user model for admin dashboard authentication.

Auth.js v5 validates credentials against this model via a FastAPI endpoint.
Passwords are stored as bcrypt hashes — never plaintext.
"""

from __future__ import annotations

import enum
import uuid
from datetime import datetime

from sqlalchemy import DateTime, ForeignKey, String, UniqueConstraint, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column

from shared.models.tenant import Base


class UserRole(str, enum.Enum):
    """
    Platform-level role enum for portal_users and user_tenant_roles.

    PLATFORM_ADMIN: Full access to all tenants and platform settings.
    CUSTOMER_ADMIN: Admin within their own tenant(s); manages agents, keys, billing.
    CUSTOMER_OPERATOR: Read-only + send-message access within their tenant(s).
    """

    PLATFORM_ADMIN = "platform_admin"
    CUSTOMER_ADMIN = "customer_admin"
    CUSTOMER_OPERATOR = "customer_operator"


class PortalUser(Base):
    """
    An operator with access to the Konstruct admin portal.

    RLS is NOT applied to this table — users are authenticated before
    tenant context is established. Authorization is handled at the
    application layer (role field + JWT claims).
    """

    __tablename__ = "portal_users"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        default=uuid.uuid4,
    )
    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
    hashed_password: Mapped[str] = mapped_column(
        String(255),
        nullable=False,
        comment="bcrypt hash — never store plaintext",
    )
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    role: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        default="customer_admin",
        comment="platform_admin | customer_admin | customer_operator",
    )
    language: Mapped[str] = mapped_column(
        String(10),
        nullable=False,
        server_default="en",
        comment="UI and email language preference: en | es | pt",
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
        onupdate=func.now(),
    )

    def __repr__(self) -> str:
        return f"<PortalUser id={self.id} email={self.email!r} role={self.role!r}>"


class UserTenantRole(Base):
    """
    Maps a portal user to a tenant with a specific role.

    A user can have at most one role per tenant (UNIQUE constraint).
    platform_admin users typically have no rows here — they bypass
    tenant membership checks entirely.
    """

    __tablename__ = "user_tenant_roles"
    __table_args__ = (
        UniqueConstraint("user_id", "tenant_id", name="uq_user_tenant_role"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        default=uuid.uuid4,
    )
    user_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("portal_users.id", ondelete="CASCADE"),
        nullable=False,
    )
    tenant_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("tenants.id", ondelete="CASCADE"),
        nullable=False,
    )
    role: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        comment="customer_admin | customer_operator",
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )

    def __repr__(self) -> str:
        return f"<UserTenantRole user={self.user_id} tenant={self.tenant_id} role={self.role!r}>"


class PortalInvitation(Base):
    """
    Invitation record for invite-only onboarding flow.

    token_hash stores SHA-256(raw_token) for secure lookup without exposing
    the raw token in the DB. The raw token is returned to the inviter in the
    API response and sent via email.
    """

    __tablename__ = "portal_invitations"

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        default=uuid.uuid4,
    )
    email: Mapped[str] = mapped_column(String(255), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    tenant_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("tenants.id", ondelete="CASCADE"),
        nullable=False,
    )
    role: Mapped[str] = mapped_column(
        String(50),
        nullable=False,
        comment="customer_admin | customer_operator",
    )
    invited_by: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("portal_users.id"),
        nullable=True,
        comment="ID of the user who created the invitation",
    )
    token_hash: Mapped[str] = mapped_column(
        String(255),
        nullable=False,
        unique=True,
        comment="SHA-256 hex digest of the raw HMAC invite token",
    )
    status: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default="pending",
        comment="pending | accepted | revoked",
    )
    expires_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=func.now(),
    )

    def __repr__(self) -> str:
        return (
            f"<PortalInvitation id={self.id} email={self.email!r} "
            f"status={self.status!r} tenant={self.tenant_id}>"
        )
