<template>
    <div class="calendar-appointment-manager" v-cloak>

        <div class="mb-4 flex items-center justify-between flex-wrap gap-2">
            <div class="flex items-center gap-2">
                <div class="relative">
                    <select
                        v-model="filterStatus"
                        class="appearance-none bg-white border border-gray-300 rounded-sm text-xs text-gray-700 pl-3 pr-8 py-1.5 focus:outline-none focus:shadow-outline-blue focus:border-blue-300"
                    >
                        <option value="active">Active</option>
                        <option value="">All Statuses</option>
                        <option v-for="status in statuses" :value="status">{{ status | ucwords }}</option>
                    </select>
                    <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="fill-current h-3 w-3">
                            <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path>
                        </svg>
                    </div>
                </div>
                <div class="relative">
                    <select
                        v-model="filterType"
                        class="appearance-none bg-white border border-gray-300 rounded-sm text-xs text-gray-700 pl-3 pr-8 py-1.5 focus:outline-none focus:shadow-outline-blue focus:border-blue-300"
                    >
                        <option value="">All Types</option>
                        <option v-for="type in types" :value="type">{{ type | ucwords }}</option>
                    </select>
                    <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="fill-current h-3 w-3">
                            <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path>
                        </svg>
                    </div>
                </div>
            </div>
            <button
                class="inline-flex items-center px-3 py-1.5 border border-gray-300 text-xs leading-5 font-medium rounded-sm text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 active:text-gray-800 active:bg-gray-50 transition duration-150 ease-in-out"
                @click.prevent="openCreateModal"
            >
                Add Appointment
            </button>
        </div>

        <div v-if="items && items.length" :class="['has-items', state]">
            <transition-group name="list" tag="div">
                <calendar-appointment-item
                    v-for="item in paginatedItems"
                    :key="item.id"
                    :item="item"
                >
                </calendar-appointment-item>
            </transition-group>

            <div v-if="totalPages > 1" class="mt-4 flex items-center justify-between">
                <p class="text-xs text-gray-500">Showing {{ paginationFrom }}-{{ paginationTo }} of {{ filteredItems.length }}</p>
                <data-table-pagination
                    :total-page="totalPages"
                    :current-page="currentPage"
                    @paginationChanged="currentPage = $event"
                ></data-table-pagination>
            </div>
        </div>
        <div v-else :class="['no-items', state]">
            <v-card>
                <div class="message text-center">
                    <div class="message-text font-semibold">
                        No appointments found.
                    </div>
                </div>
            </v-card>
        </div>

        <div v-if="message && messageType"
             class="rounded-sm p-4 mt-4 border"
             :class="{
                'bg-red-100 border-red-400' : isErrorMessage(),
                'bg-yellow-100 border-yellow-400' : isWarningMessage(),
                'bg-blue-100 border-blue-400' : isInfoMessage(),
                'bg-green-100 border-green-400' : isSuccessMessage(),
            }"
        >
            <div class="flex">
                <div class="flex-shrink-0">
                    <svg class="h-5 w-5"
                         :class="{
                            'text-red-400' : isErrorMessage(),
                            'text-yellow-400' : isWarningMessage(),
                            'text-blue-400' : isInfoMessage(),
                            'text-green-400' : isSuccessMessage(),
                        }"
                         viewBox="0 0 20 20"
                         fill="currentColor"
                    >
                        <path fill-rule="evenodd"
                              d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
                              clip-rule="evenodd"
                        />
                    </svg>
                </div>
                <div class="ml-3">
                    <p class="text-sm leading-5"
                       :class="{
                            'text-red-800' : isErrorMessage(),
                            'text-yellow-800' : isWarningMessage(),
                            'text-blue-800' : isInfoMessage(),
                            'text-green-800' : isSuccessMessage(),
                        }"
                    >{{ message }}</p>
                </div>
            </div>
        </div>

        <modal
            ref="appointmentModal"
            :show-overflow="true"
            :btn-primary-text="mode === 'create' ? 'Create' : 'Update'"
            @btnPrimaryClicked="saveItem"
            @btnSecondaryClicked="closeModal"
        >
            <h3 slot="modal_title" class="text-lg leading-6 font-medium text-gray-900">
                {{ mode === 'create' ? 'New Appointment' : 'Edit Appointment' }}
            </h3>

            <div slot="modal_content">
                <div class="form-group">
                    <label class="text-sm leading-5 font-medium text-gray-500">Date</label>
                    <v-datepicker
                        name="date"
                        :value="currentItem.attributes.date"
                        :disable-past-dates="true"
                        :disabled-days="closedDays"
                        :booked-dates="bookedDates"
                        :restricted-dates="restrictedDates"
                        :start-on-monday="true"
                        @selected="currentItem.attributes.date = $event"
                    ></v-datepicker>
                    <p v-if="hasErrors && !currentItem.attributes.date" class="mt-1 text-sm text-red-600">Date is required</p>
                </div>

                <div class="form-group mt-4">
                    <label class="text-sm leading-5 font-medium text-gray-500">Name</label>
                    <div class="mt-1 relative rounded-sm shadow-sm">
                        <input
                            class="form-input block w-full rounded-sm sm:text-sm sm:leading-5"
                            type="text"
                            v-model="currentItem.attributes.name"
                        >
                    </div>
                    <p v-if="hasErrors && !currentItem.attributes.name" class="mt-1 text-sm text-red-600">Name is required</p>
                </div>

                <div class="form-group mt-4">
                    <label class="text-sm leading-5 font-medium text-gray-500">Email</label>
                    <div class="mt-1 relative rounded-sm shadow-sm">
                        <input
                            class="form-input block w-full rounded-sm sm:text-sm sm:leading-5"
                            type="email"
                            v-model="currentItem.attributes.email"
                        >
                    </div>
                    <p v-if="hasErrors && !currentItem.attributes.email" class="mt-1 text-sm text-red-600">Email is required</p>
                </div>

                <div class="form-group mt-4">
                    <label class="text-sm leading-5 font-medium text-gray-500">Notes</label>
                    <div class="mt-1 relative rounded-sm shadow-sm">
                        <textarea
                            class="form-input block w-full rounded-sm sm:text-sm sm:leading-5"
                            rows="3"
                            v-model="currentItem.attributes.notes"
                        ></textarea>
                    </div>
                </div>

                <div class="form-group mt-4">
                    <label class="text-sm leading-5 font-medium text-gray-500">Type</label>
                    <div class="mt-1 relative rounded-sm shadow-sm">
                        <select
                            class="form-input block w-full rounded-sm sm:text-sm sm:leading-5 pr-10"
                            v-model="currentItem.attributes.type"
                        >
                            <option v-for="type in types" :value="type">{{ type | ucwords }}</option>
                        </select>
                        <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="fill-current h-4 w-6">
                                <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path>
                            </svg>
                        </div>
                    </div>
                </div>

                <div class="form-group mt-4">
                    <label class="text-sm leading-5 font-medium text-gray-500">Status</label>
                    <div class="mt-1 relative rounded-sm shadow-sm">
                        <select
                            class="form-input block w-full rounded-sm sm:text-sm sm:leading-5 pr-10"
                            v-model="currentItem.attributes.status"
                        >
                            <option v-for="status in statuses" :value="status">{{ status | ucwords }}</option>
                        </select>
                        <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-500">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" class="fill-current h-4 w-6">
                                <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"></path>
                            </svg>
                        </div>
                    </div>
                </div>

                <div v-if="modalError" class="rounded-sm p-3 mt-4 bg-red-100 border border-red-400">
                    <p class="text-sm text-red-800">{{ modalError }}</p>
                </div>
            </div>
        </modal>
    </div>
</template>


<script>
import CalendarAppointmentItem from "./CalendarAppointmentItem";
import DataTablePagination from "../DataTable/DataTablePagination";
import Modal from "../Modal";

export default {
    name : "CalendarAppointmentManager",
    components : {
        CalendarAppointmentItem,
        DataTablePagination,
        Modal,
    },
    props : {
        calendar : {type : Object, required : true},
        resourceUrl : {type : String, required : true},
        restrictionResourceUrl : {type : String, default : ''},
    },
    data() {
        return {
            mode : 'create',
            state : 'loading',
            hasErrors : false,
            modalError : null,
            restrictions : [],

            message : null,
            messageType : null,

            types : ['provisional', 'firm'],
            statuses : ['reserved', 'approved', 'rejected'],

            filterStatus : 'active',
            filterType : '',

            perPage : 5,
            currentPage : 1,

            items : [],
            currentItem : this.defaultItem(),
            cachedItem : null,
        }
    },
    computed : {
        closedDays() {
            let days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
            return days.filter(day => !this.calendar[day]);
        },
        bookedDates() {
            return this.items
                .filter(item => item.attributes.status !== 'rejected')
                .filter(item => !this.currentItem || item.id !== this.currentItem.id)
                .map(item => {
                    let date = window.moment(item.attributes.date, ['YYYY-MM-DD', 'DD/MM/YYYY']);
                    return date.isValid() ? date.format('YYYY-MM-DD') : null;
                })
                .filter(Boolean);
        },
        restrictedDates() {
            return this.restrictions.map(item => {
                let date = window.moment(item.date, ['YYYY-MM-DD', 'DD/MM/YYYY']);
                return date.isValid() ? date.format('YYYY-MM-DD') : null;
            }).filter(Boolean);
        },
        filteredItems() {
            return this.items.filter(item => {
                if (this.filterStatus === 'active' && item.attributes.status === 'rejected') return false;
                if (this.filterStatus && this.filterStatus !== 'active' && item.attributes.status !== this.filterStatus) return false;
                if (this.filterType && item.attributes.type !== this.filterType) return false;
                return true;
            });
        },
        totalPages() {
            return Math.ceil(this.filteredItems.length / this.perPage);
        },
        paginatedItems() {
            let start = (this.currentPage - 1) * this.perPage;
            return this.filteredItems.slice(start, start + this.perPage);
        },
        paginationFrom() {
            if (this.filteredItems.length === 0) return 0;
            return ((this.currentPage - 1) * this.perPage) + 1;
        },
        paginationTo() {
            return Math.min(this.currentPage * this.perPage, this.filteredItems.length);
        },
    },
    watch : {
        filterStatus() {
            this.currentPage = 1;
        },
        filterType() {
            this.currentPage = 1;
        },
    },
    methods : {
        openCreateModal() {
            this.mode = 'create';
            this.hasErrors = false;
            this.modalError = null;
            this.currentItem = this.defaultItem();
            this.$refs.appointmentModal.showModal();
        },
        openEditModal(item) {
            this.mode = 'edit';
            this.hasErrors = false;
            this.modalError = null;
            this.cachedItem = JSON.parse(JSON.stringify(item));
            this.currentItem = item;
            this.$refs.appointmentModal.showModal();
        },
        closeModal() {
            if (this.mode === 'edit' && this.cachedItem) {
                let index = _.findIndex(this.items, {id : this.cachedItem.id});
                if (index !== -1) {
                    this.$set(this.items, index, this.cachedItem);
                }
            }
            this.cachedItem = null;
            this.$refs.appointmentModal.closeModal();
        },

        saveItem() {
            this.hasErrors = false;

            if (!this.currentItem.attributes.date || !this.currentItem.attributes.name || !this.currentItem.attributes.email) {
                this.hasErrors = true;
                return;
            }

            if (!this.currentItem.id) {
                this.storeItem();
            } else {
                this.updateItem();
            }
        },
        formatPayload() {
            let payload = Object.assign({}, this.currentItem.attributes);
            if (payload.date && payload.date.includes('/')) {
                payload.date = window.moment(payload.date, 'DD/MM/YYYY').format('YYYY-MM-DD');
            }
            return payload;
        },
        storeItem() {
            this.modalError = null;
            window.axios
                .post(this.resourceUrl, this.formatPayload())
                .then(() => {
                    this.$refs.appointmentModal.closeModal();
                    this.resetMessage();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    let errorMessage = e.response?.data?.message || e.message;
                    this.modalError = errorMessage;
                    this.showError(errorMessage);
                });
        },
        updateItem() {
            this.modalError = null;
            window.axios
                .post(`${this.resourceUrl}/${this.currentItem.id}/update`, this.formatPayload())
                .then(() => {
                    this.cachedItem = null;
                    this.$refs.appointmentModal.closeModal();
                    this.resetMessage();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    let errorMessage = e.response?.data?.message || e.message;
                    this.modalError = errorMessage;
                    this.showError(errorMessage);
                });
        },
        deleteItem(args) {
            if (!args.item) {
                this.showError('No item provided for delete');
                return;
            }

            window.axios
                .post(`${this.resourceUrl}/${args.item.id}/delete`)
                .then(() => {
                    this.resetMessage();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },
        approveItem(args) {
            if (!args.item) {
                this.showError('No item provided for approve');
                return;
            }

            window.axios
                .post(`${this.resourceUrl}/${args.item.id}/approve`)
                .then(() => {
                    this.resetMessage();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },
        rejectItem(args) {
            if (!args.item) {
                this.showError('No item provided for reject');
                return;
            }

            window.axios
                .post(`${this.resourceUrl}/${args.item.id}/reject`)
                .then(() => {
                    this.resetMessage();
                    this.fetchExistingItems();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },

        resetMessage() {
            this.message = null;
            this.messageType = null;
        },

        fetchExistingItems() {
            this.state = 'loading';

            if (!this.calendar) {
                this.showError('Calendar object not found for Appointment Manager');
                return;
            }

            window.axios
                .get(this.resourceUrl)
                .then((response) => {
                    let result = response.data;
                    if (!result.data) {
                        this.showError('No data returned from API for appointments');
                        return;
                    }

                    this.items = result.data.map(item => this.mapApiItem(item));
                    this.currentPage = 1;
                    this.state = this.items.length ? 'ready' : 'no-data';
                    this.resetMessage();
                })
                .catch(e => {
                    this.showError(e.message);
                });
        },
        mapApiItem(item) {
            let date = item.date;
            if (date && !date.includes('/')) {
                date = window.moment(date, 'YYYY-MM-DD').format('DD/MM/YYYY');
            }
            return {
                id : item.id,
                type : 'calendar_appointment',
                attributes : {
                    calendar_id : item.calendar_id,
                    date : date,
                    name : item.name,
                    email : item.email,
                    notes : item.notes,
                    type : item.type,
                    status : item.status,
                },
            }
        },
        defaultItem() {
            return {
                id : null,
                type : 'calendar_appointment',
                attributes : {
                    calendar_id : this.calendar ? this.calendar.id : null,
                    date : null,
                    name : null,
                    email : null,
                    notes : null,
                    type : 'provisional',
                    status : 'reserved',
                },
            }
        },

        showError(message) {
            this.state = 'error';
            this.message = message;
            this.messageType = 'error';
            console.error(message);
        },
        isErrorMessage() {
            return this.messageType === 'error';
        },
        isWarningMessage() {
            return this.messageType === 'warning';
        },
        isInfoMessage() {
            return this.messageType === 'info';
        },
        isSuccessMessage() {
            return this.messageType === 'success';
        },

        fetchRestrictions() {
            if (!this.restrictionResourceUrl) return;
            window.axios
                .get(this.restrictionResourceUrl)
                .then((response) => {
                    this.restrictions = response.data?.data || [];
                })
                .catch(() => {});
        },

        registerEventListeners() {
            window.EventBus.$on('calendarAppointment:editItem', (args) => this.openEditModal(args.item));
            window.EventBus.$on('calendarAppointment:deleteItem', (args) => this.deleteItem(args));
            window.EventBus.$on('calendarAppointment:approveItem', (args) => this.approveItem(args));
            window.EventBus.$on('calendarAppointment:rejectItem', (args) => this.rejectItem(args));
        }
    },
    mounted() {
        this.fetchExistingItems();
        this.fetchRestrictions();
        this.registerEventListeners();
    },
    beforeDestroy() {
        window.EventBus.$off('calendarAppointment:editItem');
        window.EventBus.$off('calendarAppointment:deleteItem');
        window.EventBus.$off('calendarAppointment:approveItem');
        window.EventBus.$off('calendarAppointment:rejectItem');
    }
}
</script>
