"""Quiz CRUD, questions, publish, review router."""
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.api.deps import get_current_user, require_roles
from app.core.rbac import Role
from app.db.base import get_db
from app.models import (
    ModuleModel,
    Question,
    QuestionOption,
    QuizModel,
    QuizStatus,
    QuizVersion,
    UserModel,
)
from app.schemas import (
    QuestionCreate,
    QuestionOut,
    QuestionUpdate,
    QuizCreate,
    QuizOut,
    QuizUpdate,
    QuizVersionOut,
)
from app.services.audit import log_audit

router = APIRouter(prefix="/api/v1/quizzes", tags=["quizzes"])


@router.post("", response_model=QuizOut, status_code=201)
async def create_quiz(
    body: QuizCreate,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuizOut:
    # Verify module exists
    module = await db.get(ModuleModel, body.module_id)
    if not module:
        raise HTTPException(status_code=404, detail="Module not found")

    quiz = QuizModel(
        module_id=body.module_id,
        title=body.title,
        description=body.description,
        created_by=current_user.id,
        requires_credit_head=body.requires_credit_head,
        status=QuizStatus.DRAFT,
    )
    db.add(quiz)
    await log_audit(db, current_user.id, "quiz_created", "quiz", quiz.id,
                    details={"title": body.title})
    await db.commit()
    await db.refresh(quiz)
    return QuizOut.model_validate(quiz)


@router.get("/{quiz_id}", response_model=QuizOut)
async def get_quiz(
    quiz_id: UUID,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(get_current_user),
) -> QuizOut:
    result = await db.execute(
        select(QuizModel)
        .where(QuizModel.id == quiz_id)
        .options(selectinload(QuizModel.questions).selectinload(Question.options))
    )
    quiz = result.scalar_one_or_none()
    if not quiz:
        raise HTTPException(status_code=404, detail="Quiz not found")
    return QuizOut.model_validate(quiz)


@router.put("/{quiz_id}", response_model=QuizOut)
async def update_quiz(
    quiz_id: UUID,
    body: QuizUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuizOut:
    quiz = await db.get(QuizModel, quiz_id)
    if not quiz:
        raise HTTPException(status_code=404, detail="Quiz not found")
    if quiz.status == QuizStatus.PUBLISHED:
        raise HTTPException(status_code=409, detail="Cannot edit published quiz")

    if body.title is not None:
        quiz.title = body.title
    if body.description is not None:
        quiz.description = body.description
    if body.requires_credit_head is not None:
        quiz.requires_credit_head = body.requires_credit_head

    await db.commit()
    await db.refresh(quiz)
    return QuizOut.model_validate(quiz)


@router.post("/{quiz_id}/questions", response_model=QuestionOut, status_code=201)
async def add_question(
    quiz_id: UUID,
    body: QuestionCreate,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuestionOut:
    quiz = await db.get(QuizModel, quiz_id)
    if not quiz:
        raise HTTPException(status_code=404, detail="Quiz not found")
    if quiz.status == QuizStatus.PUBLISHED:
        raise HTTPException(status_code=409, detail="Cannot add questions to published quiz")

    question = Question(
        quiz_id=quiz_id,
        question_text=body.question_text,
        topic_tag=body.topic_tag,
        explanation=body.explanation,
        confidence_score=body.confidence_score,
        is_ai_generated=body.is_ai_generated,
    )
    db.add(question)
    await db.flush()

    # Add 4 options — caller provides via a separate endpoint or inline
    # For now, options must be added via update_question
    await db.commit()
    await db.refresh(question)
    return QuestionOut.model_validate(question)


@router.put("/{quiz_id}/questions/{question_id}", response_model=QuestionOut)
async def update_question(
    quiz_id: UUID,
    question_id: UUID,
    body: QuestionUpdate,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuestionOut:
    result = await db.execute(
        select(Question)
        .where(Question.id == question_id, Question.quiz_id == quiz_id)
        .options(selectinload(Question.options))
    )
    question = result.scalar_one_or_none()
    if not question:
        raise HTTPException(status_code=404, detail="Question not found")

    if body.question_text is not None:
        question.question_text = body.question_text
    if body.topic_tag is not None:
        question.topic_tag = body.topic_tag
    if body.explanation is not None:
        question.explanation = body.explanation
    if body.confidence_score is not None:
        question.confidence_score = body.confidence_score

    # Replace options if provided
    if body.options is not None:
        # Validate exactly 4 options with 1 correct
        if len(body.options) != 4:
            raise HTTPException(status_code=422, detail="Exactly 4 options required")
        correct_count = sum(1 for o in body.options if o.is_correct)
        if correct_count != 1:
            raise HTTPException(status_code=422, detail="Exactly 1 correct answer required")

        # Delete old options
        for old_opt in question.options:
            await db.delete(old_opt)
        await db.flush()

        # Add new options
        for i, opt in enumerate(body.options):
            new_opt = QuestionOption(
                question_id=question_id,
                option_text=opt.option_text,
                is_correct=opt.is_correct,
                order_index=i,
            )
            db.add(new_opt)

    await db.commit()
    await db.refresh(question)
    # Re-load options
    result = await db.execute(
        select(Question)
        .where(Question.id == question_id)
        .options(selectinload(Question.options))
    )
    question = result.scalar_one()
    return QuestionOut.model_validate(question)


@router.post("/{quiz_id}/questions/{question_id}/regenerate", response_model=QuestionOut)
async def regenerate_question(
    quiz_id: UUID,
    question_id: UUID,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuestionOut:
    """Regenerate a single AI question. For MVP, returns the same question (mock)."""
    result = await db.execute(
        select(Question)
        .where(Question.id == question_id, Question.quiz_id == quiz_id)
        .options(selectinload(Question.options))
    )
    question = result.scalar_one_or_none()
    if not question:
        raise HTTPException(status_code=404, detail="Question not found")

    # In production, this calls the AI service to regenerate
    # For MVP, mark as regenerated
    question.confidence_score = 0.85  # mock
    await db.commit()
    await db.refresh(question)
    return QuestionOut.model_validate(question)


@router.post("/{quiz_id}/submit-review", response_model=QuizOut)
async def submit_for_review(
    quiz_id: UUID,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuizOut:
    quiz = await db.get(QuizModel, quiz_id)
    if not quiz:
        raise HTTPException(status_code=404, detail="Quiz not found")
    if quiz.status != QuizStatus.DRAFT:
        raise HTTPException(status_code=409, detail=f"Cannot submit quiz in {quiz.status} state")

    quiz.status = QuizStatus.IN_REVIEW
    await log_audit(db, current_user.id, "quiz_submitted_review", "quiz", quiz_id)
    await db.commit()
    await db.refresh(quiz)
    return QuizOut.model_validate(quiz)


@router.post("/{quiz_id}/publish", response_model=QuizVersionOut, status_code=201)
async def publish_quiz(
    quiz_id: UUID,
    db: AsyncSession = Depends(get_db),
    current_user: UserModel = Depends(
        require_roles(Role.LD_ADMIN, Role.DEPT_HEAD, Role.SALES_MANAGER)
    ),
) -> QuizVersionOut:
    """Publish quiz — creates a versioned snapshot."""
    result = await db.execute(
        select(QuizModel)
        .where(QuizModel.id == quiz_id)
        .options(selectinload(QuizModel.questions).selectinload(Question.options))
    )
    quiz = result.scalar_one_or_none()
    if not quiz:
        raise HTTPException(status_code=404, detail="Quiz not found")
    if quiz.status not in (QuizStatus.IN_REVIEW, QuizStatus.DRAFT):
        raise HTTPException(status_code=409, detail=f"Cannot publish quiz in {quiz.status} state")

    # Validate each question has 4 options and 1 correct
    for q in quiz.questions:
        if len(q.options) != 4:
            raise HTTPException(status_code=422, detail=f"Question {q.id} must have 4 options")
        correct = sum(1 for o in q.options if o.is_correct)
        if correct != 1:
            raise HTTPException(status_code=422, detail=f"Question {q.id} must have exactly 1 correct answer")

    # Build snapshot
    snapshot = {
        "title": quiz.title,
        "description": quiz.description,
        "questions": [
            {
                "id": str(q.id),
                "question_text": q.question_text,
                "topic_tag": q.topic_tag,
                "explanation": q.explanation,
                "options": [
                    {
                        "id": str(o.id),
                        "option_text": o.option_text,
                        "is_correct": o.is_correct,
                        "order_index": o.order_index,
                    }
                    for o in sorted(q.options, key=lambda x: x.order_index)
                ],
            }
            for q in quiz.questions
        ],
    }

    # Get next version number
    version_result = await db.execute(
        select(func.max(QuizVersion.version_number)).where(QuizVersion.quiz_id == quiz_id)
    )
    max_version = version_result.scalar() or 0

    version = QuizVersion(
        quiz_id=quiz_id,
        version_number=max_version + 1,
        snapshot=snapshot,
        published_by=current_user.id,
    )
    db.add(version)
    quiz.status = QuizStatus.PUBLISHED

    await log_audit(db, current_user.id, "quiz_published", "quiz", quiz_id,
                    details={"version": max_version + 1})
    await db.commit()
    await db.refresh(version)
    return QuizVersionOut.model_validate(version)
