<?php

declare(strict_types=1);

namespace App\Services\Academy;

use App\Models\AcademyCourse;
use App\Models\AcademyCourseEnrollment;
use App\Models\AcademyCourseLesson;
use App\Models\AcademyLesson;
use App\Models\AcademyLessonProgress;
use App\Models\User;

final class AcademyCourseProgressService
{
    public function __construct(private readonly AcademyCourseNavigationService $navigation)
    {
    }

    public function getProgress(?User $user, AcademyCourse $course): array
    {
        $totalRequired = $this->getTotalRequiredLessonsCount($course);
        $completedRequired = $user ? $this->getCompletedRequiredLessonsCount($user, $course) : 0;
        $enrollment = $user
            ? AcademyCourseEnrollment::query()->with('lastLesson')->where('user_id', $user->id)->where('course_id', $course->id)->first()
            : null;

        return [
            'total_required' => $totalRequired,
            'completed_required' => $completedRequired,
            'progress_percent' => $this->getProgressPercent($user, $course),
            'enrollment' => $enrollment,
            'next_lesson' => $user ? $this->getNextLesson($user, $course) : null,
            'continue_lesson' => $user ? $this->getContinueLesson($user, $course) : null,
            'completed' => $enrollment?->status === AcademyCourseEnrollment::STATUS_COMPLETED,
        ];
    }

    public function getCompletedRequiredLessonsCount(User $user, AcademyCourse $course): int
    {
        $lessonIds = $course->courseLessons()
            ->where('is_required', true)
            ->pluck('lesson_id')
            ->filter()
            ->values()
            ->all();

        if ($lessonIds === []) {
            return 0;
        }

        return AcademyLessonProgress::query()
            ->where('user_id', $user->id)
            ->whereIn('lesson_id', $lessonIds)
            ->whereNotNull('completed_at')
            ->count();
    }

    public function getCompletedLessonIds(User $user, AcademyCourse $course): array
    {
        $lessonIds = $course->courseLessons()
            ->pluck('lesson_id')
            ->filter()
            ->map(fn ($lessonId): int => (int) $lessonId)
            ->values()
            ->all();

        if ($lessonIds === []) {
            return [];
        }

        return AcademyLessonProgress::query()
            ->where('user_id', $user->id)
            ->whereIn('lesson_id', $lessonIds)
            ->whereNotNull('completed_at')
            ->pluck('lesson_id')
            ->map(fn ($lessonId): int => (int) $lessonId)
            ->values()
            ->all();
    }

    public function getTotalRequiredLessonsCount(AcademyCourse $course): int
    {
        return $course->courseLessons()->where('is_required', true)->count();
    }

    public function getProgressPercent(?User $user, AcademyCourse $course): int
    {
        if (! $user) {
            return 0;
        }

        $totalRequired = $this->getTotalRequiredLessonsCount($course);

        if ($totalRequired < 1) {
            return 0;
        }

        return (int) floor(($this->getCompletedRequiredLessonsCount($user, $course) / $totalRequired) * 100);
    }

    public function getNextLesson(User $user, AcademyCourse $course): ?AcademyCourseLesson
    {
        $completedLessonIds = $this->getCompletedLessonIds($user, $course);

        return $this->navigation->orderedCourseLessons($course)
            ->first(fn (AcademyCourseLesson $courseLesson): bool => ! in_array((int) $courseLesson->lesson_id, $completedLessonIds, true));
    }

    public function getPreviousLesson(AcademyCourse $course, AcademyLesson $lesson): ?AcademyCourseLesson
    {
        return $this->navigation->previousLesson($course, $lesson);
    }

    public function getContinueLesson(User $user, AcademyCourse $course): ?AcademyCourseLesson
    {
        $enrollment = AcademyCourseEnrollment::query()
            ->where('user_id', $user->id)
            ->where('course_id', $course->id)
            ->first();

        if ($enrollment?->last_lesson_id) {
            $lastLesson = AcademyLesson::query()->find($enrollment->last_lesson_id);

            if ($lastLesson instanceof AcademyLesson) {
                $nextLesson = $this->navigation->nextLesson($course, $lastLesson);

                return $nextLesson ?? $this->navigation->findCourseLesson($course, $lastLesson);
            }
        }

        return $this->getNextLesson($user, $course) ?? $this->navigation->firstPublishedLesson($course);
    }

    public function markEnrollmentStarted(User $user, AcademyCourse $course): AcademyCourseEnrollment
    {
        return AcademyCourseEnrollment::query()->updateOrCreate(
            [
                'user_id' => $user->id,
                'course_id' => $course->id,
            ],
            [
                'status' => AcademyCourseEnrollment::STATUS_ACTIVE,
                'started_at' => now(),
                'completed_at' => null,
            ],
        );
    }

    public function updateLastLesson(User $user, AcademyCourse $course, AcademyLesson $lesson): AcademyCourseEnrollment
    {
        $enrollment = $this->markEnrollmentStarted($user, $course);
        $enrollment->forceFill([
            'last_lesson_id' => $lesson->id,
        ])->save();

        return $enrollment;
    }

    public function markCourseCompletedIfFinished(User $user, AcademyCourse $course): AcademyCourseEnrollment
    {
        $enrollment = $this->markEnrollmentStarted($user, $course);
        $progressPercent = $this->getProgressPercent($user, $course);
        $isComplete = $this->getTotalRequiredLessonsCount($course) > 0 && $progressPercent >= 100;

        $enrollment->forceFill([
            'status' => $isComplete ? AcademyCourseEnrollment::STATUS_COMPLETED : AcademyCourseEnrollment::STATUS_ACTIVE,
            'completed_at' => $isComplete ? ($enrollment->completed_at ?? now()) : null,
        ])->save();

        return $enrollment;
    }
}