import enum
import uuid
from typing import TYPE_CHECKING, Any

from sqlalchemy import Boolean, Enum, ForeignKey, Integer, String, Text
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from src.models.base import Base, TimestampMixin, UUIDMixin

if TYPE_CHECKING:
    from src.models.category import Category
    from src.models.pattern import Pattern


class Difficulty(str, enum.Enum):
    """Question difficulty levels."""

    EASY = "easy"
    MEDIUM = "medium"
    HARD = "hard"


# Association tables
class QuestionCategory(Base):
    """Junction table for questions and categories."""

    __tablename__ = "question_categories"

    question_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("questions.id", ondelete="CASCADE"),
        primary_key=True,
    )
    category_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("categories.id", ondelete="CASCADE"),
        primary_key=True,
    )


class QuestionPattern(Base):
    """Junction table for questions and patterns."""

    __tablename__ = "question_patterns"

    question_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("questions.id", ondelete="CASCADE"),
        primary_key=True,
    )
    pattern_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("patterns.id", ondelete="CASCADE"),
        primary_key=True,
    )
    is_optimal: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    # Relationships
    question: Mapped["Question"] = relationship(back_populates="question_patterns")
    pattern: Mapped["Pattern"] = relationship()


class Question(Base, UUIDMixin, TimestampMixin):
    """Coding interview question."""

    __tablename__ = "questions"

    title: Mapped[str] = mapped_column(String(255), nullable=False)
    slug: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
    difficulty: Mapped[Difficulty] = mapped_column(
        Enum(
            Difficulty,
            name="difficulty_enum",
            create_type=False,
            values_callable=lambda x: [e.value for e in x],
        ),
        nullable=False,
    )
    description: Mapped[str] = mapped_column(Text, nullable=False)
    constraints: Mapped[str | None] = mapped_column(Text, nullable=True)
    examples: Mapped[dict[str, Any] | None] = mapped_column(JSONB, nullable=True)
    leetcode_id: Mapped[int | None] = mapped_column(Integer, unique=True, nullable=True)
    leetcode_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
    function_signature: Mapped[str | None] = mapped_column(Text, nullable=True)
    test_cases: Mapped[dict[str, Any] | None] = mapped_column(JSONB, nullable=True)

    # Relationships
    explanation: Mapped["Explanation | None"] = relationship(
        back_populates="question",
        uselist=False,
        cascade="all, delete-orphan",
    )
    solutions: Mapped[list["Solution"]] = relationship(
        back_populates="question",
        cascade="all, delete-orphan",
    )
    categories: Mapped[list["Category"]] = relationship(
        secondary="question_categories",
        back_populates="questions",
    )
    patterns: Mapped[list["Pattern"]] = relationship(
        secondary="question_patterns",
        back_populates="questions",
    )
    question_patterns: Mapped[list["QuestionPattern"]] = relationship(
        back_populates="question",
        cascade="all, delete-orphan",
    )


class Explanation(Base, UUIDMixin):
    """Educational explanation for a question."""

    __tablename__ = "explanations"

    question_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("questions.id", ondelete="CASCADE"),
        unique=True,
        nullable=False,
    )
    approach: Mapped[str] = mapped_column(Text, nullable=False)
    intuition: Mapped[str] = mapped_column(Text, nullable=False)
    common_pitfalls: Mapped[list[dict[str, Any]] | None] = mapped_column(JSONB, nullable=True)
    key_takeaways: Mapped[list[str] | None] = mapped_column(JSONB, nullable=True)
    time_complexity: Mapped[str] = mapped_column(Text, nullable=False)
    space_complexity: Mapped[str] = mapped_column(Text, nullable=False)
    complexity_explanation: Mapped[str | None] = mapped_column(Text, nullable=True)
    pattern_comparison: Mapped[str | None] = mapped_column(Text, nullable=True)

    # Relationships
    question: Mapped["Question"] = relationship(back_populates="explanation")


class Solution(Base, UUIDMixin):
    """Code solution for a question."""

    __tablename__ = "solutions"

    question_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("questions.id", ondelete="CASCADE"),
        nullable=False,
    )
    approach_name: Mapped[str] = mapped_column(String(100), nullable=False)
    code: Mapped[str] = mapped_column(Text, nullable=False)
    language: Mapped[str] = mapped_column(String(20), default="python", nullable=False)
    is_optimal: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    explanation: Mapped[str | None] = mapped_column(Text, nullable=True)

    # Relationships
    question: Mapped["Question"] = relationship(back_populates="solutions")
