/* ----------------------------------------------------------------------------
 * Easy!Appointments - Open Source Web Scheduler
 *
 * @package     EasyAppointments
 * @author      A.Tselegidis <alextselegidis@gmail.com>
 * @copyright   Copyright (c) Alex Tselegidis
 * @license     https://opensource.org/licenses/GPL-3.0 - GPLv3
 * @link        https://easyappointments.org
 * @since       v1.2.0
 * ---------------------------------------------------------------------------- */

/**
 * Backend Calendar Appointments Modal
 *
 * This module implements the appointments modal functionality.
 *
 * @module BackendCalendarAppointmentsModal
 */
window.BackendCalendarAppointmentsModal = window.BackendCalendarAppointmentsModal || {};

(function (exports) {
    'use strict';

    function updateTimezone() {
        var providerId = $('#select-provider').val();

        var provider = GlobalVariables.availableProviders.find(function (availableProvider) {
            return Number(availableProvider.id) === Number(providerId);
        });

        if (provider && provider.timezone) {
            $('.provider-timezone').text(GlobalVariables.timezones[provider.timezone]);
        }
    }

    function bindEventHandlers() {
        /**
         * Event: Manage Appointments Dialog Save Button "Click"
         *
         * Stores the appointment changes or inserts a new appointment depending the dialog mode.
         */
        $('#manage-appointment #save-appointment').on('click', function () {
            // Before doing anything the appointment data need to be validated.
            if (!validateAppointmentForm()) {
                return;
            }

            // Prepare appointment data for AJAX request.
            var $dialog = $('#manage-appointment');

            // ID must exist on the object in order for the model to update the record and not to perform
            // an insert operation.

            var startDatetime = moment($dialog.find('#start-datetime').datetimepicker('getDate')).format(
                'YYYY-MM-DD HH:mm:ss'
            );
            var endDatetime = moment($dialog.find('#end-datetime').datetimepicker('getDate')).format(
                'YYYY-MM-DD HH:mm:ss'
            );

            var appointment = {
                id_services: $dialog.find('#select-service').val(),
                id_users_provider: $dialog.find('#select-provider').val(),
                start_datetime: startDatetime,
                end_datetime: endDatetime,
                location: $dialog.find('#appointment-location').val(),
                notes: $dialog.find('#appointment-notes').val(),
                is_unavailable: false
            };

            if ($dialog.find('#appointment-id').val() !== '') {
                // Set the id value, only if we are editing an appointment.
                appointment.id = $dialog.find('#appointment-id').val();
            }

            var customer = {
                first_name: $dialog.find('#first-name').val(),
                last_name: $dialog.find('#last-name').val(),
                email: $dialog.find('#email').val(),
                phone_number: $dialog.find('#phone-number').val(),
                address: $dialog.find('#address').val(),
                city: $dialog.find('#city').val(),
                zip_code: $dialog.find('#zip-code').val(),
                notes: $dialog.find('#customer-notes').val()
            };

            if ($dialog.find('#customer-id').val() !== '') {
                // Set the id value, only if we are editing an appointment.
                customer.id = $dialog.find('#customer-id').val();
                appointment.id_users_customer = customer.id;
            }

            // Define success callback.
            var successCallback = function (response) {
                // Display success message to the user.
                Backend.displayNotification(App.Lang.appointment_saved);

                // Close the modal dialog and refresh the calendar appointments.
                $dialog.find('.alert').addClass('d-none');
                $dialog.modal('hide');
                $('#select-filter-item').trigger('change');
            };

            // Define error callback.
            var errorCallback = function () {
                $dialog.find('.modal-message').text(App.Lang.service_communication_error);
                $dialog.find('.modal-message').addClass('alert-danger').removeClass('d-none');
                $dialog.find('.modal-body').scrollTop(0);
            };

            // Save appointment data.
            BackendCalendarApi.saveAppointment(appointment, customer, successCallback, errorCallback);
        });

        /**
         * Event: Insert Appointment Button "Click"
         *
         * When the user presses this button, the manage appointment dialog opens and lets the user to
         * create a new appointment.
         */
        $('#insert-appointment').on('click', function () {
            $('.popover').remove();

            BackendCalendarAppointmentsModal.resetAppointmentDialog();
            var $dialog = $('#manage-appointment');

            // Set the selected filter item and find the next appointment time as the default modal values.
            if ($('#select-filter-item option:selected').attr('type') === 'provider') {
                var providerId = $('#select-filter-item').val();

                var providers = GlobalVariables.availableProviders.filter(function (provider) {
                    return Number(provider.id) === Number(providerId);
                });

                if (providers.length) {
                    $dialog.find('#select-service').val(providers[0].services[0]).trigger('change');
                    $dialog.find('#select-provider').val(providerId);
                }
            } else if ($('#select-filter-item option:selected').attr('type') === 'service') {
                $dialog
                    .find('#select-service option[value="' + $('#select-filter-item').val() + '"]')
                    .prop('selected', true);
            } else {
                $dialog.find('#select-service option:first').prop('selected', true).trigger('change');
            }

            var serviceId = $dialog.find('#select-service').val();

            var service = GlobalVariables.availableServices.find(function (availableService) {
                return Number(availableService.id) === Number(serviceId);
            });

            var duration = service ? service.duration : 60;

            var startMoment = moment();

            var currentMin = parseInt(startMoment.format('mm'));

            if (currentMin > 0 && currentMin < 15) {
                startMoment.set({minutes: 15});
            } else if (currentMin > 15 && currentMin < 30) {
                startMoment.set({minutes: 30});
            } else if (currentMin > 30 && currentMin < 45) {
                startMoment.set({minutes: 45});
            } else {
                startMoment.add(1, 'hour').set({minutes: 0});
            }

            $dialog
                .find('#start-datetime')
                .val(GeneralFunctions.formatDate(startMoment.toDate(), GlobalVariables.dateFormat, true));

            $dialog
                .find('#end-datetime')
                .val(
                    GeneralFunctions.formatDate(
                        startMoment.add(duration, 'minutes').toDate(),
                        GlobalVariables.dateFormat,
                        true
                    )
                );

            // Display modal form.
            $dialog.find('.modal-header h3').text(App.Lang.new_appointment_title);

            $dialog.modal('show');
        });

        /**
         * Event: Pick Existing Customer Button "Click"
         */
        $('#select-customer').on('click', function () {
            var $list = $('#existing-customers-list');

            if (!$list.is(':visible')) {
                $(this).find('span').text(App.Lang.hide);
                $list.empty();
                $list.slideDown('slow');
                $('#filter-existing-customers').fadeIn('slow');
                $('#filter-existing-customers').val('');
                GlobalVariables.customers.forEach(function (customer) {
                    $('<div/>', {
                        'data-id': customer.id,
                        'text': customer.first_name + ' ' + customer.last_name
                    }).appendTo($list);
                });
            } else {
                $list.slideUp('slow');
                $('#filter-existing-customers').fadeOut('slow');
                $(this).find('span').text(App.Lang.select);
            }
        });

        /**
         * Event: Select Existing Customer From List "Click"
         */
        $('#manage-appointment').on('click', '#existing-customers-list div', function () {
            var customerId = $(this).attr('data-id');

            var customer = GlobalVariables.customers.find(function (customer) {
                return Number(customer.id) === Number(customerId);
            });

            if (customer) {
                $('#customer-id').val(customer.id);
                $('#first-name').val(customer.first_name);
                $('#last-name').val(customer.last_name);
                $('#email').val(customer.email);
                $('#phone-number').val(customer.phone_number);
                $('#address').val(customer.address);
                $('#city').val(customer.city);
                $('#zip-code').val(customer.zip_code);
                $('#customer-notes').val(customer.notes);
            }

            $('#select-customer').trigger('click'); // Hide the list.
        });

        var filterExistingCustomersTimeout = null;

        /**
         * Event: Filter Existing Customers "Change"
         */
        $('#filter-existing-customers').on('keyup', function () {
            if (filterExistingCustomersTimeout) {
                clearTimeout(filterExistingCustomersTimeout);
            }

            var key = $(this).val().toLowerCase();

            filterExistingCustomersTimeout = setTimeout(function () {
                var $list = $('#existing-customers-list');

                var url = GlobalVariables.baseUrl + '/index.php/backend_api/ajax_filter_customers';

                var data = {
                    csrf_token: GlobalVariables.csrfToken,
                    key: key
                };

                $('#loading').css('visibility', 'hidden');

                // Try to get the updated customer list.
                $.post(url, data)
                    .done(function (response) {
                        $list.empty();

                        response.forEach(function (customer) {
                            $('<div/>', {
                                'data-id': customer.id,
                                'text': customer.first_name + ' ' + customer.last_name
                            }).appendTo($list);

                            // Verify if this customer is on the old customer list.
                            var result = GlobalVariables.customers.filter(function (globalVariablesCustomer) {
                                return Number(globalVariablesCustomer.id) === Number(customer.id);
                            });

                            // Add it to the customer list.
                            if (!result.length) {
                                GlobalVariables.customers.push(customer);
                            }
                        });
                    })
                    .fail(function (jqXHR, textStatus, errorThrown) {
                        // If there is any error on the request, search by the local client database.
                        $list.empty();

                        GlobalVariables.customers.forEach(function (customer, index) {
                            if (
                                customer.first_name.toLowerCase().indexOf(key) !== -1 ||
                                customer.last_name.toLowerCase().indexOf(key) !== -1 ||
                                customer.email.toLowerCase().indexOf(key) !== -1 ||
                                customer.phone_number.toLowerCase().indexOf(key) !== -1 ||
                                customer.address.toLowerCase().indexOf(key) !== -1 ||
                                customer.city.toLowerCase().indexOf(key) !== -1 ||
                                customer.zip_code.toLowerCase().indexOf(key) !== -1 ||
                                customer.notes.toLowerCase().indexOf(key) !== -1
                            ) {
                                $('<div/>', {
                                    'data-id': customer.id,
                                    'text': customer.first_name + ' ' + customer.last_name
                                }).appendTo($list);
                            }
                        });
                    })
                    .always(function () {
                        $('#loading').css('visibility', '');
                    });
            }, 1000);
        });

        /**
         * Event: Selected Service "Change"
         *
         * When the user clicks on a service, its available providers should become visible. Also we need to
         * update the start and end time of the appointment.
         */
        $('#select-service').on('change', function () {
            var serviceId = $('#select-service').val();

            $('#select-provider').empty();

            // Automatically update the service duration.
            var service = GlobalVariables.availableServices.find(function (availableService) {
                return Number(availableService.id) === Number(serviceId);
            });

            var duration = service ? service.duration : 60;

            var start = $('#start-datetime').datetimepicker('getDate');
            $('#end-datetime').datetimepicker('setDate', new Date(start.getTime() + duration * 60000));

            // Update the providers select box.

            GlobalVariables.availableProviders.forEach(function (provider) {
                provider.services.forEach(function (providerServiceId) {
                    if (
                        GlobalVariables.user.role_slug === Backend.DB_SLUG_PROVIDER &&
                        Number(provider.id) !== GlobalVariables.user.id
                    ) {
                        return; // continue
                    }

                    if (
                        GlobalVariables.user.role_slug === Backend.DB_SLUG_SECRETARY &&
                        GlobalVariables.secretaryProviders.indexOf(provider.id) === -1
                    ) {
                        return; // continue
                    }

                    // If the current provider is able to provide the selected service, add him to the listbox.
                    if (Number(providerServiceId) === Number(serviceId)) {
                        $('#select-provider').append(
                            new Option(provider.first_name + ' ' + provider.last_name, provider.id)
                        );
                    }
                });
            });
        });

        /**
         * Event: Provider "Change"
         */
        $('#select-provider').on('change', function () {
            updateTimezone();
        });

        /**
         * Event: Enter New Customer Button "Click"
         */
        $('#new-customer').on('click', function () {
            $('#manage-appointment')
                .find(
                    '#customer-id, #first-name, #last-name, #email, ' +
                        '#phone-number, #address, #city, #zip-code, #customer-notes'
                )
                .val('');
        });
    }

    /**
     * Reset Appointment Dialog
     *
     * This method resets the manage appointment dialog modal to its initial state. After that you can make
     * any modification might be necessary in order to bring the dialog to the desired state.
     */
    exports.resetAppointmentDialog = function () {
        var $dialog = $('#manage-appointment');

        // Empty form fields.
        $dialog.find('input, textarea').val('');
        $dialog.find('.modal-message').fadeOut();

        // Prepare service and provider select boxes.
        $dialog.find('#select-service').val($dialog.find('#select-service').eq(0).attr('value'));

        // Fill the providers listbox with providers that can serve the appointment's
        // service and then select the user's provider.
        $dialog.find('#select-provider').empty();
        GlobalVariables.availableProviders.forEach(function (provider, index) {
            var canProvideService = false;

            var serviceId = $dialog.find('#select-service').val();

            var canProvideService =
                provider.services.filter(function (providerServiceId) {
                    return Number(providerServiceId) === Number(serviceId);
                }).length > 0;

            if (canProvideService) {
                // Add the provider to the listbox.
                $dialog
                    .find('#select-provider')
                    .append(new Option(provider.first_name + ' ' + provider.last_name, provider.id));
            }
        });

        // Close existing customers-filter frame.
        $('#existing-customers-list').slideUp('slow');
        $('#filter-existing-customers').fadeOut('slow');
        $('#select-customer span').text(App.Lang.select);

        // Setup start and datetimepickers.
        // Get the selected service duration. It will be needed in order to calculate the appointment end datetime.
        var serviceId = $dialog.find('#select-service').val();

        var service = GlobalVariables.availableServices.forEach(function (service) {
            return Number(service.id) === Number(serviceId);
        });

        var duration = service ? service.duration : 0;

        var startDatetime = new Date();
        var endDatetime = moment().add(duration, 'minutes').toDate();
        var dateFormat;

        switch (GlobalVariables.dateFormat) {
            case 'DMY':
                dateFormat = 'dd/mm/yy';
                break;
            case 'MDY':
                dateFormat = 'mm/dd/yy';
                break;
            case 'YMD':
                dateFormat = 'yy/mm/dd';
                break;
            default:
                throw new Error('Invalid GlobalVariables.dateFormat value.');
        }

        var firstWeekDay = GlobalVariables.firstWeekday;
        var firstWeekDayNumber = GeneralFunctions.getWeekDayId(firstWeekDay);

        $dialog.find('#start-datetime').datetimepicker({
            dateFormat: dateFormat,
            timeFormat: GlobalVariables.timeFormat === 'regular' ? 'h:mm tt' : 'HH:mm',

            // Translation
            dayNames: [
                App.Lang.sunday,
                App.Lang.monday,
                App.Lang.tuesday,
                App.Lang.wednesday,
                App.Lang.thursday,
                App.Lang.friday,
                App.Lang.saturday
            ],
            dayNamesShort: [
                App.Lang.sunday.substr(0, 3),
                App.Lang.monday.substr(0, 3),
                App.Lang.tuesday.substr(0, 3),
                App.Lang.wednesday.substr(0, 3),
                App.Lang.thursday.substr(0, 3),
                App.Lang.friday.substr(0, 3),
                App.Lang.saturday.substr(0, 3)
            ],
            dayNamesMin: [
                App.Lang.sunday.substr(0, 2),
                App.Lang.monday.substr(0, 2),
                App.Lang.tuesday.substr(0, 2),
                App.Lang.wednesday.substr(0, 2),
                App.Lang.thursday.substr(0, 2),
                App.Lang.friday.substr(0, 2),
                App.Lang.saturday.substr(0, 2)
            ],
            monthNames: [
                App.Lang.january,
                App.Lang.february,
                App.Lang.march,
                App.Lang.april,
                App.Lang.may,
                App.Lang.june,
                App.Lang.july,
                App.Lang.august,
                App.Lang.september,
                App.Lang.october,
                App.Lang.november,
                App.Lang.december
            ],
            prevText: App.Lang.previous,
            nextText: App.Lang.next,
            currentText: App.Lang.now,
            closeText: App.Lang.close,
            timeOnlyTitle: App.Lang.select_time,
            timeText: App.Lang.time,
            hourText: App.Lang.hour,
            minuteText: App.Lang.minutes,
            firstDay: firstWeekDayNumber,
            onClose: function () {
                var serviceId = $('#select-service').val();

                // Automatically update the #end-datetime DateTimePicker based on service duration.
                var service = GlobalVariables.availableServices.find(function (availableService) {
                    return Number(availableService.id) === Number(serviceId);
                });

                var start = $('#start-datetime').datetimepicker('getDate');
                $('#end-datetime').datetimepicker('setDate', new Date(start.getTime() + service.duration * 60000));
            }
        });
        $dialog.find('#start-datetime').datetimepicker('setDate', startDatetime);

        $dialog.find('#end-datetime').datetimepicker({
            dateFormat: dateFormat,
            timeFormat: GlobalVariables.timeFormat === 'regular' ? 'h:mm tt' : 'HH:mm',

            // Translation
            dayNames: [
                App.Lang.sunday,
                App.Lang.monday,
                App.Lang.tuesday,
                App.Lang.wednesday,
                App.Lang.thursday,
                App.Lang.friday,
                App.Lang.saturday
            ],
            dayNamesShort: [
                App.Lang.sunday.substr(0, 3),
                App.Lang.monday.substr(0, 3),
                App.Lang.tuesday.substr(0, 3),
                App.Lang.wednesday.substr(0, 3),
                App.Lang.thursday.substr(0, 3),
                App.Lang.friday.substr(0, 3),
                App.Lang.saturday.substr(0, 3)
            ],
            dayNamesMin: [
                App.Lang.sunday.substr(0, 2),
                App.Lang.monday.substr(0, 2),
                App.Lang.tuesday.substr(0, 2),
                App.Lang.wednesday.substr(0, 2),
                App.Lang.thursday.substr(0, 2),
                App.Lang.friday.substr(0, 2),
                App.Lang.saturday.substr(0, 2)
            ],
            monthNames: [
                App.Lang.january,
                App.Lang.february,
                App.Lang.march,
                App.Lang.april,
                App.Lang.may,
                App.Lang.june,
                App.Lang.july,
                App.Lang.august,
                App.Lang.september,
                App.Lang.october,
                App.Lang.november,
                App.Lang.december
            ],
            prevText: App.Lang.previous,
            nextText: App.Lang.next,
            currentText: App.Lang.now,
            closeText: App.Lang.close,
            timeOnlyTitle: App.Lang.select_time,
            timeText: App.Lang.time,
            hourText: App.Lang.hour,
            minuteText: App.Lang.minutes,
            firstDay: firstWeekDayNumber
        });
        $dialog.find('#end-datetime').datetimepicker('setDate', endDatetime);
    };

    /**
     * Validate the manage appointment dialog data. Validation checks need to
     * run every time the data are going to be saved.
     *
     * @return {Boolean} Returns the validation result.
     */
    function validateAppointmentForm() {
        var $dialog = $('#manage-appointment');

        // Reset previous validation css formatting.
        $dialog.find('.is-invalid').removeClass('is-invalid');
        $dialog.find('.modal-message').addClass('d-none');

        try {
            // Check required fields.
            var missingRequiredField = false;

            $dialog.find('.required').each(function (index, requiredField) {
                if ($(requiredField).val() === '' || $(requiredField).val() === null) {
                    $(requiredField).addClass('is-invalid');
                    missingRequiredField = true;
                }
            });

            if (missingRequiredField) {
                throw new Error(App.Lang.fields_are_required);
            }

            // Check email address.
            if (!GeneralFunctions.validateEmail($dialog.find('#email').val())) {
                $dialog.find('#email').addClass('is-invalid');
                throw new Error(App.Lang.invalid_email);
            }

            // Check appointment start and end time.
            var start = $('#start-datetime').datetimepicker('getDate');
            var end = $('#end-datetime').datetimepicker('getDate');
            if (start > end) {
                $dialog.find('#start-datetime, #end-datetime').addClass('is-invalid');
                throw new Error(App.Lang.start_date_before_end_error);
            }

            return true;
        } catch (error) {
            $dialog.find('.modal-message').addClass('alert-danger').text(error.message).removeClass('d-none');
            return false;
        }
    }

    exports.initialize = function () {
        bindEventHandlers();
    };
})(window.BackendCalendarAppointmentsModal);