<?php

declare(strict_types=1);

namespace App\Services\Academy;

use App\Models\AcademyCourse;
use App\Models\AcademyLesson;
use App\Models\AcademyLessonProgress;
use App\Models\AcademyPromptTemplate;
use App\Models\AcademySavedPrompt;
use App\Models\AcademySave;
use App\Models\AcademyUserProgress;
use App\Models\User;
use App\Support\AcademyAnalytics\AcademyAnalyticsContentType;
use App\Support\AcademyAnalytics\AcademyAnalyticsEventType;
use App\Support\AcademyAnalytics\AcademyAnalyticsProgressStatus;
use Illuminate\Http\Request;

final class AcademyProgressService
{
    public function __construct(
        private readonly AcademyBadgeService $badges,
        private readonly AcademyCourseProgressService $courses,
        private readonly AcademyAnalyticsService $analytics,
    ) {
    }

    public function startLesson(User $user, int $lessonId, ?int $courseId = null, ?Request $request = null): AcademyUserProgress
    {
        $progress = $this->updateUserProgressRecord($user, $courseId, $lessonId, [
            'status' => AcademyAnalyticsProgressStatus::STARTED,
            'progress_percent' => 0,
            'started_at' => now(),
            'completed_at' => null,
            'last_seen_at' => now(),
        ]);

        if ($courseId) {
            $course = AcademyCourse::query()->find($courseId);
            if ($course instanceof AcademyCourse) {
                $this->courses->markEnrollmentStarted($user, $course);
                $lesson = AcademyLesson::query()->find($lessonId);
                if ($lesson instanceof AcademyLesson) {
                    $this->courses->updateLastLesson($user, $course, $lesson);
                }
                $this->syncCourseProgressRecord($user, $course);
            }
        }

        $this->analytics->track([
            'event_type' => AcademyAnalyticsEventType::LESSON_STARTED,
            'content_type' => AcademyAnalyticsContentType::LESSON,
            'content_id' => $lessonId,
            'metadata' => array_filter([
                'course_id' => $courseId,
            ], static fn (mixed $value): bool => $value !== null),
        ], $user, $request);

        return $progress;
    }

    public function completeLesson(User $user, int $lessonId, ?int $courseId = null, ?Request $request = null): AcademyUserProgress
    {
        $progress = $this->updateUserProgressRecord($user, $courseId, $lessonId, [
            'status' => AcademyAnalyticsProgressStatus::COMPLETED,
            'progress_percent' => 100,
            'started_at' => now(),
            'completed_at' => now(),
            'last_seen_at' => now(),
        ]);

        if ($courseId) {
            $course = AcademyCourse::query()->find($courseId);
            if ($course instanceof AcademyCourse) {
                $this->syncCourseProgressRecord($user, $course);
            }
        }

        $this->analytics->track([
            'event_type' => AcademyAnalyticsEventType::LESSON_COMPLETED,
            'content_type' => AcademyAnalyticsContentType::LESSON,
            'content_id' => $lessonId,
            'metadata' => array_filter([
                'course_id' => $courseId,
            ], static fn (mixed $value): bool => $value !== null),
        ], $user, $request);

        return $progress;
    }

    public function startCourse(User $user, int $courseId, ?Request $request = null): AcademyUserProgress
    {
        $course = AcademyCourse::query()->findOrFail($courseId);
        $this->courses->markEnrollmentStarted($user, $course);

        $progress = $this->syncCourseProgressRecord($user, $course, true);

        $this->analytics->track([
            'event_type' => AcademyAnalyticsEventType::COURSE_STARTED,
            'content_type' => AcademyAnalyticsContentType::COURSE,
            'content_id' => $courseId,
        ], $user, $request);

        return $progress;
    }

    public function completeCourse(User $user, int $courseId, ?Request $request = null): AcademyUserProgress
    {
        $course = AcademyCourse::query()->findOrFail($courseId);
        $this->courses->markCourseCompletedIfFinished($user, $course);
        $progress = $this->syncCourseProgressRecord($user, $course);

        if ($progress->status === AcademyAnalyticsProgressStatus::COMPLETED) {
            $this->analytics->track([
                'event_type' => AcademyAnalyticsEventType::COURSE_COMPLETED,
                'content_type' => AcademyAnalyticsContentType::COURSE,
                'content_id' => $courseId,
            ], $user, $request);
        }

        return $progress;
    }

    public function markLessonComplete(User $user, AcademyLesson $lesson, ?AcademyCourse $course = null, ?Request $request = null): AcademyLessonProgress
    {
        $progress = AcademyLessonProgress::query()->updateOrCreate(
            [
                'lesson_id' => $lesson->id,
                'user_id' => $user->id,
            ],
            [
                'completed_at' => now(),
            ],
        );

        if ($course instanceof AcademyCourse) {
            $this->courses->updateLastLesson($user, $course, $lesson);
            $this->courses->markCourseCompletedIfFinished($user, $course);
        }

        $this->completeLesson($user, (int) $lesson->id, $course?->id, $request);

        $this->badges->syncForUser($user);

        return $progress;
    }

    public function savePrompt(User $user, AcademyPromptTemplate $prompt): AcademySavedPrompt
    {
        $saved = AcademySavedPrompt::query()->firstOrCreate([
            'prompt_template_id' => $prompt->id,
            'user_id' => $user->id,
        ]);

        AcademySave::query()->firstOrCreate([
            'user_id' => $user->id,
            'content_type' => AcademyAnalyticsContentType::PROMPT,
            'content_id' => $prompt->id,
        ]);

        $this->analytics->track([
            'event_type' => AcademyAnalyticsEventType::PROMPT_SAVE,
            'content_type' => AcademyAnalyticsContentType::PROMPT,
            'content_id' => (int) $prompt->id,
        ], $user);

        $this->badges->syncForUser($user);

        return $saved;
    }

    public function unsavePrompt(User $user, AcademyPromptTemplate $prompt): void
    {
        AcademySavedPrompt::query()
            ->where('prompt_template_id', $prompt->id)
            ->where('user_id', $user->id)
            ->delete();

        AcademySave::query()
            ->where('user_id', $user->id)
            ->where('content_type', AcademyAnalyticsContentType::PROMPT)
            ->where('content_id', $prompt->id)
            ->delete();

        $this->badges->syncForUser($user);
    }

    /**
     * @param  array<string, mixed>  $attributes
     */
    private function updateUserProgressRecord(User $user, ?int $courseId, ?int $lessonId, array $attributes): AcademyUserProgress
    {
        return AcademyUserProgress::query()->updateOrCreate(
            [
                'user_id' => $user->id,
                'course_id' => $courseId,
                'lesson_id' => $lessonId,
            ],
            $attributes,
        );
    }

    private function syncCourseProgressRecord(User $user, AcademyCourse $course, bool $forceStarted = false): AcademyUserProgress
    {
        $progressPercent = $this->courses->getProgressPercent($user, $course);
        $isComplete = $this->courses->getTotalRequiredLessonsCount($course) > 0 && $progressPercent >= 100;
        $status = $isComplete
            ? AcademyAnalyticsProgressStatus::COMPLETED
            : ($progressPercent > 0 || $forceStarted
                ? AcademyAnalyticsProgressStatus::IN_PROGRESS
                : AcademyAnalyticsProgressStatus::STARTED);

        return $this->updateUserProgressRecord($user, (int) $course->id, null, [
            'status' => $status,
            'progress_percent' => $progressPercent,
            'started_at' => now(),
            'completed_at' => $isComplete ? now() : null,
            'last_seen_at' => now(),
            'metadata' => [
                'completed_required' => $this->courses->getCompletedRequiredLessonsCount($user, $course),
                'total_required' => $this->courses->getTotalRequiredLessonsCount($course),
            ],
        ]);
    }
}