<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class AssessmentService
{
    private FileStore $questions;
    private FileStore $attempts;
    private CourseService $courses;
    private EnrollmentService $enrollments;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->questions = new FileStore(storage_path('data/quiz_questions.json'));
        $this->attempts = new FileStore(storage_path('data/quiz_attempts.json'));
        $this->courses = new CourseService();
        $this->enrollments = new EnrollmentService();
        $this->logs = new ActivityLogService();
    }

    public function questionsForCourse(int $courseId): array
    {
        $items = array_values(array_filter(
            $this->questions->all(),
            fn (array $question): bool => (int) ($question['course_id'] ?? 0) === $courseId
        ));

        usort($items, fn (array $a, array $b): int => (int) ($a['position'] ?? 0) <=> (int) ($b['position'] ?? 0));

        return $items;
    }

    public function addQuestion(int $courseId, array $data, array $user): array
    {
        $course = $this->courses->find($courseId);

        if (!$course || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Curso nao encontrado ou sem permissao.'];
        }

        $question = trim((string) ($data['question'] ?? ''));
        $type = (string) ($data['type'] ?? 'multiple_choice');
        $options = $this->parseOptions((string) ($data['options'] ?? ''), $type);
        $correct = trim((string) ($data['correct_answer'] ?? ''));
        $points = max(1, (int) ($data['points'] ?? 1));

        if ($question === '') {
            return ['ok' => false, 'error' => 'Escreva a pergunta.'];
        }

        if (!in_array($type, ['multiple_choice', 'true_false'], true)) {
            return ['ok' => false, 'error' => 'Tipo de pergunta invalido.'];
        }

        if ($type === 'multiple_choice' && count($options) < 2) {
            return ['ok' => false, 'error' => 'Perguntas de escolha multipla precisam de pelo menos 2 opcoes.'];
        }

        if (!in_array($correct, $options, true)) {
            return ['ok' => false, 'error' => 'A resposta correcta deve existir nas opcoes.'];
        }

        $created = $this->questions->insert([
            'course_id' => $courseId,
            'question' => $question,
            'type' => $type,
            'options' => $options,
            'correct_answer' => $correct,
            'points' => $points,
            'position' => count($this->questionsForCourse($courseId)) + 1,
            'status' => 'active',
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('quiz_question_created', "Pergunta adicionada ao curso {$course['title']}.", (int) $user['id']);

        return ['ok' => true, 'question' => $created];
    }

    public function deleteQuestion(int $courseId, int $questionId, array $user): array
    {
        $course = $this->courses->find($courseId);

        if (!$course || !$this->courses->canManage($course, $user)) {
            return ['ok' => false, 'error' => 'Curso nao encontrado ou sem permissao.'];
        }

        $this->questions->save(array_filter(
            $this->questions->all(),
            fn (array $question): bool => (int) ($question['id'] ?? 0) !== $questionId || (int) ($question['course_id'] ?? 0) !== $courseId
        ));

        return ['ok' => true];
    }

    public function submitAttempt(int $courseId, int $enrollmentId, array $student, array $answers): array
    {
        $course = $this->courses->find($courseId);
        $enrollment = $this->enrollments->enrollmentById($enrollmentId);

        if (!$course || !$enrollment || (int) $enrollment['course_id'] !== $courseId || (int) $enrollment['student_id'] !== (int) $student['id']) {
            return ['ok' => false, 'error' => 'Teste indisponivel.'];
        }

        $questions = $this->questionsForCourse($courseId);

        if (!$questions) {
            return ['ok' => false, 'error' => 'Este curso ainda nao tem teste configurado.'];
        }

        $total = 0;
        $earned = 0;
        $review = [];

        foreach ($questions as $question) {
            $id = (int) $question['id'];
            $answer = trim((string) ($answers[$id] ?? ''));
            $points = (int) ($question['points'] ?? 1);
            $correct = hash_equals((string) $question['correct_answer'], $answer);
            $total += $points;
            $earned += $correct ? $points : 0;
            $review[] = [
                'question_id' => $id,
                'question' => $question['question'],
                'answer' => $answer,
                'correct_answer' => $question['correct_answer'],
                'is_correct' => $correct,
                'points' => $correct ? $points : 0,
                'max_points' => $points,
            ];
        }

        $score = $total > 0 ? (int) round(($earned / $total) * 100) : 0;
        $passScore = max(1, (int) ($course['final_quiz_pass_score'] ?? 70));
        $passed = $score >= $passScore;

        $attempt = $this->attempts->insert([
            'course_id' => $courseId,
            'enrollment_id' => $enrollmentId,
            'student_id' => (int) $student['id'],
            'score' => $score,
            'earned_points' => $earned,
            'total_points' => $total,
            'pass_score' => $passScore,
            'passed' => $passed,
            'answers' => $review,
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('quiz_submitted', "Teste final submetido com {$score}%.", (int) $student['id']);

        return ['ok' => true, 'attempt' => $attempt];
    }

    public function bestAttempt(int $enrollmentId): ?array
    {
        $items = array_values(array_filter(
            $this->attempts->all(),
            fn (array $attempt): bool => (int) ($attempt['enrollment_id'] ?? 0) === $enrollmentId
        ));

        usort($items, fn (array $a, array $b): int => (int) ($b['score'] ?? 0) <=> (int) ($a['score'] ?? 0));

        return $items[0] ?? null;
    }

    public function passedRequiredQuiz(array $course, int $enrollmentId): bool
    {
        if (empty($course['final_quiz_required'])) {
            return true;
        }

        $attempt = $this->bestAttempt($enrollmentId);

        return $attempt !== null && !empty($attempt['passed']);
    }

    private function parseOptions(string $value, string $type): array
    {
        if ($type === 'true_false') {
            return ['Verdadeiro', 'Falso'];
        }

        $parts = preg_split('/[\r\n]+/', $value) ?: [];
        $parts = array_values(array_filter(array_map('trim', $parts), fn (string $item): bool => $item !== ''));

        return array_values(array_unique($parts));
    }
}
