<?php

namespace App\Services\Administrator;

use App\Models\Student;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class AdministratorEnrollmentStatusService
{
    public function __construct(
        protected AdministratorSharedService $shared,
        protected Student $studentModel,
        protected User $userModel,
        protected AdministratorEnrollmentRefundService $refundService,
        protected AdministratorEnrollmentEventService $eventService,
    ) {
    }

    public function updateStatuses(array $enrollmentStatuses, int $editorUserId): array
    {
        $schoolYear = $this->shared->getSchoolYear();
        $semester = $this->shared->getSemester();

        try {
            if (empty($enrollmentStatuses)) {
                return [
                    'success' => false,
                    'message' => 'No enrollment statuses were submitted.',
                    'status' => 422,
                ];
            }

            DB::beginTransaction();

            $errors = [];
            $groupsByParentStatus = [];
            $parentInfo = [];
            $refundParents = [];

            $validStatuses = [
                'admission under review',
                'payment pending',
                'enrolled',
                'withdraw under review',
                'refund pending',
                'withdrawn',
                'denied',
                'waitlist',
            ];

            foreach ($enrollmentStatuses as $studentId => $newEnrollmentStatus) {
                $studentId = (int) $studentId;

                if (!in_array($newEnrollmentStatus, $validStatuses, true)) {
                    $errors[] = "Invalid enrollment status '{$newEnrollmentStatus}' for student ID {$studentId}.";
                    continue;
                }

                $result = $this->upsertSingleStatus(
                    $studentId,
                    $newEnrollmentStatus,
                    $schoolYear,
                    $semester,
                    $parentInfo,
                    $groupsByParentStatus,
                    $refundParents
                );

                if (!$result['success']) {
                    $errors[] = $result['message'];
                }
            }

            $refundResult = $this->refundService->processRefunds(
                array_keys($refundParents),
                $schoolYear,
                $editorUserId
            );

            $errors = array_merge($errors, $refundResult['errors']);

            DB::commit();

            $this->eventService->dispatchGroupedEvents(
                $groupsByParentStatus,
                $parentInfo,
                $refundResult['refundAmountByParent'],
                $schoolYear
            );

            return [
                'success' => empty($errors),
                'message' => empty($errors)
                    ? 'Enrollment statuses updated and notifications sent.'
                    : implode(' ', $errors),
                'status' => empty($errors) ? 200 : 207,
            ];
        } catch (\Throwable $e) {
            DB::rollBack();
            Log::error('Enrollment withdrawal error: ' . $e->getMessage());

            return [
                'success' => false,
                'message' => 'An unexpected error occurred while processing enrollments.',
                'status' => 500,
            ];
        }
    }

    protected function upsertSingleStatus(
        int $studentId,
        string $newEnrollmentStatus,
        string $schoolYear,
        string $semester,
        array &$parentInfo,
        array &$groupsByParentStatus,
        array &$refundParents
    ): array {
        $admissionStatus = match ($newEnrollmentStatus) {
            'denied' => 'denied',
            'enrolled', 'payment pending' => 'accepted',
            default => 'pending',
        };

        $enrollmentRow = DB::table('enrollments')
            ->where('student_id', $studentId)
            ->where('school_year', $schoolYear)
            ->first();

        if (!$enrollmentRow) {
            return $this->createEnrollmentRow(
                $studentId,
                $newEnrollmentStatus,
                $admissionStatus,
                $schoolYear,
                $semester,
                $parentInfo,
                $groupsByParentStatus,
                $refundParents
            );
        }

        $oldStatus = $enrollmentRow->enrollment_status ?? null;
        $parentId = (int) ($enrollmentRow->parent_id ?? 0);

        if (!$parentId) {
            return [
                'success' => false,
                'message' => "No parent ID found for student ID {$studentId}.",
            ];
        }

        if ($oldStatus === $newEnrollmentStatus) {
            return ['success' => true, 'message' => 'No change'];
        }

        $updated = DB::table('enrollments')
            ->where('student_id', $studentId)
            ->where('school_year', $schoolYear)
            ->update([
                'enrollment_status' => $newEnrollmentStatus,
                'admission_status' => $admissionStatus,
                'updated_at' => now(),
            ]);

        if (!$updated) {
            return [
                'success' => false,
                'message' => "Failed to update enrollment for student ID {$studentId}.",
            ];
        }

        $studentName = $this->resolveStudentName($studentId);
        $this->ensureParentInfoLoaded($parentId, $parentInfo);

        $groupsByParentStatus[$parentId][$newEnrollmentStatus][] = [
            'student_id' => $studentId,
            'student_name' => $studentName,
        ];

        if ($newEnrollmentStatus === 'refund pending') {
            $refundParents[$parentId] = true;
        }

        return ['success' => true, 'message' => 'Updated'];
    }

    protected function createEnrollmentRow(
        int $studentId,
        string $newEnrollmentStatus,
        string $admissionStatus,
        string $schoolYear,
        string $semester,
        array &$parentInfo,
        array &$groupsByParentStatus,
        array &$refundParents
    ): array {
        $stu = $this->studentModel->find($studentId);
        $stu = is_array($stu) ? $stu : ($stu?->toArray() ?? []);

        $parentId = (int) ($stu['parent_id'] ?? ($stu['secondparent_user_id'] ?? 0));
        if (!$parentId) {
            return [
                'success' => false,
                'message' => "No parent ID found for student ID {$studentId}.",
            ];
        }

        $isWithdrawn = in_array($newEnrollmentStatus, ['withdrawn', 'refund pending', 'withdraw under review'], true) ? 1 : 0;

        $ok = DB::table('enrollments')->insert([
            'student_id' => $studentId,
            'parent_id' => $parentId,
            'school_year' => $schoolYear,
            'semester' => $semester,
            'enrollment_date' => now()->toDateString(),
            'is_withdrawn' => $isWithdrawn,
            'enrollment_status' => $newEnrollmentStatus,
            'admission_status' => $admissionStatus,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        if (!$ok) {
            return [
                'success' => false,
                'message' => "Failed to create enrollment for student ID {$studentId}.",
            ];
        }

        $studentName = $this->resolveStudentName($studentId);
        $this->ensureParentInfoLoaded($parentId, $parentInfo);

        $groupsByParentStatus[$parentId][$newEnrollmentStatus][] = [
            'student_id' => $studentId,
            'student_name' => $studentName,
        ];

        if ($newEnrollmentStatus === 'refund pending') {
            $refundParents[$parentId] = true;
        }

        return ['success' => true, 'message' => 'Created'];
    }

    protected function resolveStudentName(int $studentId): string
    {
        $studentRow = $this->studentModel->find($studentId);
        $studentData = is_array($studentRow) ? $studentRow : ($studentRow?->toArray() ?? []);

        return trim(($studentData['firstname'] ?? '') . ' ' . ($studentData['lastname'] ?? ''))
            ?: "Student #{$studentId}";
    }

    protected function ensureParentInfoLoaded(int $parentId, array &$parentInfo): void
    {
        if (isset($parentInfo[$parentId])) {
            return;
        }

        $p = $this->userModel->find($parentId);
        $parentData = is_array($p) ? $p : ($p?->toArray() ?? []);

        $parentInfo[$parentId] = [
            'user_id' => $parentData['id'] ?? $parentId,
            'email' => $parentData['email'] ?? null,
            'firstname' => $parentData['firstname'] ?? '',
            'lastname' => $parentData['lastname'] ?? '',
        ];
    }
}
