<?php

namespace App\Services;

use App\Repositories\Interfaces\BookingRepositoryInterface;
use App\Repositories\Interfaces\PayoutSettingRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalDocumentRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalEducationRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalOfficeRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalScheduleRepositoryInterface;
use App\Repositories\Interfaces\ProfessionalSpecialtyRepositoryInterface;
use App\Repositories\Interfaces\ProfileRepositoryInterface;
use App\Repositories\Interfaces\RatingRepositoryInterface;
use App\Repositories\Interfaces\ScheduleOverrideRepositoryInterface;
use App\Repositories\Interfaces\UserRepositoryInterface;
use Carbon\Carbon;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

class ProfessionalService
{
    /**
     * @var UserRepositoryInterface
     */
    protected $userRepository;

    /**
     * @var ProfileRepository
     */
    protected $profileRepository;

    /**
     * @var ProfessionalRepositoryInterface
     */
    protected $professionalRepository;

    /**
     * @var ProfessionalOfficeRepositoryInterface
     */
    protected $professionalOfficeRepository;

    /**
     * @var ProfessionalScheduleRepositoryInterface
     */
    protected $professionalScheduleRepository;

    /**
     * @var ProfessionalDocumentRepositoryInterface
     */
    protected $professionalDocumentRepository;

    protected $professionalEducationRepository;

    /**
     * @var ProfessionalSpecialtyRepositoryInterface
     */
    protected $professionalSpecialtyRepository;

    /**
     * @var ScheduleOverrideRepositoryInterface
     */
    protected $scheduleOverrideRepository;

    /**
     * @var PayoutSettingRepositoryInterface
     */
    protected $payoutSettingRepository;

    /**
     * @var RatingRepositoryInterface
     */
    protected $ratingRepository;

    /**
     * @var BookingRepositoryInterface
     */
    protected $bookingRepository;

    /**
     * @var UploadService
     */
    protected $uploadService;

    /**
     * @var EmailService
     */
    protected $emailService;

    /**
     * @var AdvanceSearchService
     */
    protected $advanceSearchService;

    protected $adminNotificationService;

    private $response = [
        'success' => true,
        'message' => null,
    ];

    public function __construct(
        UserRepositoryInterface $userRepository,
        ProfileRepositoryInterface $profileRepository,
        ProfessionalRepositoryInterface $professionalRepository,
        ProfessionalOfficeRepositoryInterface $professionalOfficeRepository,
        ProfessionalScheduleRepositoryInterface $professionalScheduleRepository,
        ProfessionalDocumentRepositoryInterface $professionalDocumentRepository,
        ProfessionalEducationRepositoryInterface $professionalEducationRepository,
        ProfessionalSpecialtyRepositoryInterface $professionalSpecialtyRepository,
        ScheduleOverrideRepositoryInterface $scheduleOverrideRepository,
        PayoutSettingRepositoryInterface $payoutSettingRepository,
        RatingRepositoryInterface $ratingRepository,
        UploadService $uploadService,
        EmailService $emailService,
        AdvanceSearchService $advanceSearchService,
        BookingRepositoryInterface $bookingRepository,
        AdminNotificationService $adminNotificationService,
    ) {
        $this->userRepository = $userRepository;
        $this->profileRepository = $profileRepository;
        $this->professionalRepository = $professionalRepository;
        $this->professionalOfficeRepository = $professionalOfficeRepository;
        $this->professionalScheduleRepository = $professionalScheduleRepository;
        $this->professionalDocumentRepository = $professionalDocumentRepository;
        $this->professionalEducationRepository = $professionalEducationRepository;
        $this->professionalSpecialtyRepository = $professionalSpecialtyRepository;
        $this->scheduleOverrideRepository = $scheduleOverrideRepository;
        $this->payoutSettingRepository = $payoutSettingRepository;
        $this->ratingRepository = $ratingRepository;
        $this->uploadService = $uploadService;
        $this->emailService = $emailService;
        $this->advanceSearchService = $advanceSearchService;
        $this->bookingRepository = $bookingRepository;
        $this->adminNotificationService = $adminNotificationService;
    }

    public function getProfileInformation(string $userUuid): array
    {
        $user = $this->userRepository->getByColumn('uuid', $userUuid);
        $professional = $user?->professional;
        if (! $professional) {
            return ['success' => false, 'message' => 'Professional not found.', 'status' => 404];
        }

        return ['success' => true, 'message' => 'Data retrieved.', 'data' => [
            'bio' => $professional->bio ?? '',
            'specialty_ids' => $professional->specialties->pluck('specialty_id')->filter()->map(fn ($id) => (int) $id)->values(),
            'educations' => $this->professionalEducationRepository->forProfessional($professional->id),
        ]];
    }

    public function updateProfileInformation(string $userUuid, array $payload): array
    {
        $user = $this->userRepository->getByColumn('uuid', $userUuid);
        $professional = $user?->professional;
        if (! $professional) {
            return ['success' => false, 'message' => 'Professional not found.', 'status' => 404];
        }

        if (! Schema::hasTable('professional_educations')) {
            return [
                'success' => false,
                'message' => 'Professional education storage is not installed. Run php artisan migrate on the API server.',
                'status' => 503,
            ];
        }

        validator($payload, [
            'bio' => ['nullable', 'string', 'max:3000'],
            'specialty_ids' => ['array', 'max:30'],
            'specialty_ids.*' => ['required', 'integer', 'distinct', Rule::exists('professions', 'id')->where('parent_id', $professional->profession_id)],
            'educations' => ['array', 'max:20'],
            'educations.*.school' => ['required', 'string', 'max:255'],
            'educations.*.degree' => ['required', 'string', 'max:255'],
            'educations.*.field_of_study' => ['nullable', 'string', 'max:255'],
            'educations.*.start_year' => ['nullable', 'integer', 'min:1900', 'max:'.(date('Y') + 10)],
            'educations.*.end_year' => ['nullable', 'integer', 'min:1900', 'max:'.(date('Y') + 10)],
        ])->validate();

        try {
            DB::transaction(function () use ($professional, $user, $payload) {
                $this->professionalRepository->update(['bio' => trim((string) ($payload['bio'] ?? '')) ?: null], $professional->id);
                $this->professionalSpecialtyRepository->syncForProfessional($user->id, $professional->id, $payload['specialty_ids'] ?? []);
                $this->professionalEducationRepository->replaceForProfessional($professional->id, $payload['educations'] ?? []);
            });
        } catch (Exception $exception) {
            app('log')->error('Unable to update professional information.', [
                'professional_id' => $professional->id,
                'error' => $exception->getMessage(),
            ]);

            $migrationMissing = str_contains($exception->getMessage(), 'custom_name')
                || str_contains($exception->getMessage(), 'professional_educations');
            $message = $migrationMissing
                ? 'Professional profile database migrations have not been applied.'
                : 'Unable to save professional information. Please try again.';
            if (config('app.debug')) {
                $message .= ' '.$exception->getMessage();
            }

            return [
                'success' => false,
                'message' => $message,
                'status' => 500,
            ];
        }

        return ['success' => true, 'message' => 'Professional information updated.'];
    }

    public function submitApplicationForm(Request $request): array
    {
        try {
            DB::beginTransaction();

            $payload = $request->all();

            $user = $this->userRepository->getByColumn('email', $payload['email']);

            if (! $user) {
                DB::rollBack();
                return ['success' => false, 'message' => 'Your account could not be found. Please sign in again.', 'status' => 404];
            }

            $isExisting = $this->professionalRepository->getByColumn('user_id', $user->id);

            if ($isExisting) {
                $message = match ($isExisting->status) {
                    'pending' => 'Has pending application',
                    'resubmit' => 'Has pending application',
                    'approved' => 'Professional already exist',
                    'declined' => 'Application has been declined',
                    default => 'An application already exists for this account',
                };
                $this->response['success'] = false;
                $this->response['message'] = $message;
                $this->response['status'] = 400;

                DB::rollBack();
                return $this->response;
            }

            $professionalUuid = Str::uuid();

            $professionalPayload = [
                'user_id' => $user->id,
                'professional_uuid' => $professionalUuid,
                'profession_id' => $payload['profession_id'],
                'default_rate' => $payload['default_rate'],
                'has_applied' => true,
            ];

            $professional = $this->professionalRepository->create($professionalPayload);

            $officePayload = [
                'user_id' => $user->id,
                'professional_id' => $professional->id,
                'address' => $payload['address'] ?? null,
                'building_name' => $payload['building_name'] ?? null,
                'country' => $payload['country'],
                'province' => $payload['province'],
                'city' => $payload['city'],
                'zip_code' => $payload['zip_code'],
            ];

            $prcFile = $request->file('prc_id');
            $signatureFile = $request->file('signature');
            $selfiePrcFile = $request->file('selfie_prc');
            $profilePictureFile = $request->file('profile_picture');

            $prcId = $this->uploadService->processAttachments($prcFile, 'professionals/'.$professionalUuid);
            $signature = $this->uploadService->processAttachments($signatureFile, 'professionals/'.$professionalUuid);
            $selfiePrc = $this->uploadService->processAttachments($selfiePrcFile, 'professionals/'.$professionalUuid);

            $profilePayload = [
                'first_name' => $payload['first_name'],
                'middle_name' => $payload['middle_name'],
                'last_name' => $payload['last_name'],
                'gender' => $payload['gender'],
                'contact_number' => $payload['contact_number'],
                'image' => $profilePictureFile ?
                    $this->uploadService->processAttachments($profilePictureFile, 'users/'.$user->uuid) : null,
            ];

            $documentPayload = [
                'user_id' => $user->id,
                'professional_id' => $professional->id,
                'prc_number' => $payload['prc_number'],
                'prc_id' => $prcId,
                'signature' => $signature,
                'selfie_prc' => $selfiePrc,
                'admission_date' => $payload['admission_date'],
            ];

            foreach ($payload['schedules'] as $schedule) {
                $schedulePayload = [
                    'user_id' => $user->id,
                    'professional_id' => $professional->id,
                    'day_of_week' => $schedule['day_of_week'],
                    'time_from' => $schedule['time_from'],
                    'time_to' => $schedule['time_to'],
                    'rate' => $schedule['rate'],
                    'is_available' => $schedule['is_available'] ?? true,
                ];
                $this->professionalScheduleRepository->create($schedulePayload);
            }

            if (isset($payload['specialties'])) {
                foreach ($payload['specialties'] as $specialtyId) {
                    $specialtyPayload = [
                        'user_id' => $user->id,
                        'professional_id' => $professional->id,
                        'specialty_id' => $specialtyId,
                    ];
                    $this->professionalSpecialtyRepository->create($specialtyPayload);
                }
            }

            $this->profileRepository->update($profilePayload, $user->profile->id);
            $this->professionalOfficeRepository->create($officePayload);
            $this->professionalDocumentRepository->create($documentPayload);

            $emailInfo = [
                'name' => $profilePayload['first_name'],
                'email' => $user->email,
            ];

            $this->adminNotificationService->professionalApplicationSubmitted($professional);

            DB::commit();

            try {
                $this->emailService->sendEmail('professional-application', $emailInfo);
            } catch (\Throwable $e) {
                app('log')->error('Professional application confirmation email failed', [
                    'professional_uuid' => (string) $professionalUuid,
                    'errorMessage' => $e->getMessage(),
                ]);
            }

            try {
                $this->emailService->sendEmail('professional-application-support', [
                    'name' => 'Desxpert Support',
                    'email' => 'support@desxpert.com',
                    'applicant_name' => trim($payload['first_name'].' '.$payload['last_name']),
                    'applicant_email' => $user->email,
                    'application_reference' => (string) $professionalUuid,
                ]);
            } catch (\Throwable $e) {
                app('log')->error('Professional application support email failed', [
                    'professional_uuid' => (string) $professionalUuid,
                    'errorMessage' => $e->getMessage(),
                ]);
            }

            $this->response['message'] = 'Application submitted';

            return $this->response;

        } catch (\Throwable $e) {
            app('log')->error('Professional application submission failed', [
                'professional_uuid' => isset($professionalUuid) ? (string) $professionalUuid : null,
                'errorMessage' => $e->getMessage(),
            ]);
            DB::rollBack();
            $this->response['success'] = false;
            $this->response['message'] = 'We could not save your application. Please try again. If this continues, contact support.';
            $this->response['status'] = 500;

            return $this->response;
        }
    }

    public function updateApplicationStatus(array $payload): array
    {
        try {
            $professional = $this->professionalRepository->getByColumn('professional_uuid', $payload['professional_id']);
            $this->professionalRepository->update(
                ['status' => $payload['status']],
                $professional->id
            );

            $emailPage = match ($payload['status']) {
                'approved' => 'professional-approved',
                'declined' => 'professional-declined'
            };

            $emailInfo = [
                'name' => $professional->user->profile->first_name,
                'email' => $professional->user->email,
            ];

            $userStatus = match ($payload['status']) {
                'approved' => 'active',
                'declined' => 'deactivated'
            };

            $this->userRepository->update(['status' => $userStatus], $professional->user->id);
            $this->emailService->sendEmail($emailPage, $emailInfo);

            $this->response['message'] = 'Application status has been updated';

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';
            $this->response['status'] = 400;

            return $this->response;
        }
    }

    public function getApplicationList(): array
    {
        try {
            $applications = $this->professionalRepository->getApplicationList();
            foreach ($applications as $key => $application) {
                if (isset($application['documents'])) {
                    $professionalUuid = $application->professional_uuid;
                    $applications[$key]['documents']->prc_id_link = $this->uploadService->generatePublicLink(
                        'professionals', ['id' => $professionalUuid, 'column' => $application['documents']['prc_id']]
                    );
                    $applications[$key]['documents']->signature_link = $this->uploadService->generatePublicLink(
                        'professionals', ['id' => $professionalUuid, 'column' => $application['documents']['signature']]
                    );
                    $applications[$key]['documents']->selfie_prc_link = $this->uploadService->generatePublicLink(
                        'professionals', ['id' => $professionalUuid, 'column' => $application['documents']['selfie_prc']]
                    );
                }
            }
            $this->response['message'] = 'Data retrieved';
            $this->response['data'] = $applications;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';

            return $this->response;
        }
    }

    public function updateSchedules(string $uuid, array $payload): array
    {
        try {
            DB::beginTransaction();
            if ($payload) {
                $user = $this->userRepository->getByColumn('uuid', $uuid);
                $professionalId = $user->professional->id;

                $this->professionalScheduleRepository->deleteByColumn('professional_id', $professionalId);

                $schedules = [];

                foreach ($payload['schedules'] as $schedule) {
                    $schedule['user_id'] = $user->id;
                    $schedule['professional_id'] = $professionalId;
                    $schedules[] = $schedule;
                }
                $this->professionalScheduleRepository->bulkInsert($schedules);
            }
            $this->response['message'] = 'Schedule Updated';
            DB::commit();

            return $this->response;
        } catch (Exception $e) {
            dd($e->getMessage());
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';
            DB::rollBack();

            return $this->response;
        }
    }

    public function getOverrideSchedules(string $uuid): array
    {
        try {
            $user = $this->userRepository->getByColumn('uuid', $uuid);
            $schedules = $this->scheduleOverrideRepository->getMultipleValues('professional_id', $user->professional->id);
            foreach ($schedules as $schedule) {
                unset($schedule['user_id']);
                unset($schedule['professional_id']);
            }
            $this->response['message'] = 'Data retrieved';
            $this->response['data'] = $schedules;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';

            return $this->response;
        }
    }

    public function overrideSchedule(string $uuid, array $payload): array
    {
        try {
            DB::beginTransaction();
            if ($payload) {
                $user = $this->userRepository->getByColumn('uuid', $uuid);
                $professionalId = $user->professional->id;
                $payload['user_id'] = $user->id;
                $payload['professional_id'] = $professionalId;
                $isExisting = $this->scheduleOverrideRepository->checkIfHasExistingOverride($payload);

                if ($isExisting) {
                    $this->response['success'] = false;
                    $this->response['message'] = 'There is existing override schedule for the selected date';
                    $this->response['status'] = 400;

                    return $this->response;
                }
                $this->scheduleOverrideRepository->create($payload);
            }
            $this->response['message'] = 'Schedule has been override';
            DB::commit();

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';
            DB::rollBack();

            return $this->response;
        }
    }

    /**
     * @param  string  $id
     */
    public function updateOverrideSchedule(string $uuid, array $payload, int $id): array
    {
        try {
            DB::beginTransaction();
            $user = $this->userRepository->getByColumn('uuid', $uuid);
            $isUserSchedule = $this->scheduleOverrideRepository->getByColumn('id', $id);
            if ($isUserSchedule->professional_id !== $user->professional->id) {
                $this->response['message'] = 'You are not allowed to update other professional schedule';
                $this->response['status'] = 403;

                return $this->response;
            }
            $this->scheduleOverrideRepository->update($payload, $id);
            $this->response['message'] = 'Schedule has been updated';
            DB::commit();

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';
            DB::rollBack();

            return $this->response;
        }
    }

    /**
     * @param  string  $id
     */
    public function deleteOverrideSchedule(string $uuid, int $id): array
    {
        try {
            DB::beginTransaction();
            $user = $this->userRepository->getByColumn('uuid', $uuid);
            $isUserSchedule = $this->scheduleOverrideRepository->getByColumn('id', $id);
            if ($isUserSchedule->professional_id !== $user->professional->id) {
                $this->response['message'] = 'You are not allowed to delete other professional schedule';
                $this->response['status'] = 403;

                return $this->response;
            }
            $this->scheduleOverrideRepository->delete($id);
            $this->response['message'] = 'Schedule has been deleted';
            DB::commit();

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';
            DB::rollBack();

            return $this->response;
        }
    }

    public function getPayoutSetting(string $uuid): array
    {
        try {
            $user = $this->userRepository->getByColumn('uuid', $uuid);
            $settings = $this->payoutSettingRepository->getByColumn('professional_id', $user->professional->id);
            $this->response['message'] = 'Data retrieved';
            $this->response['data'] = $settings;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';

            return $this->response;
        }
    }

    public function storePayoutSetting(string $uuid, array $payload): array
    {
        try {
            DB::beginTransaction();
            $user = $this->userRepository->getByColumn('uuid', $uuid);
            $professionalId = $user->professional->id;
            $isExisting = $this->payoutSettingRepository->getByColumn('professional_id', $professionalId);
            if (! $isExisting) {
                $payload['user_id'] = $user->id;
                $payload['professional_id'] = $professionalId;
                $this->payoutSettingRepository->create($payload);
            } else {
                $this->payoutSettingRepository->update($payload, $isExisting->id);
            }
            $this->response['message'] = 'Payout setting has been updated';
            DB::commit();

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Bad Request';
            DB::rollBack();

            return $this->response;
        }
    }

    public function searchProfessionals(): array
    {
        try {
            $filters = request()->query('q') ?? [];
            $type = 'search';
            $results = [];
            if ($filters) {
                $decodedFilter = $this->advanceSearchService->decodeQueryParameters($filters);
                $type = $decodedFilter[0]['type'];
                unset($decodedFilter[0]);
                $filters = $this->advanceSearchService->validateQueryParameters($decodedFilter);
            }
            if ($type === 'today') {
                $dateNow = now();
                $dateToday = $dateNow->format('Y-m-d');
                $dayOfWeek = $dateNow->format('l');
                $withOverrides = $this->scheduleOverrideRepository->getMultipleValues('date', $dateToday);

                $overrideAvailable = [];
                $overrideNotAvailable = [];

                foreach ($withOverrides as $withOverride) {
                    $withOverride->is_available ? $overrideAvailable[] = $withOverride->professional_id : $overrideNotAvailable[] = $withOverride->professional_id;
                }

                $availableToday = $this->professionalScheduleRepository->getAvailableToday($dayOfWeek);
                $availableToday = array_merge($availableToday, $overrideAvailable);
                $filters[] = [
                    'key' => 'professional_id',
                    'condition' => 'in',
                    'value' => $availableToday,
                ];

                $filters[] = [
                    'key' => 'professional_id',
                    'condition' => 'notIn',
                    'value' => $overrideNotAvailable,
                ];
            }
            $results = $this->professionalRepository->searchProfessional($filters);

            $results['records']->each(function ($professional) {
                $professional->rate_ave = $professional->rate_ave !== null
                    ? (float) $professional->rate_ave
                    : null;
                $professional->rev_count = (int) $professional->rev_count;
                $professional->image = $professional->image
                    ? $this->uploadService->generatePublicLink('users', [
                        'id' => $professional->user_id,
                        'column' => $professional->image,
                    ])
                    : null;
                unset($professional->user_id);
            });

            $this->response['message'] = 'Data retrieved';
            $this->response['data'] = $results;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Error retrieving data';

            return $this->response;
        }
    }

    public function viewProfessionalDetails(string $professionalId): array
    {
        try {
            $result = $this->professionalRepository->viewProfessionalDetails($professionalId);

            $refinedResult = $this->refineViewProfessionalResult($result);
            $this->response['message'] = 'Data retrieved';
            $this->response['data'] = $refinedResult;

            return $this->response;
        } catch (Exception $e) {
            app('log')->error('Error', ['errorMessage' => $e->getMessage()]);
            $this->response['success'] = false;
            $this->response['message'] = 'Error retrieving data';

            return $this->response;
        }
    }

    /**
     * @param  mixed  $result
     */
    private function refineViewProfessionalResult($result): array
    {
        $specialties = [];
        $ratings = [];
        $rateSum = 0;
        $rateCount = 0;

        foreach ($result->specialties as $specialty) {
            $specialtyName = $specialty->custom_name ?: $specialty->specialty?->name;
            if ($specialtyName) {
                $specialties[] = $specialtyName;
            }
        }

        $specialties = array_values(array_unique($specialties));

        foreach ($result->ratings as $rate) {
            $ratings[] = [
                'name' => $rate->rateBy->profile->full_name,
                'image' => $rate->rateBy?->profile?->image
                    ? $this->uploadService->generatePublicLink('users', ['id' => $rate->rate_by, 'column' => $rate->rateBy->profile->image])
                    : null,
                'rate' => $rate->rate,
                'remarks' => $rate->remarks,
                'created_at' => optional($rate->created_at)->toISOString(),
            ];
            $rateCount++;
            $rateSum += $rate->rate;
        }

        $rateCount = $rateCount > 0 ? $rateCount : 1;
        $refinedResult = [
            'professional_uuid' => $result->professional_uuid,
            'image' => $result->user?->profile?->image
                ? $this->uploadService->generatePublicLink('users', [
                    'id' => $result->user_id,
                    'column' => $result->user->profile->image,
                ])
                : null,
            'bio' => $result->bio,
            'specialties' => $specialties,
            'educations' => $result->educations->toArray(),
            'ratings' => $ratings,
            'rateAverage' => number_format($rateSum / $rateCount, 1),
        ];

        return $refinedResult;
    }

    public function getUnreleasedEarnings(string $userUuid): array
    {
        $user = $this->userRepository->getByColumn('uuid', $userUuid);
        $professional = $user ? $this->professionalRepository->getByColumn('user_id', $user->id) : null;
        if (! $professional) {
            return ['success' => false, 'message' => 'Professional profile not found'];
        }

        $bookings = $this->bookingRepository->getUnreleasedEarnings($professional->id);

        return ['success' => true, 'message' => 'Data retrieved', 'data' => [
            'total' => (float) $bookings->sum('amount'),
            'bookings' => $bookings->map(fn ($booking) => [
                'booking_id' => $booking->uuid,
                'client_name' => $booking->client?->profile?->full_name ?? 'Client',
                'client_image' => $booking->client?->profile?->image
                    ? $this->uploadService->generatePublicLink('users', ['id' => $booking->client_id, 'column' => $booking->client->profile->image]) : null,
                'date' => $booking->date,
                'time' => $booking->time,
                'type' => $booking->type,
                'amount' => (float) $booking->amount,
                'closed_at' => optional($booking->consultation_closed_at)->toISOString(),
            ])->values(),
        ]];
    }

    public function getScheduleOfTheDay(string $professionalId, string $date)
    {
        $timeSlots = [];
        $timezone = config('desxpert.booking_timezone');
        $requestedDate = Carbon::createFromFormat('Y-m-d', $date, $timezone)->startOfDay();

        if ($requestedDate->lessThan(Carbon::tomorrow($timezone))) {
            $this->response['success'] = true;
            $this->response['message'] = 'Scheduled appointments are available from tomorrow onwards';
            $this->response['data'] = null;

            return $this->response;
        }

        $professional = $this->professionalRepository->getByColumn('professional_uuid', $professionalId);

        $bookings = $this->bookingRepository->getBookingByDate($professional->id, $date);
        $day = Carbon::parse($date)->dayName;

        $scheduleOverride = $this->scheduleOverrideRepository->getOverrideByDate($professional->id, $date);
        if ($scheduleOverride) {
            if (! $scheduleOverride->is_available) {
                $this->response['success'] = true;
                $this->response['message'] = 'Data retrieved';
                $this->response['data'] = null;

                return $this->response;
            }
            $schedule = $scheduleOverride;
        } else {
            $schedule = $this->professionalScheduleRepository->getScheduleOfTheDay($professional->id, $day);
        }

        if (! $schedule) {
            $this->response['success'] = true;
            $this->response['message'] = 'Data retrieved';
            $this->response['data'] = null;

            return $this->response;
        }

        $timeSlots['time_slots'] = $this->plotTimeSlots($schedule);
        $timeSlots['rate'] = $schedule->rate;

        // Preserve accepted appointments when new hours shift the slot grid.
        foreach ($timeSlots['time_slots'] as $key => $slot) {
            $slotStart = Carbon::parse("{$date} {$slot['time']}", $timezone);
            $slotEnd = $slotStart->copy()->addMinutes(30);
            foreach ($bookings as $booking) {
                $bookingStart = Carbon::parse("{$date} {$booking->time}", $timezone);
                $bookingEnd = $bookingStart->copy()->addMinutes(30);
                if ($slotStart->lt($bookingEnd) && $slotEnd->gt($bookingStart)) {
                    $timeSlots['time_slots'][$key]['is_available'] = 0;
                    break;
                }
            }
        }

        if ($requestedDate->isToday()) {
            $now = Carbon::now($timezone);
            foreach ($timeSlots['time_slots'] as $key => $slot) {
                $slotTime = Carbon::createFromFormat('Y-m-d H:i', "{$date} {$slot['time']}", $timezone);
                if (! $slotTime->isFuture()) {
                    $timeSlots['time_slots'][$key]['is_available'] = 0;
                }
            }
        }

        $this->response['message'] = 'Data retrieved';
        $this->response['data'] = $timeSlots;

        return $this->response;
    }

    /**
     * @param  mixed  $schedule
     */
    private function plotTimeSlots($schedule): array
    {
        $startTime = Carbon::parse($schedule->time_from);
        $endTime = Carbon::parse($schedule->time_to);

        $slots = [];
        for ($date = $startTime->copy(); $date->copy()->addMinutes(30)->lte($endTime); $date->addMinutes(30)) {
            $slots[] = [
                'time' => $date->format('H:i'),
                'is_available' => 1,
            ];
        }

        return $slots;
    }
}
