import uuid from datetime import datetime, timezone from typing import Optional, TYPE_CHECKING from sqlalchemy import String, DateTime, ForeignKey, Boolean from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.dialects.postgresql import UUID from app.core.database import Base if TYPE_CHECKING: from app.models.team import Team from app.models.tree import Tree from app.models.session import Session from app.models.folder import UserFolder class User(Base): __tablename__ = "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) password_hash: Mapped[str] = mapped_column(String(255), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) role: Mapped[str] = mapped_column(String(50), nullable=False, default="engineer") is_team_admin: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) team_id: Mapped[Optional[uuid.UUID]] = mapped_column( UUID(as_uuid=True), ForeignKey("teams.id"), nullable=True ) invite_code_id: Mapped[Optional[uuid.UUID]] = mapped_column( UUID(as_uuid=True), ForeignKey("invite_codes.id"), nullable=True ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) ) last_login: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True) # Relationships team: Mapped[Optional["Team"]] = relationship("Team", back_populates="users") trees: Mapped[list["Tree"]] = relationship("Tree", back_populates="author") sessions: Mapped[list["Session"]] = relationship("Session", back_populates="user") folders: Mapped[list["UserFolder"]] = relationship("UserFolder", back_populates="user") @property def is_admin(self) -> bool: """Returns True if user is a global (ResolutionFlow) admin.""" return self.role == "admin" @property def can_manage_team(self) -> bool: """Returns True if user can manage their team (team admin or global admin).""" return self.is_admin or (self.is_team_admin and self.team_id is not None)