<?php

declare(strict_types=1);

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Models\AcademyCourse;
use App\Models\AcademyCourseLesson;
use App\Models\AcademyCourseSection;
use App\Models\AcademyLesson;
use App\Services\Academy\AcademyCacheService;
use App\Services\Academy\AcademyCourseLessonOrderingService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Inertia\Response;

final class AcademyCourseBuilderController extends Controller
{
    public function __construct(
        private readonly AcademyCacheService $cache,
        private readonly AcademyCourseLessonOrderingService $courseLessonOrdering,
    )
    {
    }

    public function edit(AcademyCourse $academyCourse): Response
    {
        $academyCourse->load(['sections', 'courseLessons.section', 'courseLessons.lesson.category']);

        return Inertia::render('Admin/Academy/CourseBuilder', [
            'course' => $this->serializeCourse($academyCourse),
            'sections' => $academyCourse->sections
                ->sortBy([['order_num', 'asc'], ['id', 'asc']])
                ->values()
                ->map(fn (AcademyCourseSection $section): array => $this->serializeSection($section))
                ->all(),
            'courseLessons' => $academyCourse->courseLessons
                ->sortBy([['order_num', 'asc'], ['id', 'asc']])
                ->values()
                ->map(fn (AcademyCourseLesson $courseLesson): array => $this->serializeCourseLesson($courseLesson))
                ->all(),
            'availableLessons' => AcademyLesson::query()
                ->with('category')
                ->orderBy('title')
                ->get()
                ->map(fn (AcademyLesson $lesson): array => [
                    'id' => (int) $lesson->id,
                    'title' => (string) $lesson->title,
                    'slug' => (string) $lesson->slug,
                    'excerpt' => (string) ($lesson->excerpt ?? ''),
                    'difficulty' => (string) $lesson->difficulty,
                    'access_level' => (string) $lesson->access_level,
                    'active' => (bool) $lesson->active,
                    'published_at' => $lesson->published_at?->toISOString(),
                    'category' => $lesson->category ? (string) $lesson->category->name : '',
                    'attached' => $academyCourse->courseLessons->contains(fn (AcademyCourseLesson $courseLesson): bool => (int) $courseLesson->lesson_id === (int) $lesson->id),
                ])
                ->values()
                ->all(),
            'routes' => [
                'index' => route('admin.academy.courses.index'),
                'edit' => route('admin.academy.courses.edit', ['academyCourse' => $academyCourse]),
                'preview' => route('academy.courses.show', ['course' => $academyCourse->slug]),
                'sectionStore' => route('admin.academy.courses.sections.store', ['academyCourse' => $academyCourse]),
                'attachLesson' => route('admin.academy.courses.lessons.attach', ['academyCourse' => $academyCourse]),
                'reorder' => route('admin.academy.courses.reorder', ['academyCourse' => $academyCourse]),
            ],
        ]);
    }

    public function storeSection(Request $request, AcademyCourse $academyCourse): RedirectResponse
    {
        $data = $request->validate([
            'title' => ['required', 'string', 'max:180'],
            'slug' => ['nullable', 'string', 'max:180'],
            'description' => ['nullable', 'string'],
            'order_num' => ['nullable', 'integer', 'min:0'],
            'is_visible' => ['nullable', 'boolean'],
        ]);

        $academyCourse->sections()->create([
            'title' => $data['title'],
            'slug' => filled($data['slug'] ?? null) ? $data['slug'] : Str::slug($data['title']),
            'description' => $data['description'] ?? null,
            'order_num' => (int) ($data['order_num'] ?? ($academyCourse->sections()->max('order_num') + 1)),
            'is_visible' => (bool) ($data['is_visible'] ?? true),
        ]);

        $this->cache->clearAll();

        return back()->with('success', 'Course section created.');
    }

    public function updateSection(Request $request, AcademyCourse $academyCourse, AcademyCourseSection $academyCourseSection): RedirectResponse
    {
        abort_unless((int) $academyCourseSection->course_id === (int) $academyCourse->id, 404);

        $data = $request->validate([
            'title' => ['required', 'string', 'max:180'],
            'slug' => ['nullable', 'string', 'max:180'],
            'description' => ['nullable', 'string'],
            'order_num' => ['nullable', 'integer', 'min:0'],
            'is_visible' => ['nullable', 'boolean'],
        ]);

        $academyCourseSection->forceFill([
            'title' => $data['title'],
            'slug' => filled($data['slug'] ?? null) ? $data['slug'] : Str::slug($data['title']),
            'description' => $data['description'] ?? null,
            'order_num' => (int) ($data['order_num'] ?? 0),
            'is_visible' => (bool) ($data['is_visible'] ?? true),
        ])->save();

        $this->cache->clearAll();

        return back()->with('success', 'Course section updated.');
    }

    public function destroySection(AcademyCourse $academyCourse, AcademyCourseSection $academyCourseSection): RedirectResponse
    {
        abort_unless((int) $academyCourseSection->course_id === (int) $academyCourse->id, 404);

        $academyCourseSection->delete();
        $this->cache->clearAll();

        return back()->with('success', 'Course section deleted.');
    }

    public function attachLesson(Request $request, AcademyCourse $academyCourse): RedirectResponse
    {
        $data = $request->validate([
            'lesson_id' => ['required', 'integer', 'exists:academy_lessons,id'],
            'section_id' => ['nullable', 'integer', 'exists:academy_course_sections,id'],
            'order_num' => ['nullable', 'integer', 'min:0'],
            'is_required' => ['nullable', 'boolean'],
            'access_override' => ['nullable', 'string', 'in:free,premium,creator,pro'],
            'unlock_after_lesson_id' => ['nullable', 'integer', 'exists:academy_lessons,id'],
        ]);

        if (AcademyCourseLesson::query()->where('course_id', $academyCourse->id)->where('lesson_id', $data['lesson_id'])->exists()) {
            return back()->with('error', 'That lesson is already attached to this course.');
        }

        $sectionId = $data['section_id'] ?? null;
        if ($sectionId !== null) {
            abort_unless(AcademyCourseSection::query()->where('course_id', $academyCourse->id)->whereKey($sectionId)->exists(), 404);
        }

        AcademyCourseLesson::query()->create([
            'course_id' => $academyCourse->id,
            'section_id' => $sectionId,
            'lesson_id' => (int) $data['lesson_id'],
            'order_num' => (int) ($data['order_num'] ?? ($academyCourse->courseLessons()->max('order_num') + 1)),
            'is_required' => (bool) ($data['is_required'] ?? true),
            'access_override' => $data['access_override'] ?? null,
            'unlock_after_lesson_id' => $data['unlock_after_lesson_id'] ?? null,
        ]);

        $this->courseLessonOrdering->syncCourse($academyCourse);
        $this->syncCourseCounts($academyCourse);

        return back()->with('success', 'Lesson attached to course.');
    }

    public function updateCourseLesson(Request $request, AcademyCourse $academyCourse, AcademyCourseLesson $academyCourseLesson): RedirectResponse
    {
        abort_unless((int) $academyCourseLesson->course_id === (int) $academyCourse->id, 404);

        $data = $request->validate([
            'section_id' => ['nullable', 'integer', 'exists:academy_course_sections,id'],
            'order_num' => ['nullable', 'integer', 'min:0'],
            'is_required' => ['nullable', 'boolean'],
            'access_override' => ['nullable', 'string', 'in:free,premium,creator,pro'],
            'unlock_after_lesson_id' => ['nullable', 'integer', 'exists:academy_lessons,id'],
        ]);

        $sectionId = $data['section_id'] ?? null;
        if ($sectionId !== null) {
            abort_unless(AcademyCourseSection::query()->where('course_id', $academyCourse->id)->whereKey($sectionId)->exists(), 404);
        }

        $academyCourseLesson->forceFill([
            'section_id' => $sectionId,
            'order_num' => (int) ($data['order_num'] ?? 0),
            'is_required' => (bool) ($data['is_required'] ?? true),
            'access_override' => $data['access_override'] ?? null,
            'unlock_after_lesson_id' => $data['unlock_after_lesson_id'] ?? null,
        ])->save();

        $this->courseLessonOrdering->syncCourse($academyCourse);
        $this->syncCourseCounts($academyCourse);

        return back()->with('success', 'Course lesson updated.');
    }

    public function detachLesson(AcademyCourse $academyCourse, AcademyCourseLesson $academyCourseLesson): RedirectResponse
    {
        abort_unless((int) $academyCourseLesson->course_id === (int) $academyCourse->id, 404);

        $academyCourseLesson->delete();
        $this->courseLessonOrdering->syncCourse($academyCourse);
        $this->syncCourseCounts($academyCourse);

        return back()->with('success', 'Lesson removed from course.');
    }

    public function reorder(Request $request, AcademyCourse $academyCourse): RedirectResponse
    {
        $data = $request->validate([
            'sections' => ['nullable', 'array'],
            'sections.*.id' => ['required', 'integer', 'exists:academy_course_sections,id'],
            'sections.*.order_num' => ['required', 'integer', 'min:0'],
            'lessons' => ['nullable', 'array'],
            'lessons.*.id' => ['required', 'integer', 'exists:academy_course_lessons,id'],
            'lessons.*.order_num' => ['required', 'integer', 'min:0'],
            'lessons.*.section_id' => ['nullable', 'integer', 'exists:academy_course_sections,id'],
        ]);

        foreach ((array) ($data['sections'] ?? []) as $sectionData) {
            AcademyCourseSection::query()
                ->where('course_id', $academyCourse->id)
                ->whereKey((int) $sectionData['id'])
                ->update(['order_num' => (int) $sectionData['order_num']]);
        }

        foreach ((array) ($data['lessons'] ?? []) as $lessonData) {
            AcademyCourseLesson::query()
                ->where('course_id', $academyCourse->id)
                ->whereKey((int) $lessonData['id'])
                ->update([
                    'order_num' => (int) $lessonData['order_num'],
                    'section_id' => $lessonData['section_id'] ?? null,
                ]);
        }

                $this->courseLessonOrdering->syncCourse($academyCourse);
        $this->syncCourseCounts($academyCourse);

        return back()->with('success', 'Course order updated.');
    }

    private function syncCourseCounts(AcademyCourse $academyCourse): void
    {
        $academyCourse->forceFill([
            'lessons_count_cache' => $academyCourse->courseLessons()->count(),
        ])->save();

        $this->cache->clearAll();
    }

    private function serializeCourse(AcademyCourse $course): array
    {
        return [
            'id' => (int) $course->id,
            'title' => (string) $course->title,
            'slug' => (string) $course->slug,
            'subtitle' => (string) ($course->subtitle ?? ''),
            'excerpt' => (string) ($course->excerpt ?? ''),
            'description' => (string) ($course->description ?? ''),
            'access_level' => (string) $course->access_level,
            'difficulty' => (string) $course->difficulty,
            'status' => (string) $course->status,
            'lessons_count_cache' => (int) ($course->lessons_count_cache ?? 0),
            'cover_image' => (string) ($course->cover_image ?? ''),
            'published_at' => $course->published_at?->toISOString(),
        ];
    }

    private function serializeSection(AcademyCourseSection $section): array
    {
        return [
            'id' => (int) $section->id,
            'title' => (string) $section->title,
            'slug' => (string) ($section->slug ?? ''),
            'description' => (string) ($section->description ?? ''),
            'order_num' => (int) ($section->order_num ?? 0),
            'is_visible' => (bool) ($section->is_visible ?? true),
            'updateUrl' => route('admin.academy.courses.sections.update', ['academyCourse' => $section->course_id, 'academyCourseSection' => $section]),
            'destroyUrl' => route('admin.academy.courses.sections.destroy', ['academyCourse' => $section->course_id, 'academyCourseSection' => $section]),
        ];
    }

    private function serializeCourseLesson(AcademyCourseLesson $courseLesson): array
    {
        $lesson = $courseLesson->lesson;

        return [
            'id' => (int) $courseLesson->id,
            'section_id' => $courseLesson->section_id ? (int) $courseLesson->section_id : null,
            'lesson_id' => (int) $courseLesson->lesson_id,
            'title' => (string) ($lesson?->title ?? ''),
            'slug' => (string) ($lesson?->slug ?? ''),
            'excerpt' => (string) ($lesson?->excerpt ?? ''),
            'difficulty' => (string) ($lesson?->difficulty ?? ''),
            'access_level' => (string) ($lesson?->access_level ?? ''),
            'category' => (string) ($lesson?->category?->name ?? ''),
            'order_num' => (int) ($courseLesson->order_num ?? 0),
            'is_required' => (bool) $courseLesson->is_required,
            'access_override' => $courseLesson->access_override,
            'unlock_after_lesson_id' => $courseLesson->unlock_after_lesson_id ? (int) $courseLesson->unlock_after_lesson_id : null,
            'updateUrl' => route('admin.academy.courses.lessons.update', ['academyCourse' => $courseLesson->course_id, 'academyCourseLesson' => $courseLesson]),
            'destroyUrl' => route('admin.academy.courses.lessons.destroy', ['academyCourse' => $courseLesson->course_id, 'academyCourseLesson' => $courseLesson]),
        ];
    }
}