import React, { useState, useEffect, use } from 'react';
import { Calendar, momentLocalizer, dateFnsLocalizer } from 'react-big-calendar';
import 'react-big-calendar/lib/css/react-big-calendar.css';
import AvailabilityForm from '../availability/AvailabilityForm';
import ProtectedRoute from '../protectedRoute';
import { UserRole } from "@prisma/client";
import common from '../../src/helpers/common';

import { toast } from 'react-toastify';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import moment from 'moment'; // ToDo: obsolete, remove it
import 'moment/locale/bg'; // Import Bulgarian locale
import { ArrowLeftCircleIcon } from '@heroicons/react/24/outline';

import { FaArrowLeft, FaArrowRight, FaRegCalendarAlt, FaRegListAlt, FaRegCalendarCheck } from 'react-icons/fa';
import { MdToday } from 'react-icons/md';

import { useSwipeable } from 'react-swipeable';
import axiosInstance from '../../src/axiosSecure';
import { set } from 'date-fns';
import { get } from 'http';

// import { set, format, addDays } from 'date-fns';
// import { isEqual, isSameDay, getHours, getMinutes } from 'date-fns';
// import { filter } from 'jszip';
// import e from 'express';




// Set moment to use the Bulgarian locale
moment.locale('bg');
const localizer = momentLocalizer(moment);
// Bulgarian translations for Calendar labels
const messages = {
    allDay: 'Цял ден',
    previous: 'Предишен',
    next: 'Следващ',
    today: 'Днес',
    month: 'Месец',
    week: 'Седмица',
    day: 'Ден',
    agenda: 'Дневен ред',
    date: 'Дата',
    time: 'Час',
    event: 'Събитие', // or 'Събитие' depending on context
    // Any other labels you want to translate...
};

const AvCalendar = ({ publisherId, events, selectedDate, cartEvents, lastPublishedDate }) => {
    const [editLockedBefore, setEditLockedBefore] = useState(new Date(lastPublishedDate));
    const [isAdmin, setIsAdmin] = useState(false);
    // const [isPowerUser, setIsPowerUser] = useState(false);
    // const [currentUserId, setCurrentUserId] = useState(null);
    useEffect(() => {
        (async () => {
            try {
                setIsAdmin(await ProtectedRoute.IsInRole(UserRole.ADMIN));
                // setIsPowerUser(await ProtectedRoute.IsInRole(UserRole.POWERUSER));
                // // Assuming you have a way to get the current user's ID
                // setCurrentUserId(await ProtectedRoute.GetCurrentUserId());
            } catch (error) {
                console.error("Failed to check admin role:", error);
            }
        })();
    }, []);
    //const isAdmin = ProtectedRoute.IsInRole(UserRole.ADMIN);

    //block dates between 1 and 18 august 2024
    const blockedDates = [
        new Date(2024, 7, 1),
        new Date(2024, 7, 2),
        new Date(2024, 7, 3),
        new Date(2024, 7, 4),
        new Date(2024, 7, 5),
        new Date(2024, 7, 6),
        new Date(2024, 7, 7),
        new Date(2024, 7, 8),
        new Date(2024, 7, 9),
        new Date(2024, 7, 10),
        new Date(2024, 7, 11),
        new Date(2024, 7, 12),
        new Date(2024, 7, 13),
        new Date(2024, 7, 14),
        new Date(2024, 7, 15),
        new Date(2024, 7, 16),
        new Date(2024, 7, 17),
        new Date(2024, 7, 18),
    ];

    const [date, setDate] = useState(new Date());
    //ToDo: see if we can optimize this
    const [evts, setEvents] = useState(events); // Existing events
    const [selectedEvents, setSelectedEvents] = useState([]);
    const [displayedEvents, setDisplayedEvents] = useState(evts); // Events to display in the calendar

    const [currentView, setCurrentView] = useState('month');
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [visibleRange, setVisibleRange] = useState(() => {
        const start = new Date();
        start.setDate(1); // Set to the first day of the current month
        const end = new Date(start.getFullYear(), start.getMonth() + 1, 0); // Last day of the current month
        return { start, end };
    });

    const [cartEvent, setCartEvent] = useState(null);
    function getCartEvent(date) {
        const dayOfWeek = common.getDayOfWeekNameEnEnumForDate(date);
        const ce = cartEvents?.find(e => e.dayofweek === dayOfWeek);
        return ce;
    }

    useEffect(() => {
        //console.log("useEffect: ", date, selectedEvents, cartEvents);
        setCartEvent(getCartEvent(date));
    },
        [date, selectedEvents]);

    // Update internal state when `events` prop changes
    useEffect(() => {
        //if we have isBySystem - set type to assignment
        let updatedEvents = events?.map(event => {
            if (event.isBySystem) {
                event.type = "assignment";
            }
            return event;
        });
        updatedEvents = events?.map(event => ({
            ...event,
            date: new Date(event.startTime).setHours(0, 0, 0, 0),
            startTime: new Date(event.startTime),
            endTime: event.endTime ? new Date(event.endTime) : undefined
        }));

        setEvents(updatedEvents);
        // Call any function here to process and set displayedEvents
        // based on the new events, if necessary
    }, [events]);

    const onRangeChange = (range) => {
        if (Array.isArray(range)) {
            // For week and day views, range is an array of dates
            setVisibleRange({ start: range[0], end: range[range.length - 1] });
        } else {
            // For month view, range is an object with start and end
            setVisibleRange(range);
        }
    };

    useEffect(() => {
        if (currentView === 'agenda') {
            const filtered = evts?.filter(event => event.type === "assignment");
            setDisplayedEvents(filtered);
        } else {
            // Function to generate weekly occurrences of an event
            const recurringEvents = evts?.filter(event => event.type !== "assignment" && (event.dayOfMonth === null || event.dayOfMonth === undefined)) || [];
            const occurrences = recurringEvents?.flatMap(event => generateOccurrences(event, visibleRange.start, visibleRange.end)) || [];
            const nonRecurringEvents = evts?.filter(event => event.dayOfMonth !== null) || [];

            setDisplayedEvents([...nonRecurringEvents, ...recurringEvents, ...occurrences]);
        }
        //setDisplayedEvents(evts);
    }, [visibleRange, evts, currentView]);

    // todo: review that
    const handlers = useSwipeable({
        onSwipedLeft: () => navigate('NEXT'),
        onSwipedRight: () => navigate('PREV'),
        preventDefaultTouchmoveEvent: true,
        trackMouse: true,
    });
    const navigate = (action) => {
        console.log('navigate', action);
        setDate((currentDate) => {
            const newDate = new Date(currentDate);
            if (action === 'NEXT') {
                newDate.setMonth(newDate.getMonth() + 1);
            } else if (action === 'PREV') {
                newDate.setMonth(newDate.getMonth() - 1);
            }
            return newDate;
        });
    };

    const generateOccurrences = (event, start, end) => {
        const occurrences = [];
        const eventStart = new Date(event.startTime);
        let current = new Date(event.startTime); // Assuming startTime has the start date

        // Determine the end date for the event series
        const seriesEndDate = event.endDate ? new Date(event.endDate) : end;
        seriesEndDate.setHours(23, 59, 59); // Set to the end of the day

        while (current <= seriesEndDate && current <= end) {
            // Check if the event should be repeated weekly or on a specific day of the month
            if (event.repeatWeekly && current.getDay() === eventStart.getDay()) {
                // For weekly recurring events
                addOccurrence(event, current, occurrences);
            } else if (event.dayOfMonth && current.getDate() === event.dayOfMonth) {
                // For specific day of month events
                addOccurrence(event, current, occurrences);
            }

            // Move to the next day
            current = new Date(current.setDate(current.getDate() + 1));
        }

        return occurrences;
    };

    // Helper function to add an occurrence
    const addOccurrence = (event, current, occurrences) => {
        // Skip the original event date

        const eventStart = new Date(event.startTime);
        const eventEnd = new Date(event.endTime);
        if (current.toDateString() !== eventStart.toDateString()) {
            const occurrence = {
                ...event,
                startTime: new Date(current.setHours(eventStart.getHours(), eventStart.getMinutes())),
                endTime: new Date(current.setHours(eventEnd.getHours(), eventEnd.getMinutes())),
                date: current,
                type: 'recurring'
            };
            occurrences.push(occurrence);
        }
    };
    const filterEvents = (evts, publisherId, startdate) => {
        setDate(startdate);  // Assuming setDate is a function that sets some state or context

        const filterDayOfWeek = startdate.getDay(); // Sunday - 0, Monday - 1, ..., Saturday - 6

        // Filter events based on the publisher ID and the start date/time
        const existingEvents = evts?.filter(event => {
            // Ensure the event belongs to the specified publisher
            const isPublisherMatch = (event.publisher?.id || event.publisherId) === publisherId;
            const eventDayOfWeek = event.startTime.getDay();

            let isDateMatch;
            const eventDate = new Date(event.startTime);
            if (event.repeatWeekly && filterDayOfWeek === eventDayOfWeek) {
                isDateMatch = true;
            } else if (event.date) {
                // Compare the full date. issameday is not working. do it manually
                isDateMatch = eventDate.setHours(0, 0, 0, 0) === new Date(startdate).setHours(0, 0, 0, 0);
            }

            return isPublisherMatch && isDateMatch;
        });

        return existingEvents;
    };


    // const totalHours = maxHour - minHour;

    const handleSelect = ({ mode, start, end }) => {
        //we set the time to proper timezone
        const startdate = common.setTimezone(start);
        const enddate = common.setTimezone(end);

        if (!start || !end) return;
        //readonly for past dates (ToDo: if not admin or current user)
        //const isCurrentUser = selectedEvents.some(event => event.userId === currentUserId);

        if (!isAdmin) {
            if (startdate < new Date() || end < new Date() || startdate > end) return;
            //or if schedule is published (lastPublishedDate)
            if (editLockedBefore && startdate < editLockedBefore) {
                toast.error(`Не можете да променяте предпочитанията си за дати преди  ${common.getDateFormattedShort(editLockedBefore)}.`, { autoClose: 5000 });
                return;

            }

            if (blockedDates[0] <= startdate && startdate <= blockedDates[blockedDates.length - 1]) {
                toast.error(`Не можете да въвеждате предпочитания за ${common.getDateFormattedShort(startdate)}`, { autoClose: 5000 });
                return;
            }
        }
        // Check if start and end are on the same day
        if (startdate.toDateString() !== enddate.toDateString()) {
            end = common.setTimeHHmm(startdate, "23:59");
        }

        // Update date state and calculate events based on the new startdate
        setDate(startdate);
        const existingEvents = filterEvents(evts, publisherId, startdate);
        console.log("handleSelect: ", existingEvents);

        // Use the updated startdate for getCartEvent and ensure it reflects in the state properly
        const cartEvent = getCartEvent(startdate);
        setCartEvent(cartEvent);
        console.log("cartEvent: ", cartEvent);
        setSelectedEvents(existingEvents);
        setIsModalOpen(true);
    };

    const handleEventClick = (event) => {
        if (event.type === "assignment") return;
        //select the whole day 
        let start = new Date(event.startTime);
        start.setHours(0, 0, 0, 0);
        let end = new Date(event.startTime);
        end.setHours(23, 59, 59, 999);
        handleSelect({ mode: 'select', start: start, end: end });
    };

    const handleDialogClose = async (dialogEvent) => {
        setIsModalOpen(false);
        if (dialogEvent === null || dialogEvent === undefined) {
        } else {
            let e = await axiosInstance.get(`/api/?action=getCalendarEvents&publisherId=${publisherId}`);
            var newEvents = e.data;
            // set start and end to Date objects for all events. Fix for the calendar component
            newEvents.forEach(event => {
                event.startTime = new Date(event.startTime);
                event.endTime = new Date(event.endTime);
            });
            setEvents(newEvents);
        }

        console.log("handleSave: ", dialogEvent);
    };

    const handleCancel = () => {
        setIsModalOpen(false);
    };


    // REDRAW (PAGING) STYLES FOR THE EVENT DEFINED HERE
    const eventStyleGetter = (event, start, end, isSelected) => {

        //console.log("eventStyleGetter: " + event);
        let backgroundColor = '#3174ad';         // default color for calendar events - #3174ad
        if (currentView === 'agenda') {
            return { style: {} }
        }
        if (event.type === "assignment") {
            //event.title = event.publisher.name; //ToDo: add other publishers names
        }
        if (event.type === "availability") {

        }
        if (event.isFromPreviousAssignment) { //ToDo: does it work?
            // orange-500 from Tailwind CSS
            backgroundColor = '#f56565';
        }
        if (event.isFromPreviousMonth) {
            //gray
            backgroundColor = '#a0aec0';
        }


        // if (event.isActive) {
        switch (event.type) {
            case 'assignment':
                backgroundColor = '#48bb78' // always green-500 as we don't pass isConfirmed correctly  
                //backgroundColor =  event.isConfirmed ? '#48bb78' : '#f6e05e'; // green-500 and yellow-300 from Tailwind CSS
                break;
            case 'recurring':
                backgroundColor = '#63b3ed'; // blue-300 from Tailwind CSS
                break;
            default: // availability
                //backgroundColor = '#a0aec0'; // gray-400 from Tailwind CSS
                break;
        }
        // } else {
        //     backgroundColor = '#a0aec0'; // Default color for inactive events
        // }

        return {
            style: {
                backgroundColor,
                opacity: event.startTime < new Date() ? 0.5 : 1,
                color: 'white',
                border: '0px',
                display: 'block',
            }
        };
    }

    // INITIAL STYLE FOR THE EVENT DEFINED HERE
    const EventWrapper = ({ event, style }) => {
        const [isHovered, setIsHovered] = useState(false);
        let eventStyle = {
            ...style
        };
        const handleMouseEnter = () => setIsHovered(true);
        const handleMouseLeave = () => setIsHovered(false);
        if (currentView !== 'agenda') {
            //if event.type is availability show in blue. if it is schedule - green if confirmed, yellow if not confirmed
            var bgColor = "";
            //ToDo: fix this. maybe we're missing some properties
            // if (event.isFromPreviousMonth) {
            //     //    set opacity to 0.5 
            //     bgColor = "bg-orange-500";
            // }
            if (event.type === "assignment") {
                bgColor = event.isBySystem ? "bg-red-500" : (event.isConfirmed || true ? "bg-green-500" : "bg-yellow-500");

                //event.title = event.publisher.name; //ToDo: add other publishers names
                //event.title = common.getTimeFormatted(event.startTime) + " - " + common.getTimeFormatted(event.endTime);
            } else {
                if (event.start !== undefined && event.end !== undefined && event.startTime !== null && event.endTime !== null) {
                    try {
                        if (event.type === "recurring") {
                            event.title = common.getTimeFormatted(event.startTime) + " - " + common.getTimeFormatted(event.endTime);
                        }
                        else {
                            event.title = common.getTimeFormatted(event.startTime) + " - " + common.getTimeFormatted(event.endTime);
                        }
                    }
                    catch (err) {
                        event.title = event.startTime + " - " + event.endTime;
                        console.log("Error in EventWrapper: " + err);
                    }

                }
            }


            eventStyle = {
                ...style,
                // backgroundColor: bgColorClass,
                //height: "50px",
                //color: 'white',
                //if (event.isFromPreviousAssignment) { set opacity to 0.5 }
                // opacity: event.isFromPreviousMonth ? 0.5 : 1,
                whiteSpace: 'normal', // Allow the text to wrap to the next line
                overflow: 'hidden',  // Hide overflowed content
                textOverflow: 'ellipsis' // Add ellipsis to text that's too long to fit
            };
            if (event.date < new Date()) {
                eventStyle.opacity = 0.5;
            }

        }
        const onDelete = (event) => {
            // Remove the event from the calendar
            setEvents(currentEvents => currentEvents.filter(e => e.id !== event.id));
        };

        const onConfirm = (event) => {
            console.log("onConfirm: " + event.id);
            toast.info("Потвърдено!", { autoClose: 2000 });
            // Update the event data
            event.isConfirmed = true;
            event.isBySystem = false;
            // Update the events array by first removing the old event and then adding the updated one
            setEvents(currentEvents => {
                const filteredEvents = currentEvents.filter(e => e.id !== event.id);
                return [...filteredEvents, event];
            });
            //store the updated event in the database
            var assignment = {
                isConfirmed: true,
                isBySystem: false
            };
            axiosInstance.put('/api/data/assignments/' + event.id, assignment)
                .then((response) => {
                    console.log(response);
                })
                .catch((error) => {
                    console.log(error);
                });
        };
        return (
            <div style={eventStyle} className={bgColor + " relative"}
                onMouseEnter={handleMouseEnter}
                onMouseLeave={handleMouseLeave} >
                {event.title}
                {isHovered && event.type == "assignment" && (event.status == "pending" || event.status == undefined)
                    && (
                        <div className="absolute top-1 left-0 right-0 flex justify-between px-1">
                            {/* Delete Icon */}
                            {/* <span
                                className="disabled cursor-pointer rounded-full bg-red-500 text-white flex items-center justify-center"
                                style={{ width: '24px', height: '24px' }} // Adjust the size as needed
                                onClick={() => onDelete(event)}
                            >
                                ✕
                            </span> */}

                            {/* Confirm Icon */}
                            {/* {!event.isConfirmed && (
                                <span
                                    className=" cursor-pointer rounded-full bg-green-500 text-white flex items-center justify-center"
                                    style={{ width: '24px', height: '24px' }} // Adjust the size as needed
                                    onClick={() => onConfirm(event)}
                                >
                                    ✓
                                </span>
                            )} */}
                        </div>
                    )}
            </div>
        );
    };


    const CustomToolbar = ({ onNavigate, label, onView, view }) => {
        return (
            <div className="rbc-toolbar">
                <span className="rbc-btn-group">
                    <button type="button" onClick={() => onNavigate('PREV')}>
                        <FaArrowLeft className="icon-large" />
                    </button>
                    <button type="button" onClick={() => onNavigate('TODAY')}>
                        <MdToday className="icon-large" />
                    </button>
                    <button type="button" onClick={() => onNavigate('NEXT')}>
                        <FaArrowRight className="icon-large" />
                    </button>
                </span>
                <span className="rbc-toolbar-label">{label}</span>
                <span className="rbc-btn-group">
                    <button type="button" onClick={() => onView('month')} className={view === 'month' ? 'rbc-active' : ''}>
                        <FaRegCalendarAlt className="icon-large" />
                    </button>
                    <button type="button" onClick={() => onView('week')} className={view === 'week' ? 'rbc-active' : ''}>
                        <FaRegListAlt className="icon-large" />
                    </button>
                    <button type="button" onClick={() => onView('agenda')} className={view === 'agenda' ? 'rbc-active' : ''}>
                        <FaRegCalendarCheck className="icon-large" />
                    </button>
                    {/* Add more view buttons as needed */}
                </span>
            </div>
        );
    };


    const CustomEventAgenda = ({
        event
    }) => (
        <span>
            <em style={{ color: 'black' }}>{event.title}</em>
            <p>{event.desc}</p>
        </span>
    );


    return (
        <> <div  {...handlers} className="flex flex-col"
        >
            {/* достъпности на {publisherId} */}
            {/* having multiple ToastContainers causes double rendering of toasts and all kind of problems */}
            {/* <ToastContainer position="top-center" style={{ zIndex: 9999 }} /> */}
        </div>
            <Calendar
                localizer={localizer}
                events={displayedEvents}
                startAccessor="startTime"
                endAccessor="endTime"
                selectable={true}
                onSelectSlot={handleSelect}
                onSelectEvent={handleEventClick}
                style={{ height: '100%', width: '100%' }}
                min={cartEvent?.startTime} // Set minimum time
                max={cartEvent?.endTime} // Set maximum time
                messages={messages}
                view={currentView}
                views={['month', 'week', 'agenda']}
                onView={view => setCurrentView(view)}
                onRangeChange={onRangeChange}
                components={{
                    event: EventWrapper,
                    toolbar: CustomToolbar,
                    view: CustomEventAgenda,
                    agenda: {
                        event: CustomEventAgenda
                    },
                    // ... other custom components
                }}
                dayPropGetter={(date) => {
                    // Highlight the current day
                    // if (date.toDateString() === new Date().toDateString()) {
                    //     return {
                    //         style: {
                    //             // white-500 from Tailwind CSS
                    //             backgroundColor: '#f9fafb',
                    //             color: 'white'
                    //         }
                    //     };
                    // }
                    if (blockedDates[0] <= date && date <= blockedDates[blockedDates.length - 1]) {
                        return {
                            style: {
                                // red-100 from Tailwind CSS
                                backgroundColor: '#fee2e2',
                                color: 'white'
                            }
                        };
                    }
                }}
                eventPropGetter={(eventStyleGetter)}
                date={date}
                showAllEvents={true}
                onNavigate={setDate}
                className="rounded-lg shadow-lg"
                longPressThreshold={150} // default value 250
            />
            {isModalOpen && (
                <div className="fixed inset-0 flex items-center justify-center z-50">
                    <div className="modal-content">
                        <AvailabilityForm
                            publisherId={publisherId}
                            existingItems={selectedEvents}
                            date={date}
                            onDone={handleDialogClose}
                            inline={true}
                            cartEvent={cartEvent}
                            lockedBeforeDate={editLockedBefore}
                        // Pass other props as needed
                        />
                    </div>
                    <div className="fixed inset-0 bg-black opacity-50" onClick={handleCancel}></div>
                </div>
            )}
        </>
    );
};

export default AvCalendar;
