<?php

namespace App\Services\School;

use App\Models\Announcement;
use App\Models\Assessment;
use App\Models\Assignment;
use App\Models\AssignmentSubmission;
use App\Models\AttendanceRecord;
use App\Models\DisciplinaryCase;
use App\Models\Invoice;
use App\Models\Quiz;
use App\Models\ReportCard;
use App\Models\ResultsCompilation;
use App\Models\Student;
use App\Models\StudyMaterial;
use App\Models\Term;
use App\Models\TimetableSlot;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class AcademicDashboardService
{
    public function __construct(
        private readonly TeacherScopeService $scope,
        private readonly StudentScopeService $studentScope,
        private readonly ParentScopeService $parentScope,
    ) {}

    /**
     * @return array<string, mixed>
     */
    public function teacherStats(?User $user = null): array
    {
        $classes = $this->scope->assignedClasses($user);
        $classIds = $classes->pluck('id');

        $pendingMarking = AssignmentSubmission::query()
            ->whereNull('marks_awarded')
            ->whereHas('assignment', fn ($q) => $q->whereIn('class_id', $classIds))
            ->count();

        $pendingAssignments = Assignment::query()
            ->whereIn('class_id', $classIds)
            ->where('status', 'draft')
            ->count();

        $upcomingExams = Assessment::query()
            ->whereIn('class_id', $classIds)
            ->where('assessment_date', '>=', now()->toDateString())
            ->where('status', 'active')
            ->count();

        $activeQuizzes = Quiz::query()
            ->whereIn('class_id', $classIds)
            ->where('status', 'published')
            ->where(fn ($q) => $q->whereNull('ends_at')->orWhere('ends_at', '>=', now()))
            ->count();

        $draftResults = ResultsCompilation::query()
            ->whereIn('class_id', $classIds)
            ->where('status', 'draft')
            ->count();

        $submittedResults = ResultsCompilation::query()
            ->whereIn('class_id', $classIds)
            ->where('status', 'submitted')
            ->count();

        $staffId = $this->scope->staffId($user);
        $upcomingLessons = $staffId
            ? TimetableSlot::query()
                ->with(['subject', 'schoolClass'])
                ->where('staff_id', $staffId)
                ->where('day_of_week', strtolower(now()->format('l')))
                ->orderBy('starts_at')
                ->limit(5)
                ->get()
            : collect();

        return [
            'classes' => $classes,
            'total_students' => $this->scope->totalStudents($user),
            'subjects' => $this->scope->assignedSubjects($user),
            'pending_marking' => $pendingMarking,
            'pending_assignments' => $pendingAssignments,
            'upcoming_exams' => $upcomingExams,
            'active_quizzes' => $activeQuizzes,
            'draft_results' => $draftResults,
            'submitted_results' => $submittedResults,
            'upcoming_lessons' => $upcomingLessons,
            'materials_count' => StudyMaterial::query()->whereIn('class_id', $classIds)->count(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function headmasterStats(): array
    {
        return [
            'total_teachers' => \App\Models\Staff::query()->where('status', 'active')->count(),
            'total_students' => \App\Models\Student::query()->where('status', 'active')->count(),
            'total_classes' => \App\Models\SchoolClass::query()->count(),
            'pending_approvals' => ResultsCompilation::query()->where('status', 'submitted')->count(),
            'approved_unreleased' => ResultsCompilation::query()->where('status', 'approved')->whereNull('released_at')->count(),
            'open_discipline' => \App\Models\DisciplinaryCase::query()->whereNotIn('stage', ['closed', 'dismissed'])->count(),
            'draft_assignments' => Assignment::query()->where('status', 'draft')->count(),
            'pending_announcements' => Announcement::query()->where('approval_status', 'pending')->count(),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function studentStats(?Student $student = null): array
    {
        $student ??= $this->studentScope->student();
        if (! $student) {
            return ['student' => null];
        }

        $class = $this->studentScope->primaryClass($student);
        $classIds = $this->studentScope->classIds($student);
        $term = Term::query()->where('is_current', true)->first();
        $studentId = $student->id;
        $today = strtolower(now()->format('l'));

        $submittedIds = AssignmentSubmission::query()
            ->where('student_id', $studentId)
            ->whereIn('status', ['submitted', 'late_submitted', 'marked'])
            ->pluck('assignment_id');

        $upcomingAssignments = Assignment::query()
            ->with(['subject'])
            ->whereIn('class_id', $classIds)
            ->where('status', 'published')
            ->whereNotIn('id', $submittedIds)
            ->where(fn ($q) => $q->whereNull('due_at')->orWhere('due_at', '>=', now()))
            ->orderBy('due_at')
            ->limit(5)
            ->get();

        $pendingSubmissions = Assignment::query()
            ->whereIn('class_id', $classIds)
            ->where('status', 'published')
            ->whereNotIn('id', $submittedIds)
            ->count();

        $upcomingQuizzes = Quiz::query()
            ->with(['subject'])
            ->whereIn('class_id', $classIds)
            ->where('status', 'published')
            ->where(fn ($q) => $q->whereNull('ends_at')->orWhere('ends_at', '>=', now()))
            ->orderBy('starts_at')
            ->limit(5)
            ->get();

        $upcomingExams = Assessment::query()
            ->with(['subject'])
            ->whereIn('class_id', $classIds)
            ->where('status', 'active')
            ->where('assessment_date', '>=', now()->toDateString())
            ->orderBy('assessment_date')
            ->limit(5)
            ->get();

        $recentMaterials = StudyMaterial::query()
            ->with(['subject'])
            ->whereIn('class_id', $classIds)
            ->where('status', 'published')
            ->where(fn ($q) => $q->whereNull('publish_at')->orWhere('publish_at', '<=', now()))
            ->latest()
            ->limit(5)
            ->get();

        $announcements = Announcement::query()
            ->where('status', 'published')
            ->where('approval_status', 'approved')
            ->where(fn ($q) => $q->whereIn('audience', ['students', 'all'])->orWhereNull('audience'))
            ->where(fn ($q) => $q->whereNull('class_id')->orWhereIn('class_id', $classIds))
            ->where(fn ($q) => $q->whereNull('expires_at')->orWhere('expires_at', '>=', now()))
            ->latest('published_at')
            ->limit(5)
            ->get();

        $attendanceSummary = $this->attendanceSummary($studentId, $term?->id);

        $latestReportCard = ReportCard::query()
            ->with('term')
            ->where('student_id', $studentId)
            ->where('status', 'published')
            ->latest('published_at')
            ->first();

        $pendingResults = ResultsCompilation::query()
            ->where('student_id', $studentId)
            ->whereNotIn('status', ['released'])
            ->exists();

        $disciplineCount = DisciplinaryCase::query()
            ->where('subject_student_id', $studentId)
            ->whereNotIn('stage', ['closed', 'dismissed'])
            ->count();

        $feesBalance = Invoice::query()
            ->where('student_id', $studentId)
            ->whereIn('status', ['issued', 'partially_paid', 'overdue'])
            ->sum('balance_due');

        $subjects = TimetableSlot::query()
            ->with(['subject', 'staff'])
            ->where('class_id', $class?->id)
            ->when($term?->id, fn ($q) => $q->where('term_id', $term->id))
            ->get()
            ->unique('subject_id')
            ->values();

        $todayTimetable = TimetableSlot::query()
            ->with(['subject', 'staff'])
            ->where('class_id', $class?->id)
            ->where('day_of_week', $today)
            ->when($term?->id, fn ($q) => $q->where('term_id', $term->id))
            ->orderBy('starts_at')
            ->get();

        return [
            'student' => $student->load('academicYear'),
            'class' => $class,
            'term' => $term,
            'subjects' => $subjects,
            'today_timetable' => $todayTimetable,
            'upcoming_assignments' => $upcomingAssignments,
            'pending_submissions' => $pendingSubmissions,
            'upcoming_quizzes' => $upcomingQuizzes,
            'upcoming_exams' => $upcomingExams,
            'recent_materials' => $recentMaterials,
            'announcements' => $announcements,
            'attendance_summary' => $attendanceSummary,
            'latest_report_card' => $latestReportCard,
            'results_pending' => $pendingResults && ! $latestReportCard,
            'discipline_count' => $disciplineCount,
            'fees_balance' => $feesBalance,
            'unread_notifications' => $student->user?->unreadNotifications()->count() ?? auth()->user()?->unreadNotifications()->count() ?? 0,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function parentStats(?Student $activeChild = null): array
    {
        $children = $this->parentScope->linkedStudents();
        $activeChild ??= $this->parentScope->activeStudent();

        $childStats = [];
        foreach ($children as $child) {
            $stats = $this->studentStats($child);
            $childStats[$child->id] = [
                'student' => $child,
                'class' => $stats['class'] ?? null,
                'fees_balance' => $stats['fees_balance'] ?? 0,
                'attendance_summary' => $stats['attendance_summary'] ?? null,
                'pending_submissions' => $stats['pending_submissions'] ?? 0,
                'discipline_count' => $stats['discipline_count'] ?? 0,
            ];
        }

        $activeStats = $activeChild ? $this->studentStats($activeChild) : [];

        $recentPayments = $activeChild
            ? $activeChild->receipts()->latest()->limit(3)->get()
            : collect();

        return array_merge($activeStats, [
            'children' => $children,
            'active_child' => $activeChild,
            'child_stats' => $childStats,
            'recent_payments' => $recentPayments,
            'guardian' => $this->parentScope->guardian(),
        ]);
    }

    /**
     * @return object|null
     */
    protected function attendanceSummary(int $studentId, ?int $termId = null): ?object
    {
        return AttendanceRecord::query()
            ->select([
                DB::raw("SUM(CASE WHEN attendance_records.status = 'present' THEN 1 ELSE 0 END) as present_count"),
                DB::raw("SUM(CASE WHEN attendance_records.status = 'absent' THEN 1 ELSE 0 END) as absent_count"),
                DB::raw("SUM(CASE WHEN attendance_records.status = 'late' THEN 1 ELSE 0 END) as late_count"),
                DB::raw('COUNT(*) as total_sessions'),
            ])
            ->join('attendances', 'attendance_records.attendance_id', '=', 'attendances.id')
            ->where('attendance_records.student_id', $studentId)
            ->when($termId, fn ($q) => $q->where('attendances.term_id', $termId))
            ->first();
    }
}
