import uuid
from datetime import datetime
from sqlalchemy import (
    Column, String, Integer, Boolean, DateTime,
    Text, Numeric, ForeignKey, CHAR, TypeDecorator,
)
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.orm import relationship
from .database import Base


class GUID(TypeDecorator):
    """Platform-portable UUID type.
    Uses PostgreSQL UUID natively, otherwise stores as CHAR(36) string."""
    impl = CHAR
    cache_ok = True

    def load_dialect_impl(self, dialect):
        if dialect.name == "postgresql":
            return dialect.type_descriptor(PG_UUID(as_uuid=True))
        return dialect.type_descriptor(CHAR(36))

    def process_bind_param(self, value, dialect):
        if value is None:
            return value
        if dialect.name == "postgresql":
            return value
        if isinstance(value, uuid.UUID):
            return str(value)
        return str(uuid.UUID(value))

    def process_result_value(self, value, dialect):
        if value is None:
            return value
        if isinstance(value, uuid.UUID):
            return value
        return uuid.UUID(value)


class User(Base):
    __tablename__ = "users"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    email = Column(String(255), unique=True, nullable=False, index=True)
    password_hash = Column(String(255), nullable=False)
    first_name = Column(String(100), nullable=False)
    last_name = Column(String(100), nullable=False)
    grade = Column(Integer, nullable=True)          # 8 = EN, 12 = Bac
    school = Column(String(255), nullable=True)
    city = Column(String(100), nullable=True)
    county = Column(String(100), nullable=True)
    is_active = Column(Boolean, default=True, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    sessions = relationship("ExamSession", back_populates="user", lazy="dynamic")


class Administrator(Base):
    __tablename__ = "administrators"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    email = Column(String(255), unique=True, nullable=False, index=True)
    password_hash = Column(String(255), nullable=False)
    first_name = Column(String(100), nullable=False)
    last_name = Column(String(100), nullable=False)
    role = Column(String(50), default="admin", nullable=False)   # admin | superadmin
    is_active = Column(Boolean, default=True, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)

    exams = relationship("Exam", back_populates="creator")


class ExamType(Base):
    __tablename__ = "exam_types"

    id = Column(Integer, primary_key=True, autoincrement=True)
    code = Column(String(50), unique=True, nullable=False)       # EN | BAC
    name = Column(String(255), nullable=False)
    description = Column(Text, nullable=True)
    grade_level = Column(Integer, nullable=True)                 # 8 | 12

    subjects = relationship("Subject", back_populates="exam_type")
    exams = relationship("Exam", back_populates="exam_type")


class Subject(Base):
    __tablename__ = "subjects"

    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(255), nullable=False)
    code = Column(String(50), unique=True, nullable=False)
    exam_type_id = Column(Integer, ForeignKey("exam_types.id"), nullable=False)
    is_mandatory = Column(Boolean, default=True)
    profile = Column(String(100), nullable=True)                 # real | uman | tehnic | NULL

    exam_type = relationship("ExamType", back_populates="subjects")
    exams = relationship("Exam", back_populates="subject")


class Exam(Base):
    __tablename__ = "exams"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    title = Column(String(255), nullable=False)
    subject_id = Column(Integer, ForeignKey("subjects.id"), nullable=False)
    exam_type_id = Column(Integer, ForeignKey("exam_types.id"), nullable=False)
    year = Column(Integer, nullable=True)
    duration_minutes = Column(Integer, default=120, nullable=False)
    total_points = Column(Integer, default=100, nullable=False)
    instructions = Column(Text, nullable=True)
    is_published = Column(Boolean, default=False, nullable=False)
    created_by = Column(GUID(), ForeignKey("administrators.id"), nullable=True)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    subject = relationship("Subject", back_populates="exams")
    exam_type = relationship("ExamType", back_populates="exams")
    creator = relationship("Administrator", back_populates="exams")
    questions = relationship(
        "Question", back_populates="exam",
        cascade="all, delete-orphan",
        order_by="Question.order_index",
    )
    sessions = relationship("ExamSession", back_populates="exam", lazy="dynamic")


class Question(Base):
    __tablename__ = "questions"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    exam_id = Column(GUID(), ForeignKey("exams.id", ondelete="CASCADE"), nullable=False)
    question_number = Column(Integer, nullable=False)
    # multiple_choice | true_false | open_ended
    question_type = Column(String(50), nullable=False, default="multiple_choice")
    question_text = Column(Text, nullable=False)
    points = Column(Numeric(5, 2), default=10, nullable=False)
    order_index = Column(Integer, nullable=False)
    image_url = Column(String(500), nullable=True)
    created_at = Column(DateTime, default=datetime.utcnow)

    exam = relationship("Exam", back_populates="questions")
    options = relationship(
        "QuestionOption", back_populates="question",
        cascade="all, delete-orphan",
        order_by="QuestionOption.option_label",
    )
    answers = relationship("Answer", back_populates="question")


class QuestionOption(Base):
    __tablename__ = "question_options"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    question_id = Column(GUID(), ForeignKey("questions.id", ondelete="CASCADE"), nullable=False)
    option_label = Column(String(10), nullable=False)    # A, B, C, D
    option_text = Column(Text, nullable=False)
    is_correct = Column(Boolean, default=False, nullable=False)

    question = relationship("Question", back_populates="options")


class ExamSession(Base):
    __tablename__ = "exam_sessions"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
    exam_id = Column(GUID(), ForeignKey("exams.id"), nullable=False)
    started_at = Column(DateTime, default=datetime.utcnow, nullable=False)
    submitted_at = Column(DateTime, nullable=True)
    time_spent_seconds = Column(Integer, nullable=True)
    # in_progress | submitted
    status = Column(String(50), default="in_progress", nullable=False)
    total_score = Column(Numeric(5, 2), nullable=True)
    total_points = Column(Integer, nullable=True)
    grade = Column(Numeric(4, 2), nullable=True)          # Romanian scale 1-10
    passed = Column(Boolean, nullable=True)

    user = relationship("User", back_populates="sessions")
    exam = relationship("Exam", back_populates="sessions")
    answers = relationship("Answer", back_populates="session", cascade="all, delete-orphan")


class Answer(Base):
    __tablename__ = "answers"

    id = Column(GUID(), primary_key=True, default=uuid.uuid4)
    session_id = Column(GUID(), ForeignKey("exam_sessions.id", ondelete="CASCADE"), nullable=False)
    question_id = Column(GUID(), ForeignKey("questions.id"), nullable=False)
    answer_text = Column(Text, nullable=True)
    selected_option_id = Column(GUID(), ForeignKey("question_options.id"), nullable=True)
    is_correct = Column(Boolean, nullable=True)
    points_earned = Column(Numeric(5, 2), default=0, nullable=False)
    answered_at = Column(DateTime, nullable=True)

    session = relationship("ExamSession", back_populates="answers")
    question = relationship("Question", back_populates="answers")
    selected_option = relationship("QuestionOption")
