<?php

namespace App\Controllers;

use App\Controllers\Base\ExamResultController as BaseController;

class ExamResultController extends BaseController
{
    /**
     * Get all exam results with pagination, filtered by teacher association
     */
    public function index()
    {
        $user = \Wibiesana\Padi\Core\Auth::user();
        $userId = is_array($user) ? ($user['id'] ?? $user['user_id'] ?? null) : ($user->id ?? $user->user_id ?? null);
        $role = is_array($user) ? ($user['role'] ?? $user['role_id'] ?? null) : ($user->role ?? $user->role_id ?? null);
        $isAdmin = in_array($role, ['admin', 'superadmin'], true) || (int)$role === 1;

        $page = max(1, (int)$this->request->query('page', 1));
        $perPage = min(100, max(1, (int)$this->request->query('per-page', 10)));
        $search = $this->request->query('search');
        $sortBy = $this->request->query('sort_by');
        $order = $this->request->query('order', 'asc');
        $orderBy = $sortBy ? "{$sortBy} " . (strtolower($order) === 'desc' ? 'DESC' : 'ASC') : "exam_result.id DESC";

        $query = \Wibiesana\Padi\Core\Query::find()
            ->select("exam_result.*")
            ->from("exam_result")
            ->leftJoin('exam', 'exam_result.exam_id = exam.id')
            ->leftJoin('student', 'exam_result.student_id = student.id')
            ->leftJoin('subject', 'exam.subject_id = subject.id')
            ->leftJoin('exam_examiners', 'exam.id = exam_examiners.exam_id');

        // Apply filters (Identity/Role)
        if (!$isAdmin) {
            $teacher = \App\Models\Teacher::findQuery()->where(['id', '=', $userId])->one();
            $teacherId = $teacher ? $teacher['id'] : $userId;

            // Teacher can see results for exams they created or assigned to
            $query->andWhere([
                'OR',
                ['exam.created_by' => $userId],
                ['exam_examiners.teacher_id' => $teacherId]
            ]);
        }

        // Apply search keyword
        if (!empty($search)) {
            $keyword = "%{$search}%";
            $query->andWhere([
                'OR',
                ['LIKE', 'exam.name', $keyword],
                ['LIKE', 'student.name', $keyword],
                ['LIKE', 'subject.name', $keyword]
            ]);
        }

        $query->orderBy($orderBy);
        $query->groupBy('exam_result.id'); // Avoid duplicates
        $result = $query->paginate($perPage, $page);

        if (!empty($result['data'])) {
            $this->model->loadRelations($result['data']);
        }

        $paginatedResult = [
            'data' => $result['data'],
            'meta' => [
                'total' => (int)$result['total'],
                'per_page' => $result['per_page'],
                'current_page' => $result['current_page'],
                'last_page' => $result['last_page']
            ]
        ];

        return \App\Resources\ExamResultResource::collection($paginatedResult);
    }

    /**
     * Get all exam results without pagination
     */
    public function all()
    {
        $user = \Wibiesana\Padi\Core\Auth::user();
        $userId = is_array($user) ? ($user['id'] ?? $user['user_id'] ?? null) : ($user->id ?? $user->user_id ?? null);
        $role = is_array($user) ? ($user['role'] ?? $user['role_id'] ?? null) : ($user->role ?? $user->role_id ?? null);
        $isAdmin = in_array($role, ['admin', 'superadmin'], true) || (int)$role === 1;

        $search = $this->request->query('search');
        $sortBy = $this->request->query('sort_by');
        $order = $this->request->query('order', 'asc');
        $orderBy = $sortBy ? "{$sortBy} " . (strtolower($order) === 'desc' ? 'DESC' : 'ASC') : "exam_result.id DESC";

        $query = \Wibiesana\Padi\Core\Query::find()
            ->select("exam_result.*")
            ->from("exam_result")
            ->leftJoin('exam', 'exam_result.exam_id = exam.id')
            ->leftJoin('student', 'exam_result.student_id = student.id')
            ->leftJoin('exam_examiners', 'exam.id = exam_examiners.exam_id');

        if (!$isAdmin) {
            $teacher = \App\Models\Teacher::findQuery()->where(['id', '=', $userId])->one();
            $teacherId = $teacher ? $teacher['id'] : $userId;

            $query->andWhere([
                'OR',
                ['exam.created_by' => $userId],
                ['exam_examiners.teacher_id' => $teacherId]
            ]);
        }

        if (!empty($search)) {
            $keyword = "%{$search}%";
            $query->andWhere([
                'OR',
                ['LIKE', 'exam.name', $keyword],
                ['LIKE', 'student.name', $keyword]
            ]);
        }

        $query->orderBy($orderBy);
        $query->groupBy('exam_result.id');
        $result = $query->limit(100)->all();

        if (!empty($result)) {
            $this->model->loadRelations($result);
        }

        return \App\Resources\ExamResultResource::collection($result);
    }
    /**
     * Get exam results for current logged in student
     */
    public function myResults()
    {
        $userId = \Wibiesana\Padi\Core\Auth::userId();
        if (!$userId) {
            throw new \Exception('Unauthorized', 401);
        }

        // Results with status 4 (waiting grading) or 5 (done)
        $this->model->with(['exam', 'exam.subject']);
        $results = $this->model->findQuery()
            ->where(['student_id' => $userId])
            ->andWhere(['status' => [4, 5]])
            ->orderBy('updated_at DESC')
            ->all();

        if (!empty($results)) {
            $this->model->loadRelations($results);
        }

        return \App\Resources\ExamResultResource::collection($results);
    }

    /**
     * Get summary of exam results for a classroom
     */
    public function teacherClassSummary()
    {
        $classId = $this->request->query('classroom_id');
        if (!$classId) {
            throw new \Exception('Classroom ID is required', 400);
        }

        $user = \Wibiesana\Padi\Core\Auth::user();
        if (!$user) {
            throw new \Exception('Unauthorized', 401);
        }
        $userId = is_array($user) ? ($user['id'] ?? $user['user_id'] ?? null) : ($user->id ?? $user->user_id ?? null);
        $role = is_array($user) ? ($user['role'] ?? $user['role_id'] ?? null) : ($user->role ?? $user->role_id ?? null);
        $isAdmin = in_array($role, ['admin', 'superadmin'], true) || (int)$role === 1;

        // Find associated teacher profile
        $teacher = \App\Models\Teacher::findQuery()->where(['id', '=', $userId])->andWhere(['status', '=', 1])->one();
        $isHomeroom = false;

        $classroom = \App\Models\Classroom::findQuery()->where(['id', '=', $classId])->one();
        if ($classroom && $teacher && $classroom['teacher_id'] == $teacher['id']) {
            $isHomeroom = true;
        }

        // Get all students in this class
        $students = \App\Models\Student::findQuery()
            ->select('student.id, student.name')
            ->from('student')
            ->leftJoin('classroom_member', 'student.id = classroom_member.student_id')
            ->where(['classroom_member.class_id' => $classId])
            ->all();

        // Build Exam Query
        $examBaseQuery = \App\Models\Exam::findQuery()
            ->select('DISTINCT exam.id, exam.name, subject.name as subject_name')
            ->from('exam')
            ->leftJoin('subject', 'exam.subject_id = subject.id');

        // If not Admin or Homeroom, restrict exams
        if (!$isAdmin && !$isHomeroom) {
            // Restrict by Creator or Examiner
            $examBaseQuery->leftJoin('exam_examiners', 'exam.id = exam_examiners.exam_id');

            $orConditions = ['OR'];
            $orConditions[] = ['exam.created_by', '=', $userId];
            if ($teacher) {
                $orConditions[] = ['exam_examiners.teacher_id', '=', $teacher['id']];
            }

            $examBaseQuery->andWhere($orConditions);
        }

        // 1. Get exams explicitly linked to this class
        $examsExplicitQuery = clone $examBaseQuery;
        $examsExplicit = $examsExplicitQuery
            ->innerJoin('exam_class', 'exam.id = exam_class.exam_id')
            ->andWhere(['exam_class.class_id' => $classId])
            ->all();

        // 2. Get exams that have results for students in this class
        $examsWithResultsQuery = clone $examBaseQuery;
        $examsWithResults = $examsWithResultsQuery
            ->innerJoin('exam_result', 'exam.id = exam_result.exam_id')
            ->innerJoin('classroom_member', 'exam_result.student_id = classroom_member.student_id')
            ->andWhere(['classroom_member.class_id' => $classId])
            ->andWhere(['exam_result.status' => [4, 5]])
            ->all();

        // Merge and unique by ID
        $allExams = array_merge($examsExplicit, $examsWithResults);
        $uniqueExams = [];
        $seen = [];
        foreach ($allExams as $e) {
            if (!isset($seen[$e['id']])) {
                $uniqueExams[] = $e;
                $seen[$e['id']] = true;
            }
        }
        $exams = $uniqueExams;

        $examIds = array_column($exams, 'id');
        $studentIds = array_column($students, 'id');

        if (empty($examIds) || empty($studentIds)) {
            return [
                'students' => $students,
                'exams' => $exams,
                'matrix' => []
            ];
        }

        // Get results
        $results = \App\Models\ExamResult::findQuery()
            ->where(['exam_id' => $examIds])
            ->andWhere(['student_id' => $studentIds])
            ->andWhere(['status' => [4, 5]])
            ->orderBy('updated_at ASC')
            ->all();

        $matrix = [];
        foreach ($results as $res) {
            $matrix[$res['student_id']][$res['exam_id']] = (float)$res['total_result'];
        }

        return [
            'students' => $students,
            'exams' => $exams,
            'matrix' => $matrix
        ];
    }

    /**
     * Get exam results for a specific exam, optionally filtered by classroom
     */
    public function getByExam()
    {
        $examId = $this->request->query('exam_id');
        $classId = $this->request->query('classroom_id');

        if (!$examId) {
            throw new \Exception('Exam ID is required', 400);
        }

        $query = \Wibiesana\Padi\Core\Query::find()
            ->select("exam_result.*")
            ->from("exam_result")
            ->where(['exam_result.exam_id' => $examId]);

        if ($classId) {
            $query->leftJoin('student', 'exam_result.student_id = student.id')
                ->leftJoin('classroom_member', 'student.id = classroom_member.student_id')
                ->andWhere(['classroom_member.class_id' => $classId]);
        }

        $results = $query->all();

        if (!empty($results)) {
            $this->model->with(['student'])->loadRelations($results);
        }

        return \App\Resources\ExamResultResource::collection($results);
    }

    /**
     * Get detailed result with questions and answers for grading
     */
    public function getGradeDetail()
    {
        $id = $this->request->param('id');
        $this->model->with(['student', 'exam']);
        $result = $this->model->find($id);

        if (!$result) {
            throw new \Exception('Result not found', 404);
        }

        // Convert to array if it's an object for consistent access
        $resultData = is_array($result) ? $result : $result->toArray();

        // Load exam and question bank
        $exam = \App\Models\Exam::findQuery()->where(['id' => $resultData['exam_id']])->one();
        if (!$exam) throw new \Exception('Exam not found', 404);

        // Get answers from table instead of JSON column for reliability
        $answerModel = new \App\Models\ExamResultAnswer();
        $savedAnswers = $answerModel->findQuery()
            ->where(['exam_result_id' => $resultData['id']])
            ->all();
            
        $answers = [];
        foreach ($savedAnswers as $sa) {
            $answers[] = [
                'q_id' => $sa['question_id'],
                'answer' => $sa['answer']
            ];
        }

        $qIds = [];
        foreach ($answers as $ans) {
            if (isset($ans['q_id'])) {
                $qIds[] = (int)$ans['q_id'];
            } elseif (isset($ans['question_id'])) {
                $qIds[] = (int)$ans['question_id'];
            }
        }
        $qIds = array_unique($qIds);

        $questions = [];
        if (!empty($qIds)) {
            $questions = \App\Models\Question::findQuery()
                ->where(['id' => $qIds])
                ->all();
        }

        // Map questions by ID (handling both array and object)
        $qMap = [];
        foreach ($questions as $q) {
            $qid = is_array($q) ? ($q['id'] ?? null) : ($q->id ?? null);
            if ($qid) $qMap[$qid] = $q;
        }

        // Parse scores
        $scores = json_decode($resultData['answer_score_list'] ?? '[]', true);
        if (!is_array($scores)) $scores = [];

        $scoreMap = [];
        foreach ($scores as $s) {
            if (isset($s['q_id'])) {
                $scoreMap[$s['q_id']] = $s['score'] ?? 0;
            }
        }

        $detailedAnswers = [];
        foreach ($answers as $ans) {
            $qId = $ans['q_id'] ?? $ans['question_id'] ?? null;
            if ($qId && isset($qMap[$qId])) {
                $q = $qMap[$qId];
                $isArr = is_array($q);
                $detailedAnswers[] = [
                    'question_id' => $qId,
                    'question_text' => $isArr ? ($q['question'] ?? '') : ($q->question ?? ''),
                    'type' => $isArr ? ($q['type'] ?? '') : ($q->type ?? ''),
                    'key_answer' => $isArr ? ($q['answer'] ?? '') : ($q->answer ?? ''),
                    'student_answer' => $ans['answer'] ?? '',
                    'max_score' => $isArr ? ($q['answer_score'] ?? 0) : ($q->answer_score ?? 0),
                    'current_score' => $scoreMap[$qId] ?? 0
                ];
            }
        }

        return [
            'result' => \App\Resources\ExamResultResource::make($result),
            'details' => $detailedAnswers,
            'debug' => [
                'answer_count' => count($answers),
                'question_ids_found' => $qIds,
                'questions_fetched_count' => count($questions),
                'qmap_count' => count($qMap),
                'detailed_count' => count($detailedAnswers)
            ]
        ];
    }

    /**
     * Save essay grades and update total score
     */
    public function gradeEssay()
    {
        $id = $this->request->param('id');
        $payload = $this->request->all();
        $grades = $payload['grades'] ?? []; // [{q_id, score}, ...]

        $result = $this->model->find($id);
        if (!$result) throw new \Exception('Result not found', 404);

        $resultData = is_array($result) ? $result : $result->toArray();

        $exam = \App\Models\Exam::findQuery()->where(['id' => $resultData['exam_id']])->one();
        if (!$exam) throw new \Exception('Exam not found', 404);

        // 1. Update answer_score_list
        $currentScores = json_decode($resultData['answer_score_list'] ?? '[]', true);
        if (!is_array($currentScores)) $currentScores = [];

        $scoreMap = [];
        foreach ($currentScores as $s) {
            if (isset($s['q_id'])) {
                $scoreMap[$s['q_id']] = $s['score'] ?? 0;
            }
        }

        foreach ($grades as $g) {
            if (isset($g['q_id'])) {
                $scoreMap[$g['q_id']] = $g['score'] ?? 0;
            }
        }

        $newScoreList = [];
        foreach ($scoreMap as $qId => $score) {
            $newScoreList[] = ['q_id' => (int)$qId, 'score' => (float)$score];
        }

        // 2. Fetch Questions to identify Essay and their Max Scores
        $answerList = json_decode($resultData['answer_list'] ?? '[]', true);
        if (!is_array($answerList)) $answerList = [];
        $qIds = array_column($answerList, 'q_id');
        
        $questions = [];
        if (!empty($qIds)) {
            $questions = \App\Models\Question::findQuery()->where(['id' => $qIds])->all();
        }
        
        $essaySum = 0;
        $essayMax = 0;
        foreach ($questions as $q) {
            $qType = (int)($q['type'] ?? 0);
            // Normalize type check: 2, 0 or 'essay'
            if ($qType === 2 || $qType === 0 || strtolower((string)($q['type'] ?? '')) === 'essay') {
                $essayMax += (float)($q['answer_score'] ?? 0);
                $essaySum += (float)($scoreMap[$q['id']] ?? 0);
            }
        }

        // 3. Normalize Essay Result (0-100) then apply weight
        $essayRaw = 0;
        if ($essayMax > 0) {
            $essayRaw = ($essaySum / $essayMax) * 100;
        }

        // 4. Calculate Weighted Total Result
        $mcWeight = (float)($exam['percentage_mc_value'] ?? 100);
        $essayWeight = (float)($exam['percentage_essay_value'] ?? 0);
        
        $essayWeighted = ($essayRaw * $essayWeight / 100);
        $mcWeighted = (float)$result['mc_result']; // This is already weighted in performFinishCalculation
        
        $totalResult = $mcWeighted + $essayWeighted;

        $this->model->update($id, [
            'answer_score_list' => json_encode($newScoreList),
            'essay_result' => $essayWeighted,
            'total_result' => $totalResult,
            'status' => 5 // Mark as Done/Graded
        ]);

        return ['success' => true, 'total_result' => $totalResult, 'essay_result' => $essayWeighted];
    }
}
