"""Human-in-the-loop review workflow for AI-generated exams.

When an exam is created via the AI pipeline and fails the automated quality
gate (QualityChecker + IeltsValidator), it transitions to ``pending_review``
instead of being published directly (see ADR 0004 and P1.1 in the hardening
release). This controller exposes the admin-facing endpoints used by the
review queue UI to inspect, approve, or reject those exams.

Endpoints:

* ``GET  /api/exam/review/queue``        — paginated list of pending exams
* ``GET  /api/exam/review/<exam_id>``    — detail with per-question quality data
* ``POST /api/exam/review/<exam_id>/approve`` — publish (optional notes)
* ``POST /api/exam/review/<exam_id>/reject``  — back to draft (notes required)
"""

import json
import logging

from odoo import fields, http
from odoo.http import request

from odoo.addons.encoach_api.controllers.base import (
    _error_response,
    _get_json_body,
    _json_response,
    _paginate,
    jwt_required,
)

_logger = logging.getLogger(__name__)


def _question_to_review_dict(q):
    """Compact projection of a question for the review UI."""
    try:
        report = json.loads(q.quality_report) if q.quality_report else None
    except (TypeError, ValueError):
        report = {'raw': q.quality_report}
    return {
        'id': q.id,
        'skill': q.skill,
        'question_type': q.question_type,
        'difficulty': q.difficulty,
        'stem': q.stem or '',
        'marks': q.marks or 0.0,
        'ai_generated': bool(q.ai_generated),
        'ielts_certified': bool(q.ielts_certified),
        'format_validated': bool(q.format_validated),
        'ai_model_used': q.ai_model_used or '',
        'ai_prompt_hash': q.ai_prompt_hash or '',
        'quality_score': q.quality_score or 0.0,
        'quality_report': report,
    }


def _review_summary(exam):
    """Aggregate quality stats across every question in the exam."""
    questions = exam.section_ids.mapped('question_ids')
    total = len(questions)
    if total == 0:
        return {
            'question_count': 0,
            'avg_quality_score': 0.0,
            'min_quality_score': 0.0,
            'failing_count': 0,
            'ai_generated_count': 0,
        }
    scores = [q.quality_score or 0.0 for q in questions]
    failing_threshold = 0.7  # configurable later via ir.config_parameter
    return {
        'question_count': total,
        'avg_quality_score': round(sum(scores) / total, 3),
        'min_quality_score': round(min(scores), 3),
        'failing_count': sum(1 for s in scores if s < failing_threshold),
        'ai_generated_count': sum(1 for q in questions if q.ai_generated),
    }


def _exam_to_review_dict(exam, *, include_questions=False):
    data = {
        'id': exam.id,
        'title': exam.title,
        'status': exam.status,
        'subject_id': exam.subject_id.id if exam.subject_id else None,
        'subject_name': exam.subject_id.name if exam.subject_id else '',
        'entity_id': exam.entity_id.id if exam.entity_id else None,
        'teacher_id': exam.teacher_id.id if exam.teacher_id else None,
        'teacher_name': exam.teacher_id.name if exam.teacher_id else '',
        'grading_system': exam.grading_system,
        'total_time_min': exam.total_time_min or 0,
        'total_marks': exam.total_marks or 0.0,
        'reviewed_by_id': exam.reviewed_by_id.id if exam.reviewed_by_id else None,
        'reviewed_by_name': exam.reviewed_by_id.name if exam.reviewed_by_id else '',
        'reviewed_at': (
            fields.Datetime.to_string(exam.reviewed_at) if exam.reviewed_at else None
        ),
        'review_notes': exam.review_notes or '',
        'summary': _review_summary(exam),
    }
    if include_questions:
        data['sections'] = []
        for sec in exam.section_ids:
            data['sections'].append({
                'id': sec.id,
                'title': sec.title,
                'skill': sec.skill or '',
                'sequence': sec.sequence,
                'questions': [
                    _question_to_review_dict(q) for q in sec.question_ids
                ],
            })
    return data


class EncoachExamReviewController(http.Controller):
    """Admin review queue for AI-generated exams gated by the quality checker."""

    # ------------------------------------------------------------------
    # GET /api/exam/review/queue
    # ------------------------------------------------------------------
    @http.route('/api/exam/review/queue', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def queue(self, **kw):
        try:
            Exam = request.env['encoach.exam.custom'].sudo()
            # Default to pending_review but allow callers to inspect history too.
            status = (kw.get('status') or 'pending_review').strip()
            domain = [('status', '=', status)] if status else []

            search = (kw.get('search') or '').strip()
            if search:
                domain.append(('title', 'ilike', search))

            subject_id = kw.get('subject_id')
            if subject_id:
                try:
                    domain.append(('subject_id', '=', int(subject_id)))
                except (TypeError, ValueError):
                    pass

            total = Exam.search_count(domain)
            page, per_page, offset = _paginate(kw)
            exams = Exam.search(
                domain, limit=per_page, offset=offset, order='id desc',
            )
            items = [_exam_to_review_dict(e) for e in exams]
            return _json_response({
                'items': items,
                'data': items,
                'total': total,
                'page': page,
                'size': per_page,
            })
        except Exception as e:
            _logger.exception('exam review queue failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # GET /api/exam/review/<exam_id>
    # ------------------------------------------------------------------
    @http.route('/api/exam/review/<int:exam_id>', type='http', auth='none',
                methods=['GET'], csrf=False)
    @jwt_required
    def detail(self, exam_id, **kw):
        try:
            exam = request.env['encoach.exam.custom'].sudo().browse(exam_id)
            if not exam.exists():
                return _error_response('Exam not found', 404)
            return _json_response(
                _exam_to_review_dict(exam, include_questions=True)
            )
        except Exception as e:
            _logger.exception('exam review detail failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # POST /api/exam/review/<exam_id>/approve
    # ------------------------------------------------------------------
    @http.route('/api/exam/review/<int:exam_id>/approve', type='http',
                auth='none', methods=['POST'], csrf=False)
    @jwt_required
    def approve(self, exam_id, **kw):
        try:
            exam = request.env['encoach.exam.custom'].sudo().browse(exam_id)
            if not exam.exists():
                return _error_response('Exam not found', 404)
            if exam.status != 'pending_review':
                return _error_response(
                    "Only exams in 'pending_review' can be approved "
                    f"(current status: {exam.status})",
                    400,
                )

            body = _get_json_body() or {}
            notes = (body.get('notes') or '').strip() or False
            with request.env.cr.savepoint():
                exam.write({
                    'status': 'published',
                    'reviewed_by_id': request.env.user.id,
                    'reviewed_at': fields.Datetime.now(),
                    'review_notes': notes,
                })
            _logger.info(
                'exam %s approved by user %s', exam.id, request.env.user.id,
            )
            return _json_response(
                _exam_to_review_dict(exam, include_questions=False)
            )
        except Exception as e:
            _logger.exception('exam review approve failed')
            return _error_response(str(e), 500)

    # ------------------------------------------------------------------
    # POST /api/exam/review/<exam_id>/reject
    # ------------------------------------------------------------------
    @http.route('/api/exam/review/<int:exam_id>/reject', type='http',
                auth='none', methods=['POST'], csrf=False)
    @jwt_required
    def reject(self, exam_id, **kw):
        try:
            exam = request.env['encoach.exam.custom'].sudo().browse(exam_id)
            if not exam.exists():
                return _error_response('Exam not found', 404)
            if exam.status != 'pending_review':
                return _error_response(
                    "Only exams in 'pending_review' can be rejected "
                    f"(current status: {exam.status})",
                    400,
                )

            body = _get_json_body() or {}
            notes = (body.get('notes') or '').strip()
            # Require notes on rejection — the author needs actionable feedback.
            if not notes:
                return _error_response(
                    'Review notes are required when rejecting an exam.', 400,
                )

            with request.env.cr.savepoint():
                exam.write({
                    'status': 'draft',
                    'reviewed_by_id': request.env.user.id,
                    'reviewed_at': fields.Datetime.now(),
                    'review_notes': notes,
                })
            _logger.info(
                'exam %s rejected by user %s', exam.id, request.env.user.id,
            )
            return _json_response(
                _exam_to_review_dict(exam, include_questions=False)
            )
        except Exception as e:
            _logger.exception('exam review reject failed')
            return _error_response(str(e), 500)
