import enum
import uuid
from datetime import date, datetime, timezone

from sqlalchemy import Date, DateTime, Enum, ForeignKey, Integer, Numeric, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.core.database import Base


class ExamType(str, enum.Enum):
    weekly = "weekly"
    monthly = "monthly"
    final = "final"


class WrongQuestionStatus(str, enum.Enum):
    pending = "pending"
    ocr_done = "ocr_done"
    solved = "solved"
    failed = "failed"


class WrongQuestionCategory(str, enum.Enum):
    regular = "regular"
    olympiad = "olympiad"


class CompositionStatus(str, enum.Enum):
    pending = "pending"
    generating = "generating"
    done = "done"
    failed = "failed"


class CompositionInputMode(str, enum.Enum):
    manual = "manual"
    ocr = "ocr"


class AIProvider(str, enum.Enum):
    ollama = "ollama"
    openai = "openai"


class SchoolLevel(str, enum.Enum):
    junior_high = "junior_high"
    senior_high = "senior_high"


class User(Base):
    __tablename__ = "users"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    username: Mapped[str] = mapped_column(String(64), unique=True, index=True)
    password_hash: Mapped[str] = mapped_column(String(255))
    is_superuser: Mapped[bool] = mapped_column(default=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )

    students: Mapped[list["Student"]] = relationship(back_populates="user", cascade="all, delete-orphan")


class Student(Base):
    __tablename__ = "students"

    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("users.id"), index=True)
    name: Mapped[str] = mapped_column(String(64))
    school_level: Mapped[SchoolLevel] = mapped_column(
        Enum(SchoolLevel), default=SchoolLevel.junior_high
    )
    grade: Mapped[str | None] = mapped_column(String(32), nullable=True)
    class_name: Mapped[str | None] = mapped_column(String(32), nullable=True)
    school_name: Mapped[str | None] = mapped_column(String(128), nullable=True)
    avatar_path: Mapped[str | None] = mapped_column(String(512), nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )

    user: Mapped["User"] = relationship(back_populates="students")
    exam_records: Mapped[list["ExamRecord"]] = relationship(
        back_populates="student", cascade="all, delete-orphan"
    )
    wrong_questions: Mapped[list["WrongQuestion"]] = relationship(
        back_populates="student", cascade="all, delete-orphan"
    )
    compositions: Mapped[list["Composition"]] = relationship(
        back_populates="student", cascade="all, delete-orphan"
    )


class Subject(Base):
    __tablename__ = "subjects"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    name: Mapped[str] = mapped_column(String(32), unique=True)

    scores: Mapped[list["SubjectScore"]] = relationship(back_populates="subject")
    wrong_questions: Mapped[list["WrongQuestion"]] = relationship(back_populates="subject")


class ExamRecord(Base):
    __tablename__ = "exam_records"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    student_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("students.id"), index=True)
    exam_type: Mapped[ExamType] = mapped_column(Enum(ExamType))
    exam_date: Mapped[date] = mapped_column(Date)
    title: Mapped[str | None] = mapped_column(String(128), nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )

    student: Mapped["Student"] = relationship(back_populates="exam_records")
    scores: Mapped[list["SubjectScore"]] = relationship(
        back_populates="exam_record", cascade="all, delete-orphan"
    )


class SubjectScore(Base):
    __tablename__ = "subject_scores"
    __table_args__ = (UniqueConstraint("exam_record_id", "subject_id", name="uq_exam_subject"),)

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    exam_record_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), ForeignKey("exam_records.id"), index=True
    )
    subject_id: Mapped[int] = mapped_column(Integer, ForeignKey("subjects.id"))
    total_score: Mapped[float] = mapped_column(Numeric(8, 2))
    obtained_score: Mapped[float] = mapped_column(Numeric(8, 2))
    ratio: Mapped[float] = mapped_column(Numeric(8, 4))
    review_statuses_json: Mapped[str | None] = mapped_column(Text, nullable=True)

    exam_record: Mapped["ExamRecord"] = relationship(back_populates="scores")
    subject: Mapped["Subject"] = relationship(back_populates="scores")


class WrongQuestion(Base):
    __tablename__ = "wrong_questions"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    student_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("students.id"), index=True)
    subject_id: Mapped[int] = mapped_column(Integer, ForeignKey("subjects.id"))
    image_path: Mapped[str] = mapped_column(String(512))
    ocr_raw_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    question_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    solution_approach: Mapped[str | None] = mapped_column(Text, nullable=True)
    solution_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    mark_regions_json: Mapped[str | None] = mapped_column(Text, nullable=True)
    annotated_image_path: Mapped[str | None] = mapped_column(String(512), nullable=True)
    cropped_image_path: Mapped[str | None] = mapped_column(String(512), nullable=True)
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[WrongQuestionStatus] = mapped_column(
        Enum(WrongQuestionStatus), default=WrongQuestionStatus.pending
    )
    category: Mapped[WrongQuestionCategory] = mapped_column(
        Enum(WrongQuestionCategory), default=WrongQuestionCategory.regular
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )

    student: Mapped["Student"] = relationship(back_populates="wrong_questions")
    subject: Mapped["Subject"] = relationship(back_populates="wrong_questions")


class Composition(Base):
    __tablename__ = "compositions"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    student_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("students.id"), index=True)
    topic: Mapped[str] = mapped_column(Text)
    input_mode: Mapped[CompositionInputMode] = mapped_column(
        Enum(CompositionInputMode), default=CompositionInputMode.manual
    )
    writing_plan: Mapped[str | None] = mapped_column(Text, nullable=True)
    sample_essay: Mapped[str | None] = mapped_column(Text, nullable=True)
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[CompositionStatus] = mapped_column(
        Enum(CompositionStatus), default=CompositionStatus.pending
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )

    student: Mapped["Student"] = relationship(back_populates="compositions")


class SystemSettings(Base):
    __tablename__ = "system_settings"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1)
    registration_enabled: Mapped[bool] = mapped_column(default=True)
    ai_review_enabled: Mapped[bool] = mapped_column(default=True)
    ai_provider: Mapped[str] = mapped_column(String(16), default="ollama")
    ollama_base_url: Mapped[str | None] = mapped_column(String(256), nullable=True)
    ollama_model: Mapped[str | None] = mapped_column(String(128), nullable=True)
    openai_base_url: Mapped[str | None] = mapped_column(String(256), nullable=True)
    openai_model: Mapped[str | None] = mapped_column(String(128), nullable=True)
    openai_api_key: Mapped[str | None] = mapped_column(String(512), nullable=True)
    ocr_service_url: Mapped[str | None] = mapped_column(String(256), nullable=True)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
    )
