import enum
from datetime import datetime

from sqlalchemy import Boolean, DateTime, Enum, ForeignKey, Integer, String, func
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


class UserRole(str, enum.Enum):
    admin = "admin"
    user = "user"


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
    hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
    full_name: Mapped[str] = mapped_column(String(255), default="")
    phone_number: Mapped[str | None] = mapped_column(String(32), nullable=True)
    address: Mapped[str | None] = mapped_column(String(512), nullable=True)
    postal_code: Mapped[str | None] = mapped_column(String(32), nullable=True)
    profile_image: Mapped[str | None] = mapped_column(String(512), nullable=True)
    role: Mapped[UserRole] = mapped_column(Enum(UserRole), nullable=False, default=UserRole.user)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    email_verified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    otp: Mapped[str | None] = mapped_column(String(32), nullable=True)
    google_drive_link: Mapped[str | None] = mapped_column(String(512), nullable=True)
    programme_session_id: Mapped[int | None] = mapped_column(
        ForeignKey("programme_sessions.id", ondelete="SET NULL"),
        nullable=True,
    )
    programme_assessment_id: Mapped[int | None] = mapped_column(
        ForeignKey("programme_assessments.id", ondelete="SET NULL"),
        nullable=True,
    )
    last_active_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    password_resets: Mapped[list["PasswordResetToken"]] = relationship(
        "PasswordResetToken", back_populates="user", cascade="all, delete-orphan"
    )
    admin_profile: Mapped["Admin | None"] = relationship(
        "Admin", back_populates="user", uselist=False, cascade="all, delete-orphan"
    )
    register_step: Mapped["UserRegisterStep | None"] = relationship(
        "UserRegisterStep", back_populates="user", uselist=False, cascade="all, delete-orphan"
    )
    programme_session: Mapped["ProgrammeSession | None"] = relationship(
        "ProgrammeSession", foreign_keys=[programme_session_id]
    )
    programme_assessment: Mapped["ProgrammeAssessment | None"] = relationship(
        "ProgrammeAssessment", foreign_keys=[programme_assessment_id]
    )


class Admin(Base):
    """Admin profile details (separate from auth fields on users)."""

    __tablename__ = "admin"

    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    user_id: Mapped[int] = mapped_column(
        ForeignKey("users.id", ondelete="CASCADE"), unique=True, nullable=False, index=True
    )
    first_name: Mapped[str] = mapped_column(String(128), default="", nullable=False)
    last_name: Mapped[str] = mapped_column(String(128), default="", nullable=False)
    phone_number: Mapped[str | None] = mapped_column(String(32), nullable=True)
    email: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
    profile_image: Mapped[str | None] = mapped_column(String(512), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    user: Mapped["User"] = relationship("User", back_populates="admin_profile")


class PasswordResetToken(Base):
    __tablename__ = "password_reset_tokens"

    id: Mapped[int] = mapped_column(primary_key=True)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
    otp_hash: Mapped[str] = mapped_column(String(255), nullable=False)
    expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    used: Mapped[bool] = mapped_column(Boolean, default=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    user: Mapped["User"] = relationship("User", back_populates="password_resets")


class UserRegisterStep(Base):
    __tablename__ = "user_register_steps"

    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    user_id: Mapped[int] = mapped_column(
        ForeignKey("users.id", ondelete="CASCADE"), unique=True, nullable=False, index=True
    )
    level_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    level_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    module_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    module_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    week_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    week_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    session_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    session_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    current_step: Mapped[str] = mapped_column(String(32), default="level", nullable=False)
    is_completed: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    user: Mapped["User"] = relationship("User", back_populates="register_step")


class UserCompletedAssessment(Base):
    """Assessment milestones marked completed for user programme progress."""

    __tablename__ = "user_completed_assessments"

    user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), primary_key=True)
    assessment_id: Mapped[int] = mapped_column(
        ForeignKey("programme_assessments.id", ondelete="CASCADE"), primary_key=True
    )


class UserCompletedSession(Base):
    """Session milestones when a session has no assessments."""

    __tablename__ = "user_completed_sessions"

    user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), primary_key=True)
    session_id: Mapped[int] = mapped_column(
        ForeignKey("programme_sessions.id", ondelete="CASCADE"), primary_key=True
    )


class UserTrackTrace(Base):
    """One row per track click — assessment/session completed on the Tracks page."""

    __tablename__ = "user_track_traces"

    id: Mapped[int] = mapped_column(primary_key=True, index=True)
    user_id: Mapped[int] = mapped_column(
        ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
    )
    level_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    level_slug: Mapped[str | None] = mapped_column(String(64), nullable=True)
    level_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    module_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    module_kind: Mapped[str | None] = mapped_column(String(64), nullable=True)
    module_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    week_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    week_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    week_index: Mapped[int | None] = mapped_column(Integer, nullable=True)
    session_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
    session_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    session_number: Mapped[int | None] = mapped_column(Integer, nullable=True)
    assessment_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
    assessment_title: Mapped[str | None] = mapped_column(String(255), nullable=True)
    action: Mapped[str] = mapped_column(String(64), nullable=False, default="assessment_completed")
    session_fully_completed: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
