<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class ReviewService
{
    private FileStore $reviews;
    private AuthService $auth;
    private CourseService $courses;
    private EnrollmentService $enrollments;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->reviews = new FileStore(storage_path('data/reviews.json'));
        $this->auth = new AuthService();
        $this->courses = new CourseService();
        $this->enrollments = new EnrollmentService();
        $this->logs = new ActivityLogService();
    }

    public function createOrUpdate(int $courseId, array $student, array $data): array
    {
        if (($student['role'] ?? '') !== 'student') {
            return ['ok' => false, 'error' => 'Apenas alunos podem avaliar cursos.'];
        }

        $course = $this->courses->find($courseId);

        if (!$course || ($course['status'] ?? '') !== 'published') {
            return ['ok' => false, 'error' => 'Curso indisponivel.'];
        }

        if (!$this->enrollments->findEnrollment((int) $student['id'], $courseId)) {
            return ['ok' => false, 'error' => 'Precisa estar inscrito no curso para avaliar.'];
        }

        $rating = (int) ($data['rating'] ?? 0);
        $comment = trim((string) ($data['comment'] ?? ''));

        if ($rating < 1 || $rating > 5) {
            return ['ok' => false, 'error' => 'Escolha uma nota entre 1 e 5 estrelas.'];
        }

        if (strlen($comment) > 1000) {
            return ['ok' => false, 'error' => 'Comentario demasiado longo.'];
        }

        $records = $this->reviews->all();
        $updated = null;

        foreach ($records as $index => $review) {
            if ((int) ($review['course_id'] ?? 0) === $courseId && (int) ($review['student_id'] ?? 0) === (int) $student['id']) {
                $records[$index] = array_merge($review, [
                    'rating' => $rating,
                    'comment' => $comment,
                    'status' => 'active',
                    'updated_at' => date('c'),
                ]);
                $updated = $records[$index];
                break;
            }
        }

        if (!$updated) {
            $updated = [
                'id' => $this->nextId($records),
                'course_id' => $courseId,
                'course_title' => $course['title'],
                'student_id' => (int) $student['id'],
                'student_name' => $student['name'] ?? 'Aluno',
                'student_email' => $student['email'] ?? '',
                'rating' => $rating,
                'comment' => $comment,
                'status' => 'active',
                'created_at' => date('c'),
                'updated_at' => date('c'),
            ];
            $records[] = $updated;
        }

        $this->reviews->save($records);
        $this->refreshCourseStats($courseId);
        $this->logs->log('course_reviewed', "Curso {$course['title']} avaliado com {$rating} estrelas.", (int) $student['id']);

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

    public function activeForCourse(int $courseId): array
    {
        $items = array_values(array_filter(
            $this->reviews->all(),
            fn (array $review): bool => (int) ($review['course_id'] ?? 0) === $courseId && ($review['status'] ?? '') === 'active'
        ));

        usort($items, fn (array $a, array $b): int => strcmp($b['updated_at'] ?? '', $a['updated_at'] ?? ''));

        return $items;
    }

    public function reviewForStudent(int $courseId, int $studentId): ?array
    {
        foreach ($this->reviews->all() as $review) {
            if ((int) ($review['course_id'] ?? 0) === $courseId && (int) ($review['student_id'] ?? 0) === $studentId) {
                return $review;
            }
        }

        return null;
    }

    public function all(array $filters = []): array
    {
        $status = (string) ($filters['status'] ?? '');
        $search = strtolower(trim((string) ($filters['search'] ?? '')));
        $items = $this->reviews->all();

        if ($status !== '') {
            $items = array_filter($items, fn (array $review): bool => ($review['status'] ?? '') === $status);
        }

        if ($search !== '') {
            $items = array_filter($items, function (array $review) use ($search): bool {
                $haystack = strtolower(
                    ($review['course_title'] ?? '') . ' ' .
                    ($review['student_name'] ?? '') . ' ' .
                    ($review['student_email'] ?? '') . ' ' .
                    ($review['comment'] ?? '')
                );

                return str_contains($haystack, $search);
            });
        }

        usort($items, fn (array $a, array $b): int => strcmp($b['updated_at'] ?? '', $a['updated_at'] ?? ''));

        return array_values($items);
    }

    public function updateStatus(int $id, string $status, int $adminId): array
    {
        if (!in_array($status, ['active', 'hidden'], true)) {
            return ['ok' => false, 'error' => 'Estado invalido.'];
        }

        $records = $this->reviews->all();
        $updated = null;

        foreach ($records as $index => $review) {
            if ((int) ($review['id'] ?? 0) === $id) {
                $records[$index]['status'] = $status;
                $records[$index]['moderated_by'] = $adminId;
                $records[$index]['moderated_at'] = date('c');
                $records[$index]['updated_at'] = date('c');
                $updated = $records[$index];
                break;
            }
        }

        if (!$updated) {
            return ['ok' => false, 'error' => 'Avaliacao nao encontrada.'];
        }

        $this->reviews->save($records);
        $this->refreshCourseStats((int) $updated['course_id']);
        $this->logs->log('review_moderated', "Avaliacao #{$id} actualizada para {$status}.", $adminId);

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

    private function refreshCourseStats(int $courseId): void
    {
        $active = $this->activeForCourse($courseId);
        $count = count($active);
        $average = $count > 0 ? array_sum(array_map(fn (array $review): int => (int) $review['rating'], $active)) / $count : 0;

        $this->courses->updateReviewStats($courseId, $average, $count);
    }

    private function nextId(array $records): int
    {
        $ids = array_column($records, 'id');

        return $ids ? max($ids) + 1 : 1;
    }
}
