<?php

namespace App\Services\Admin\AcademicClasses;

use App\Enums\AttendanceStatus;
use App\Models\Attendance;
use App\Models\CourseClass;
use App\Models\Student;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as BaseCollection;

class AttendanceService
{
    /**
     * Classes to show on the attendance overview: a dosen only sees classes
     * they lecture, optionally narrowed down to one academic term.
     */
    public function courseClassesForUser(User $user, ?int $academicTermId): Collection
    {
        return CourseClass::query()
            ->select(['course_classes.id', 'course_classes.course_id', 'course_classes.lecturer_id', 'course_classes.academic_term_id'])
            ->withCount('enrollments')
            ->with([
                'course:id,code,name',
                'academicTerm:id,academic_year,semester,start_date,end_date',
                'attendances' => fn ($q) => $q->select('course_class_id', 'meeting_number')->distinct(),
            ])
            ->when($user->hasRole('dosen'), fn ($q) => $q->where('lecturer_id', $user->lecturer?->id))
            ->when($academicTermId, fn ($q) => $q->where('academic_term_id', $academicTermId))
            ->orderBy('course_id')
            ->get()
            ->each(function (CourseClass $courseClass) {
                $courseClass->setAttribute('meetings_count', $courseClass->attendances->count());
                $courseClass->unsetRelation('attendances');
            });
    }

    /**
     * Every meeting recorded so far for a single class.
     */
    public function sessionsForClass(CourseClass $courseClass): Collection
    {
        return Attendance::query()
            ->select(['meeting_number', 'date'])
            ->selectRaw('COUNT(*) as total_count')
            ->selectRaw("SUM(CASE WHEN status = 'present' THEN 1 ELSE 0 END) as present_count")
            ->where('course_class_id', $courseClass->id)
            ->groupBy('meeting_number', 'date')
            ->orderByDesc('meeting_number')
            ->get();
    }

    /**
     * The meeting number a new session for this class should default to.
     */
    public function nextMeetingNumber(CourseClass $courseClass): int
    {
        return (Attendance::query()->where('course_class_id', $courseClass->id)->max('meeting_number') ?? 0) + 1;
    }

    public function session(CourseClass $courseClass, int $meetingNumber): array
    {
        $existing = Attendance::query()
            ->where('course_class_id', $courseClass->id)
            ->where('meeting_number', $meetingNumber)
            ->get()
            ->keyBy('student_id');

        $roster = $courseClass->enrollments()
            ->with('student.user.profile')
            ->get()
            ->map(function ($enrollment) use ($existing) {
                $attendance = $existing->get($enrollment->student_id);

                return [
                    'student_id' => $enrollment->student_id,
                    'student' => $enrollment->student,
                    'status' => $attendance?->status?->value ?? AttendanceStatus::Absent->value,
                ];
            })
            ->values();

        return [
            'roster' => $roster,
            'date' => $existing->first()?->date?->format('Y-m-d'),
        ];
    }

    public function saveSession(int $courseClassId, int $meetingNumber, string $date, array $records): void
    {
        foreach ($records as $record) {
            Attendance::updateOrCreate(
                [
                    'course_class_id' => $courseClassId,
                    'student_id' => $record['student_id'],
                    'meeting_number' => $meetingNumber,
                ],
                [
                    'date' => $date,
                    'status' => $record['status'],
                    'recorded_at' => now(),
                ],
            );
        }
    }

    public function deleteSession(int $courseClassId, int $meetingNumber): void
    {
        Attendance::query()
            ->where('course_class_id', $courseClassId)
            ->where('meeting_number', $meetingNumber)
            ->delete();
    }

    /**
     * Per-class attendance summary for a student's own classes: every class
     * they're enrolled in, with their recorded meetings and a present/total
     * tally, even for classes with no attendance taken yet.
     */
    public function forStudent(Student $student): BaseCollection
    {
        $enrollments = $student->enrollments()
            ->with([
                'courseClass.course:id,code,name',
                'courseClass.academicTerm:id,academic_year,semester,start_date,end_date',
            ])
            ->get();

        $recordsByClass = Attendance::query()
            ->where('student_id', $student->id)
            ->orderBy('meeting_number')
            ->get(['course_class_id', 'meeting_number', 'date', 'status'])
            ->groupBy('course_class_id');

        return $enrollments->map(function ($enrollment) use ($recordsByClass) {
            $records = $recordsByClass->get($enrollment->course_class_id, new Collection);

            return [
                'course_class' => $enrollment->courseClass,
                'records' => $records->values(),
                'present_count' => $records->where('status', AttendanceStatus::Present)->count(),
                'total_count' => $records->count(),
            ];
        })->values();
    }
}
