<?php

namespace FluentBooking\App\Http\Controllers;

use FluentBooking\App\Models\Calendar;
use FluentBooking\App\Models\CalendarSlot;
use FluentBooking\App\Services\Helper;
use FluentBooking\App\Services\LandingPage\LandingPageHelper;
use FluentBooking\App\Services\PermissionManager;
use FluentBooking\App\Services\AvailabilityService;
use FluentBooking\App\Services\SanitizeService;
use FluentBooking\App\Services\CalendarService;
use FluentBooking\App\Services\OnboardingService;
use FluentBooking\App\Services\CalendarEventService;
use FluentBooking\App\Services\BookingFieldService;
use FluentBooking\App\Hooks\Handlers\AdminMenuHandler;
use FluentBooking\Framework\Http\Request\Request;
use FluentBooking\Framework\Support\Arr;

class CalendarController extends Controller
{
    public function getAllCalendars(Request $request)
    {
        do_action('fluent_booking/before_get_all_calendars', $request);

        $search = sanitize_text_field(Arr::get($request->get('query'), 'search'));
        $calendarType = sanitize_text_field(Arr::get($request->get('query'), 'calendarType'));

        $applySearchFilter = function($query) use ($search) {
            $query->where('status', '!=', 'expired');
            if (!empty($search)) {
                $query->where('title', 'LIKE', '%' . $search . '%');
            }
        };

        $calendarsQuery = Calendar::with(['slots' => function($query) use ($applySearchFilter) {
            $query->where($applySearchFilter);
        }])
        ->where('status', '!=', 'expired');

        if (!empty($search)) {
            $calendarsQuery->whereHas('slots', $applySearchFilter);
        }

        if (!empty($calendarType) && $calendarType != 'all') {
            $calendarsQuery->where('type', $calendarType);
        }

        $calendarsQuery = $calendarsQuery->latest();

        $hasPermission = PermissionManager::hasAllCalendarAccess(true);

        if (!$hasPermission) {
            $attachedCalendarIds = CalendarService::getAttachedCalendarIds($calendarsQuery);
            $calendarsQuery->whereIn('id', $attachedCalendarIds);
        }

        $calendars = $calendarsQuery->paginate();

        foreach ($calendars as $calendar) {
            $calendar->author_profile = $calendar->getAuthorProfile();
            $calendar->public_url = $calendar->getLandingPageUrl();
            $calendar->event_order = $calendar->getMeta('event_order');
            foreach ($calendar->slots as $key => $slot) {
                if (!$hasPermission && !CalendarEventService::isSharedCalendarEvent($slot)) {
                    unset($calendar->slots[$key]);
                }
                $slot->shortcode = '[fluent_booking id="' . $slot->id . '"]';
                $slot->public_url = $slot->getPublicUrl();
                $slot->duration = $slot->getDefaultDuration();
                $slot->price_total = $slot->getPricingTotal();
                $slot->location_fields = $slot->getLocationFields();
                $slot->author_profiles = $slot->isMultiHostEvent() ? $slot->getAuthorProfiles() : [];
                do_action_ref_array('fluent_booking/calendar_slot', [&$slot]);
            }

            if(empty($calendar->author_profile['ID'])) {
                $calendar->generic_error = '<p style="color: red; margin:0;">Connected Host user is missing</p>';
            }

            do_action_ref_array('fluent_booking/calendar', [&$calendar, 'lists']);
        }

        $data = [
            'calendars' => $calendars
        ];

        if (in_array('calendar_event_lists', $request->get('with', []))) {
            $data['calendar_event_lists'] = [
                'hosts'  => CalendarService::getCalendarOptionsByTitle('only_hosts'),
                'teams'  => CalendarService::getCalendarOptionsByTitle('only_teams'),
                'events' => CalendarService::getCalendarOptionsByTitle('only_events')
            ];
        }

        return $data;
    }

    public function checkSlug(Request $request)
    {
        $slug = sanitize_text_field(trim($request->get('slug')));

        if (!Helper::isCalendarSlugAvailable($slug, true)) {
            return $this->sendError([
                'message' => __('The provided slug is not available. Please choose a different one', 'fluent-booking')
            ], 422);
        }

        return [
            'status' => true
        ];
    }

    public function createCalendar(Request $request)
    {
        $data = $request->get('calendar');

        $rules = [
            'author_timezone'                            => 'required',
            'slot.duration'                              => 'required|numeric|min:5',
            'slot.event_type'                            => 'required',
            'slot.availability_type'                     => 'required',
            'slot.schedule_type'                         => 'required',
            'slot.title'                                 => 'required',
            'slot.weekly_schedules'                      => 'required_if:slot.schedule_type,weekly_schedules',
            'slot.location_settings.*.type'              => 'required',
            'slot.location_settings.*.host_phone_number' => 'required_if:location_settings.*.type,phone_organizer'
        ];

        $messages = [
            'author_timezone.required'                               => __('Author timezone field is required', 'fluent-booking'),
            'slot.duration.required'                                 => __('Event duration field is required', 'fluent-booking'),
            'slot.event_type.required'                               => __('Event type field is required', 'fluent-booking'),
            'slot.availability_type.required'                        => __('Event availability type field is required', 'fluent-booking'),
            'slot.schedule_type.required'                            => __('Event schedule type field is required', 'fluent-booking'),
            'slot.title.required'                                    => __('Event title field is required', 'fluent-booking'),
            'slot.weekly_schedules.required_if'                      => __('Event weekly schedules field is required', 'fluent-booking'),
            'slot.location_settings.*.type.required'                 => __('Event location type field is required', 'fluent-booking'),
            'slot.location_settings.*.host_phone_number.required_if' => __('Event location host phone number field is required', 'fluent-booking')
        ];

        $validationConfig = apply_filters('fluent_booking/create_calendar_validation_rule', [
            'rules'    => $rules,
            'messages' => $messages
        ], $data);

        $this->validate($data, $validationConfig['rules'], $validationConfig['messages']);

        do_action('fluent_booking/before_create_calendar', $data, $this);

        if (!empty($data['user_id']) && PermissionManager::userCan('invite_team_members')) {
            $user = get_user_by('ID', $data['user_id']);
        } else {
            $user = get_user_by('ID', get_current_user_id());
        }

        if (!$user) {
            return $this->sendError([
                'message' => __('User not found', 'fluent-booking')
            ], 422);
        }

        $onboardinFeatures = $request->get('features');
        if (!empty($onboardinFeatures)) {
            $installableAddons = SanitizeService::sanitizeAddons($onboardinFeatures);
            OnboardingService::installAddons($installableAddons);
        }

        $type = sanitize_text_field(Arr::get($data, 'type', 'simple'));

        $isHostCalendar = $type == 'simple' ? true : false;

        if ($isHostCalendar && Calendar::where('user_id', $user->ID)->where('type', 'simple')->first()) {
            return $this->sendError([
                'message' => __('The user already have a calendar. Please delete it first to create a new one', 'fluent-booking')
            ], 422);
        }

        if ($isHostCalendar) {
            $userName = $user->user_login;
            if (is_email($userName)) {
                $userName = explode('@', $userName);
                $userName = $userName[0] . '-' . time();
            }
            $data['slug'] = sanitize_title($userName, '', 'display');
        }
        
        $slot = $data['slot'];

        if (!$isHostCalendar) {
            $title = sanitize_text_field(Arr::get($data, 'title', ''));
            $data['slug'] = sanitize_title($title, '', 'display');
            $teamMembers = array_map('intval', Arr::get($slot, 'settings.team_members', []));
            if (!in_array($user->ID, $teamMembers)) {
                $user = get_user_by('ID', reset($teamMembers));
                if (!$user) {
                    return $this->sendError([
                        'message' => __('Invalid Team Member', 'fluent-booking')
                    ], 422);
                }
            }
        }

        if (!Helper::isCalendarSlugAvailable($data['slug'], true)) {
            $data['slug'] .= '-' . time();
        }

        if (!empty($data['slug'])) {
            $slug = trim(sanitize_text_field($data['slug']));
            if (!Helper::isCalendarSlugAvailable($slug, true)) {
                return $this->sendError([
                    'message' => __('The provided slug is not available. Please choose a different one', 'fluent-booking')
                ], 422);
            }

            $personName = trim($user->first_name . ' ' . $user->last_name);
            if (!$personName) {
                $personName = $user->display_name;
            }

            $calendarData = [
                'slug'            => $slug,
                'user_id'         => $user->ID,
                'title'           => $isHostCalendar ? $personName : $title,
                'type'            => $type,
                'author_timezone' => sanitize_text_field($data['author_timezone']) ?: 'UTC',
            ];
            $calendar = Calendar::create($calendarData);
        } else {
            $calendar = Calendar::where('user_id', $user->ID)->where('type', 'simple')->first();
        }

        if (!$calendar) {
            return $this->sendError([
                'message' => __('Calendar could not be found. Please try again', 'fluent-booking')
            ], 422);
        }

        $weeklySchedule = Arr::get($data, 'slot.weekly_schedules', []);

        $availability = AvailabilityService::maybeCreateAvailability($calendar, $weeklySchedule);

        $title = (!empty($slot['title'])) ? sanitize_text_field($slot['title']) : $slot['duration'] . ' Minute Meeting';

        $slotData = [
            'title'             => $title,
            'slug'              => Helper::generateSlotSlug((int)$slot['duration'] . 'min', $calendar),
            'calendar_id'       => $calendar->id,
            'user_id'           => $calendar->user_id,
            'duration'          => (int)$slot['duration'],
            'description'       => wp_kses_post(Arr::get($slot, 'description')),
            'settings'          => [
                'team_members'     => !$isHostCalendar ? $teamMembers : [],
                'schedule_type'    => sanitize_text_field($slot['schedule_type']),
                'weekly_schedules' => SanitizeService::weeklySchedules($slot['weekly_schedules'], $calendar->author_timezone, 'UTC', true)
            ],
            'status'            => SanitizeService::checkCollection($slot['status'], ['active', 'draft']),
            'color_schema'      => sanitize_text_field(Arr::get($slot, 'color_schema', '#0099ff')),
            'event_type'        => sanitize_text_field(Arr::get($slot, 'event_type')),
            'availability_type' => SanitizeService::checkCollection($slot['availability_type'], ['existing_schedule', 'custom'], 'existing_schedule'),
            'availability_id'   => (int)$availability->id,
            'location_type'     => sanitize_text_field(Arr::get($slot, 'location_type')),
            'location_heading'  => wp_kses_post(Arr::get($slot, 'location_heading')),
            'location_settings' => SanitizeService::locationSettings(Arr::get($slot, 'location_settings', [])),
        ];

        $slotData['settings'] = wp_parse_args($slotData['settings'], (new CalendarSlot())->getSlotSettingsSchema());

        $slotData = apply_filters('fluent_booking/create_calendar_event_data', $slotData, $calendar);

        $slot = CalendarSlot::create($slotData);

        do_action('fluent_booking/after_create_calendar', $calendar);

        do_action('fluent_booking/after_create_calendar_slot', $slot, $calendar);

        return [
            'calendar'     => $calendar,
            'slot'         => $slot,
            'redirect_url' => Helper::getAppBaseUrl('calendars/' . $calendar->id . '/slot-settings/' . $slot->id)
        ];
    }

    public function getCalendar(Request $request, $calendarId)
    {
        $calendar = Calendar::with(['slots' => function ($query) {
            $query->where('status', '!=', 'expired');
        }])->findOrFail($calendarId);

        $calendar->author_profile = $calendar->getAuthorProfile();

        $data = [
            'calendar' => $calendar
        ];

        if (in_array('settings_menu', $request->get('with', []))) {
            $data['settings_menu'] = AdminMenuHandler::getCalendarSettingsMenuItems($calendar);
        }

        return $data;
    }

    public function getSharingSettings(Request $request, $calendarId)
    {
        $calendar = Calendar::findOrFail($calendarId);

        return [
            'settings'  => LandingPageHelper::getSettings($calendar),
            'share_url' => $calendar->getLandingPageUrl(true)
        ];
    }

    public function saveSharingSettings(Request $request, $calendarId)
    {
        $calendar = Calendar::findOrFail($calendarId);

        $calendarDataItems = Arr::only($request->get('calendar_data', []), ['title', 'timezone', 'description', 'calendar_avatar', 'featured_image', 'phone']);

        if ($calendarDataItems) {
            $this->validate($calendarDataItems, [
                'title'           => 'required',
                'calendar_avatar' => 'nullable|url',
                'featured_image'  => 'nullable|url'
            ]);

            $updatedTimezone = sanitize_text_field(Arr::get($calendarDataItems, 'timezone'));
            if ($updatedTimezone && $updatedTimezone != $calendar->author_timezone) {
                CalendarService::updateCalendarEventsSchedule($calendarId, $calendar->author_timezone, $updatedTimezone);
                $calendar->author_timezone = $updatedTimezone;
            }

            $calendar->title = sanitize_text_field(Arr::get($calendarDataItems, 'title'));
            $calendar->description = wp_kses_post(Arr::get($calendarDataItems, 'description'));
            $calendar->updateMeta('profile_photo_url', sanitize_url(Arr::get($calendarDataItems, 'calendar_avatar')));
            $calendar->updateMeta('featured_image_url', sanitize_url(Arr::get($calendarDataItems, 'featured_image')));
            $calendar->save();

            if ($calendar->user) {
                $calendar->user->updateMeta('host_phone', sanitize_text_field(Arr::get($calendarDataItems, 'phone')));
            }
        }

        $sharingSettings = $request->get('landing_page_settings', []);
        LandingPageHelper::updateSettings($calendar, $sharingSettings);

        return [
            'message' => __('Landing Page settings has been updated', 'fluent-booking')
        ];
    }

    public function updateCalendar(Request $request, $calendarId)
    {
        $data = $request->all();

        $calendar = Calendar::findOrFail($calendarId);

        do_action_ref_array('fluent_booking/before_update_calendar', [&$calendar, $data]);

        $calendar->description = wp_kses_post($request->get('description'));
        $calendar->save();
        do_action('fluent_booking/after_update_calendar', $calendar, $data);

        $calendar->author_profile = $calendar->getAuthorProfile();

        do_action_ref_array('fluent_booking/calendar', [&$calendar, 'update']);

        return [
            'calendar' => $calendar,
            'message'  => __('Calendar has been updated successfully', 'fluent-booking')
        ];
    }

    public function getEvent(Request $request, $calendarId, $eventId)
    {
        $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->with(['calendar.user'])->findOrFail($eventId);

        $calendarEvent->author_profile = $calendarEvent->getAuthorProfile();

        $calendarEvent->calendar->author_profile = $calendarEvent->calendar->getAuthorProfile();

        $calendarEvent->public_url = $calendarEvent->getPublicUrl();

        $eventSettings = $calendarEvent->settings;

        $eventSettings['weekly_schedules'] = SanitizeService::weeklySchedules($eventSettings['weekly_schedules'], 'UTC', $calendarEvent->calendar->author_timezone);

        $eventSettings['date_overrides'] = (object)SanitizeService::slotDateOverrides(Arr::get($eventSettings, 'date_overrides', []), 'UTC', $calendarEvent->calendar->author_timezone, $calendarEvent);

        $eventSettings['location_fields'] = $calendarEvent->getLocationFields();

        $eventSettings['hosts_schedules'] = $calendarEvent->getHostsSchedules();

        $calendarEvent->settings = apply_filters('fluent_booking/get_calendar_event_settings', $eventSettings, $calendarEvent, $calendarEvent->calendar);

        $data = [
            'calendar_event' => $calendarEvent
        ];

        if (in_array('calendar', $this->request->get('with', []))) {
            $calendar = $calendarEvent->calendar;
            $calendar->author_profile = $calendar->getAuthorProfile();
            $data['calendar'] = $calendar;
        }

        if (in_array('smart_codes', $this->request->get('with', []))) {
            $data['smart_codes'] = [
                'texts' => Helper::getEditorShortCodes($calendarEvent),
                'html'  => Helper::getEditorShortCodes($calendarEvent, true)
            ];
        }

        if (in_array('settings_menu', $this->request->get('with', []))) {
            $data['settings_menu'] = AdminMenuHandler::getEventSettingsMenuItems($calendarEvent);
        }

        if (in_array('calendar_event_lists', $this->request->get('with', []))) {
            $data['calendar_event_lists'] = CalendarService::getCalendarOptionsByTitle();
        }

        return $data;
    }

    public function getEventSchema(Request $request, $calendarId)
    {
        $calendar = Calendar::findOrFail($calendarId);

        $schema = (new CalendarSlot())->getEventSchema($calendar);

        return [
            'slot' => $schema
        ];
    }

    public function getAvailabilitySettings(Request $request, $calendarId, $eventId)
    {
        $availableSchedules = AvailabilityService::availabilitySchedules();

        $scheduleOptions = AvailabilityService::getScheduleOptions();

        return [
            'schedule_options'    => $scheduleOptions,
            'available_schedules' => $availableSchedules
        ];
    }

    public function createCalendarEvent(Request $request, $calendarId)
    {
        $calendar = Calendar::findOrFail($calendarId);

        $slot = $request->all();

        $rules = [
            'title'                                 => 'required',
            'duration'                              => 'required|numeric|min:5',
            'status'                                => 'required',
            'event_type'                            => 'required',
            'location_settings.*.type'              => 'required',
            'location_settings.*.title'             => 'required_if:location_settings.*.type,custom',
            'location_settings.*.description'       => 'required_if:location_settings.*.type,address_organizer',
            'location_settings.*.host_phone_number' => 'required_if:location_settings.*.type,phone_organizer'
        ];

        $messages = [
            'title.required'                                    => __('Event title field is required', 'fluent-booking'),
            'duration.required'                                 => __('Event duration field is required', 'fluent-booking'),
            'status.required'                                   => __('Event status field is required', 'fluent-booking'),
            'event_type.required'                               => __('Event type field is required', 'fluent-booking'),
            'location_settings.*.type.required'                 => __('Event location type field is required', 'fluent-booking'),
            'location_settings.*.title.required_if'             => __('Event location title field is required', 'fluent-booking'),
            'location_settings.*.description.required_if'       => __('Event location description field is required', 'fluent-booking'),
            'location_settings.*.host_phone_number.required_if' => __('Event location host phone number field is required', 'fluent-booking')
        ];

        $validationConfig = apply_filters('fluent_booking/create_calendar_event_validation_rule', [
            'rules'    => $rules,
            'messages' => $messages
        ], $slot);

        $this->validate($slot, $validationConfig['rules'], $validationConfig['messages']);

        $availability = AvailabilityService::getDefaultSchedule($calendar->user_id);

        $slotData = [
            'title'             => $slot['title'],
            'slug'              => Helper::generateSlotSlug($slot['duration'] . 'min', $calendar),
            'calendar_id'       => $calendar->id,
            'user_id'           => $calendar->user_id,
            'duration'          => (int)$slot['duration'],
            'description'       => wp_kses_post(Arr::get($slot, 'description')),
            'settings'          => [
                'schedule_type'       => sanitize_text_field($slot['settings']['schedule_type']),
                'weekly_schedules'    => SanitizeService::weeklySchedules($slot['settings']['weekly_schedules'], $calendar->author_timezone, 'UTC', true),
                'date_overrides'      => SanitizeService::slotDateOverrides(Arr::get($slot['settings'], 'date_overrides', []), $calendar->author_timezone, 'UTC', null, true),
                'range_type'          => sanitize_text_field(Arr::get($slot['settings'], 'range_type')),
                'range_days'          => (int)(Arr::get($slot['settings'], 'range_days', 60)) ?: 60,
                'range_date_between'  => SanitizeService::rangeDateBetween(Arr::get($slot['settings'], 'range_date_between', ['', ''])),
                'schedule_conditions' => SanitizeService::scheduleConditions(Arr::get($slot['settings'], 'schedule_conditions', [])),
                'buffer_time_before'  => sanitize_text_field(Arr::get($slot['settings'], 'buffer_time_before', '0')),
                'buffer_time_after'   => sanitize_text_field(Arr::get($slot['settings'], 'buffer_time_after', '0')),
                'slot_interval'       => sanitize_text_field(Arr::get($slot['settings'], 'slot_interval', '')),
                'team_members'        => array_map('intval', Arr::get($slot['settings'], 'team_members', []))
            ],
            'status'            => SanitizeService::checkCollection($slot['status'], ['active', 'draft'], 'active'),
            'color_schema'      => sanitize_text_field(Arr::get($slot, 'color_schema', '#0099ff')),
            'event_type'        => sanitize_text_field(Arr::get($slot, 'event_type')),
            'availability_type' => 'existing_schedule',
            'availability_id'   => $availability ? $availability->id : null,
            'location_type'     => sanitize_text_field(Arr::get($slot, 'location_type')),
            'location_settings' => SanitizeService::locationSettings(Arr::get($slot, 'location_settings', [])),
            'max_book_per_slot' => (int)Arr::get($slot, 'max_book_per_slot', 1),
            'is_display_spots'  => (bool)Arr::get($slot, 'is_display_spots', false),
        ];

        $slotData = apply_filters('fluent_booking/create_calendar_event_data', $slotData, $calendar);

        do_action('fluent_booking/before_create_event', $calendar, $slotData);

        $createdSlot = CalendarSlot::create($slotData);

        do_action('fluent_booking/after_create_event', $calendar, $createdSlot);

        $calendar->updateEventOrder($createdSlot->id);

        return [
            'message' => __('New Event Type has been created successfully', 'fluent-booking'),
            'slot'    => $createdSlot
        ];
    }

    public function updateEventDetails(Request $request, $calendarId, $eventId)
    {
        $data = $request->all();

        $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $rules = [
            'title'                                 => 'required',
            'duration'                              => 'required|numeric',
            'location_settings.*.type'              => 'required',
            'location_settings.*.title'             => 'required_if:location_settings.*.type,in_person_organizer',
            'location_settings.*.host_phone_number' => 'required_if:location_settings.*.type,phone_organizer'
        ];

        $messages = [
            'title.required'                                    => __('Event title field is required', 'fluent-booking'),
            'duration.required'                                 => __('Event duration field is required', 'fluent-booking'),
            'location_settings.*.type.required'                 => __('Event location type field is required', 'fluent-booking'),
            'location_settings.*.title.required_if'             => __('Event location title field is required', 'fluent-booking'),
            'location_settings.*.host_phone_number.required_if' => __('Event location host phone number field is required', 'fluent-booking')
        ];

        if ('group' === $event->event_type) {
            $rules = array_merge($rules, [
                'max_book_per_slot' => 'required|numeric|min:1',
                'is_display_spots'  => 'required|min:0|max:1',
            ]);
            $messages = array_merge($messages, [
                'max_book_per_slot.required' => __('Event max book per slot field is required', 'fluent-booking'),
                'is_display_spots.required'  => __('Event is display spots field is required', 'fluent-booking')
            ]);
        } else {
            $rules = array_merge($rules, [
                'multi_duration.default_duration'    => 'required_if:multi_duration.enabled,true',
                'multi_duration.available_durations' => 'required_if:multi_duration.enabled,true'
            ]);
            $messages = array_merge($messages, [
                'multi_duration.default_duration.required_if'    => __('Event default duration is required', 'fluent-booking'),
                'multi_duration.available_durations.required_if' => __('Event available durations is required', 'fluent-booking')
            ]);
        }

        $validationConfig = apply_filters('fluent_booking/update_event_details_validation_rule', [
            'rules'    => $rules,
            'messages' => $messages
        ], $event);

        $this->validate($data, $validationConfig['rules'], $validationConfig['messages']);

        $event->title = sanitize_text_field($data['title']);
        $event->duration = (int)$data['duration'];
        $event->status = SanitizeService::checkCollection($data['status'], ['active', 'draft']);
        $event->color_schema = sanitize_text_field(Arr::get($data, 'color_schema', '#0099ff'));
        $event->description = wp_kses_post(Arr::get($data, 'description'));
        $event->max_book_per_slot = (int)Arr::get($data, 'max_book_per_slot');
        $event->is_display_spots = (bool)Arr::get($data, 'is_display_spots');
        $event->location_settings = SanitizeService::locationSettings(Arr::get($data, 'location_settings', []));

        $event->settings = [
            'multi_duration'  => [
                'enabled'             => Arr::isTrue($data, 'multi_duration.enabled'),
                'default_duration'    => Arr::get($data, 'multi_duration.default_duration', ''),
                'available_durations' => array_map('sanitize_text_field', Arr::get($data, 'multi_duration.available_durations', []))
            ]
        ];

        $event->save();

        do_action('fluent_booking/after_update_event_details', $event);

        return [
            'message' => __('Data has been updated', 'fluent-booking'),
            'event'   => $event
        ];
    }

    public function updateEventAvailability(Request $request, $calendarId, $eventId)
    {
        $data = $request->all();

        $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $eventSettings = [
            'schedule_type'      => sanitize_text_field(Arr::get($data, 'schedule_type')),
            'weekly_schedules'   => SanitizeService::weeklySchedules(Arr::get($data, 'weekly_schedules'), $event->calendar->author_timezone, 'UTC', true),
            'date_overrides'     => SanitizeService::slotDateOverrides(Arr::get($data, 'date_overrides', []), $event->calendar->author_timezone, 'UTC', null, true),
            'range_type'         => sanitize_text_field(Arr::get($data, 'range_type')),
            'range_days'         => (int)(Arr::get($data, 'range_days', 60)) ?: 60,
            'range_date_between' => SanitizeService::rangeDateBetween(Arr::get($data, 'range_date_between', ['', ''])),
            'common_schedule'    => Arr::isTrue($data, 'common_schedule', false)
        ];

        if ($event->isTeamEvent()) {
            $eventSettings['hosts_schedules'] = array_map('intval', array_combine(
                array_map('intval', array_keys(Arr::get($data, 'hosts_schedules', []))),
                array_map('intval', Arr::get($data, 'hosts_schedules', []))
            ));
        }

        $event->settings = $eventSettings;

        $event->availability_id = (int)Arr::get($data, 'availability_id');
        $event->availability_type = SanitizeService::checkCollection(Arr::get($data, 'availability_type'), ['existing_schedule', 'custom']);

        $event->save();

        return [
            'message' => __('Data has been updated', 'fluent-booking'),
            'event'   => $event
        ];
    }

    public function updateEventLimits(Request $request, $calendarId, $eventId)
    {
        $data = $request->all();

        $event = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $event->settings = [
            'schedule_conditions'   => SanitizeService::scheduleConditions(Arr::get($data['settings'], 'schedule_conditions', [])),
            'buffer_time_before'    => sanitize_text_field(Arr::get($data, 'settings.buffer_time_before', '0')),
            'buffer_time_after'     => sanitize_text_field(Arr::get($data, 'settings.buffer_time_after', '0')),
            'slot_interval'         => sanitize_text_field(Arr::get($data, 'settings.slot_interval', '')),
            'booking_frequency'     => [
                'enabled' => Arr::isTrue($data, 'settings.booking_frequency.enabled'),
                'limits'  => $this->sanitize_mapped_data(Arr::get($data, 'settings.booking_frequency.limits'))
            ],
            'booking_duration'      => [
                'enabled' => Arr::isTrue($data, 'settings.booking_duration.enabled'),
                'limits'  => $this->sanitize_mapped_data(Arr::get($data, 'settings.booking_duration.limits'))
            ],
            'lock_timezone'         => [
                'enabled'  => Arr::isTrue($data, 'settings.lock_timezone.enabled'),
                'timezone' => sanitize_text_field(Arr::get($data, 'settings.lock_timezone.timezone'))
            ],
        ];

        $event->save();

        return [
            'message' => __('Data has been updated', 'fluent-booking'),
            'event'   => $event
        ];
    }

    public function patchCalendarEvent(Request $request, $calendarId, $eventId)
    {
        $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $status = $request->get('status');

        if ($status) {
            $slot->status = $status;
            $slot->save();
        }

        return [
            'message' => __('Data has been updated', 'fluent-booking')
        ];

    }

    public function cloneCalendarEvent(Request $request, $calendarId, $eventId)
    {
        $newCalendarId = intval($request->get('new_calendar_id')) ?: $calendarId;

        $calendar = Calendar::findOrFail($newCalendarId);

        $originalEvent = CalendarSlot::with('event_metas')->where('calendar_id', $calendarId)->findOrFail($eventId);

        $clonedEvent = $originalEvent->replicate();

        $clonedEvent->hash = null;

        $clonedEvent->calendar_id = $calendar->id;

        $clonedEvent->user_id = $calendar->user_id;

        $clonedEvent->title = $originalEvent->title . ' (clone)';

        $clonedEvent->slug = Helper::generateSlotSlug($clonedEvent->duration . 'min', $calendar);

        $clonedEvent->save();

        $calendar->updateEventOrder($clonedEvent->id);

        $eventsMeta = $originalEvent->event_metas;

        foreach ($eventsMeta as $meta) {
            $clonedMeta = $meta->replicate();
            $clonedMeta->object_id = $clonedEvent->id;
            $clonedMeta->save();
        }

        return [
            'slot'    => $clonedEvent,
            'message' => __('The Event Type has been cloned successfully', 'fluent-booking')
        ];
    }

    public function saveCalendarEventOrder(Request $request, $calendarId)
    {
        $calendar = Calendar::findOrFail($calendarId);

        $eventOrder = array_map('intval', $request->get('event_order', []));

        $calendar->updateMeta('event_order', array_filter($eventOrder));

        return [
            'calendar' => $calendar,
            'message' => __('Event order has been updated', 'fluent-booking')
        ];
    }

    public function cloneEventEmailNotification(Request $request, $calendarId, $eventId)
    {
        $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $fromEventId = intval($request->get('from_event_id'));

        $fromCalendarEvent = CalendarSlot::findOrFail($fromEventId);

        $notification = $fromCalendarEvent->getNotifications(true);

        $calendarEvent->setNotifications($notification);

        $calendarEvent->save();

        return [
            'message'       => __('The Notification has been cloned successfully', 'fluent-booking'),
            'notifications' => $notification
        ];
    }

    public function getEventEmailNotifications(Request $request, $calendarId, $eventId)
    {
        $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        /*
         * Confirmation Email to Attendee
         * Confirmation Email to Organizer
         * Reminder Email to Attendee [before 1 day, 1 hour, 30 minutes, 5 minutes]
         * Cancelled By Organizer to Attendee
         * Cancelled By Attendee to Organizer
         */
        $data = [
            'notifications' => $calendarEvent->getNotifications(true)
        ];

        if (in_array('smart_codes', $request->get('with', []))) {
            $data['smart_codes'] = [
                'texts' => Helper::getEditorShortCodes($calendarEvent),
                'html'  => Helper::getEditorShortCodes($calendarEvent, true)
            ];
        }

        return $data;
    }

    public function saveEventEmailNotifications(Request $request, $calendarId, $eventId)
    {
        $slot = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $notifications = $request->get('notifications', []);

        $formattedNotifications = [];

        foreach ($notifications as $key => $value) {
            $formattedNotifications[$key] = [
                'title'   => sanitize_text_field(Arr::get($value, 'title')),
                'enabled' => Arr::isTrue($value, 'enabled'),
                'email'   => $this->sanitize_mapped_data(Arr::get($value, 'email')),
                'is_host' => Arr::isTrue($value, 'is_host')
            ];
        }

        $slot->setNotifications($formattedNotifications);

        return [
            'message' => __('Notifications has been saved', 'fluent-booking')
        ];
    }

    public function getEventBookingFields(Request $request, $calendarId, $eventId)
    {
        $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $data = [
            'fields' => $calendarEvent->getBookingFields()
        ];

        if (in_array('smart_codes', $request->get('with', []))) {
            $data['smart_codes'] = [
                'texts' => Helper::getEditorShortCodes($calendarEvent),
                'html'  => Helper::getEditorShortCodes($calendarEvent, true)
            ];
        }

        return $data;
    }

    public function saveEventBookingFields(Request $request, $calendarId, $eventId)
    {
        $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $bookingFields = $request->get('booking_fields');

        $optionRequiredFields = ['dropdown', 'radio', 'checkbox-group', 'multi-select'];

        $formattedFields = [];

        $textFields = ['type', 'name', 'label', 'placeholder', 'limit', 'help_text', 'date_format', 'min_date', 'max_date'];
        $booleanFields = ['enabled', 'required', 'system_defined', 'disable_alter', 'is_sms_number'];

        foreach ($bookingFields as $value) {
            if (empty($value['name'])) {
                $value['name'] = BookingFieldService::generateFieldName($calendarEvent, $value['label']);
            } else {
                $value['name'] = BookingFieldService::maybeGenerateFieldName($calendarEvent, $value);
            }

            $textValues = array_map('sanitize_text_field', Arr::only($value, $textFields));

            $booleanValues = array_map(function ($valueItem) {
                return $valueItem === true || $valueItem === 'true' || $valueItem == 1;
            }, Arr::only($value, $booleanFields));

            $formattedField = array_merge($textValues, $booleanValues);

            $fieldType = Arr::get($value, 'type');

            $formattedField['index'] = (int)Arr::get($value, 'index');
            if (in_array($fieldType, $optionRequiredFields)) {
                $sanitizedOptions = array_map('sanitize_text_field', Arr::get($value, 'options'));
                $formattedField['options'] = $sanitizedOptions;
            }
            if ($fieldType == 'file') {
                $formattedField['max_file_allow'] = intval(Arr::get($value, 'max_file_allow'));
                $formattedField['allow_file_types'] = array_map('sanitize_text_field', Arr::get($value, 'allow_file_types'));
                $formattedField['file_size_value'] = intval(Arr::get($value, 'file_size_value'));
                $formattedField['file_size_unit'] = SanitizeService::checkCollection(Arr::get($value, 'file_size_unit'), ['kb','mb']);
            }
            if ($fieldType == 'hidden') {
                $formattedField['default_value'] = sanitize_text_field(Arr::get($value, 'default_value'));
            }
            if ($fieldType == 'terms-and-conditions') {
                $formattedField['terms_and_conditions'] = wp_kses_post(Arr::get($value, 'terms_and_conditions'));
            }

            $formattedField = apply_filters('fluent_booking/save_event_booking_field_' . $fieldType, $formattedField, $value, $calendarEvent);

            $formattedFields[] = $formattedField;
        }

        $calendarEvent->setBookingFields($formattedFields);

        return [
            'message' => __('Fields has been updated', 'fluent-booking')
        ];
    }

    public function getEventPaymentSettings($calendarId, $eventId)
    {
        $calendarEvent = CalendarSlot::where('calendar_id', $calendarId)->findOrFail($eventId);

        $config = [
            'native_enabled'     => Helper::isPaymentEnabled(),
            'stripe_configured'  => Helper::isPaymentConfigured('stripe'),
            'paypal_configured'  => Helper::isPaymentConfigured('paypal'),
            'offline_configured' => Helper::isPaymentConfigured('offline'),
            'native_config_link' => Helper::getAppBaseUrl('settings/payment-methods/stripe'),
            'woo_config_link'    => Helper::getAppBaseUrl('settings/configure-integrations/global-modules'),
            'has_cart'           => defined('FLUENTCART_VERSION'),
            'has_woo'            => defined('WC_PLUGIN_FILE'),
            'woo_enabled'        => defined('WC_PLUGIN_FILE') && Helper::isModuleEnabled('woo')
        ];

        $data = apply_filters('fluent_booking/payment/get_payment_settings', [
            'settings' => $calendarEvent->getPaymentSettings(),
            'config'   => $config
        ], $calendarEvent);

        return $data;
    }

    public function deleteCalendarEvent(Request $request, $calendarId, $calendarEventId)
    {
        $calendar = Calendar::query()->findOrFail($calendarId);

        $calendarEvent = CalendarSlot::query()->where('calendar_id', $calendar->id)->findOrFail($calendarEventId);

        $calendar->updateEventOrder($calendarEvent->id);

        do_action('fluent_booking/before_delete_calendar_event', $calendarEvent, $calendar);

        $calendarEvent->delete();

        do_action('fluent_booking/after_delete_calendar_event', $calendarEventId, $calendar);

        return [
            'message' => __('Calendar Event has been deleted', 'fluent-booking')
        ];
    }

    private function sanitize_mapped_data($settings)
    {
        $sanitizerMap = [
            'value'                 => 'intval',
            'unit'                  => 'sanitize_text_field',
            'subject'               => 'sanitize_text_field',
            'body'                  => 'fcal_sanitize_html',
            'additional_recipients' => 'sanitize_text_field'
        ];

        return Helper::fcal_backend_sanitizer($settings, $sanitizerMap);
    }

    public function deleteCalendar(Request $request, $calendarId)
    {
        $calendar = Calendar::findOrFail($calendarId);

        do_action('fluent_booking/before_delete_calendar', $calendar);

        $calendar->delete();

        do_action('fluent_booking/after_delete_calendar', $calendarId);

        return [
            'message' => __('Calendar Deleted Successfully!', 'fluent-booking')
        ];
    }
}
