<?php

namespace App\Services\School;

use App\Models\Assessment;
use App\Models\Event;
use App\Models\SchoolClass;
use App\Models\Student;
use App\Models\TimetableSlot;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class TimetableCalendarService
{
    /** @var array<int, string> */
    public const DAYS = [
        1 => 'Monday',
        2 => 'Tuesday',
        3 => 'Wednesday',
        4 => 'Thursday',
        5 => 'Friday',
        6 => 'Saturday',
        7 => 'Sunday',
    ];

    /**
     * @return array{days: array<int, string>, grid: array<int, list<array<string, mixed>>>, slots: Collection<int, TimetableSlot>}
     */
    public function weeklyGrid(?int $classId = null, ?int $staffId = null, ?int $termId = null): array
    {
        $query = TimetableSlot::query()
            ->with(['schoolClass', 'subject', 'staff'])
            ->orderBy('day_of_week')
            ->orderBy('starts_at');

        if ($classId) {
            $query->where('class_id', $classId);
        }

        if ($staffId) {
            $query->where('staff_id', $staffId);
        }

        if ($termId) {
            $query->where('term_id', $termId);
        } else {
            $currentTermId = DB::table('terms')->where('is_current', true)->value('id');
            if ($currentTermId) {
                $query->where('term_id', $currentTermId);
            }
        }

        $slots = $query->get();

        $grid = [];
        foreach (self::DAYS as $dayNum => $dayName) {
            $grid[$dayNum] = $slots
                ->where('day_of_week', $dayNum)
                ->map(fn (TimetableSlot $slot) => [
                    'id' => $slot->id,
                    'starts_at' => substr((string) $slot->starts_at, 0, 5),
                    'ends_at' => substr((string) $slot->ends_at, 0, 5),
                    'class' => $slot->schoolClass?->name,
                    'subject' => $slot->subject?->name,
                    'teacher' => $slot->staff?->full_name,
                    'room' => $slot->room,
                    'type' => 'class',
                ])
                ->values()
                ->all();
        }

        return [
            'days' => self::DAYS,
            'grid' => $grid,
            'slots' => $slots,
        ];
    }

    /**
     * @return Collection<int, array<string, mixed>>
     */
    public function upcomingExams(?int $classId = null, ?int $studentId = null, int $days = 30): Collection
    {
        $query = Assessment::query()
            ->with(['subject', 'schoolClass'])
            ->where('status', 'published')
            ->whereNotNull('assessment_date')
            ->whereBetween('assessment_date', [now()->toDateString(), now()->addDays($days)->toDateString()])
            ->orderBy('assessment_date');

        if ($classId) {
            $query->where('class_id', $classId);
        }

        if ($studentId) {
            $classIds = Student::query()->find($studentId)?->classes()->pluck('classes.id') ?? collect();
            $query->whereIn('class_id', $classIds);
        }

        return $query->get()->map(fn (Assessment $a) => [
            'id' => $a->id,
            'title' => $a->title,
            'date' => $a->assessment_date?->toDateString(),
            'type' => $a->assessment_type,
            'class' => $a->schoolClass?->name,
            'subject' => $a->subject?->name,
            'kind' => 'exam',
        ]);
    }

    /**
     * @return Collection<int, array<string, mixed>>
     */
    public function upcomingEvents(?string $audience = null, int $days = 30): Collection
    {
        $query = Event::query()
            ->with(['studentOrganizer'])
            ->where('status', 'scheduled')
            ->where('starts_at', '>=', now())
            ->where('starts_at', '<=', now()->addDays($days))
            ->orderBy('starts_at');

        if ($audience && $audience !== 'all') {
            $query->where(function ($q) use ($audience) {
                $q->where('audience', 'all')->orWhere('audience', $audience);
            });
        }

        return $query->get()->map(fn (Event $e) => [
            'id' => $e->id,
            'title' => $e->title,
            'date' => $e->starts_at?->toDateString(),
            'time' => $e->starts_at?->format('H:i'),
            'location' => $e->location,
            'event_type' => $e->event_type,
            'organizer' => $e->organizer_type === 'student_leadership'
                ? $e->studentOrganizer?->full_name
                : 'Administration',
            'kind' => 'event',
        ]);
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function mergedSchedule(?int $classId = null, ?int $studentId = null, ?int $staffId = null, ?string $audience = null): array
    {
        $weekly = $this->weeklyGrid($classId, $staffId);
        $exams = $this->upcomingExams($classId, $studentId)->all();
        $events = $this->upcomingEvents($audience)->all();

        return [
            'weekly' => $weekly,
            'exams' => $exams,
            'events' => $events,
        ];
    }

    public function resolveClassForStudent(?Student $student): ?SchoolClass
    {
        if (! $student) {
            return null;
        }

        return $student->classes()->first();
    }
}
