import React, {useCallback, useMemo, useRef} from 'react';
import {Dimensions, StyleSheet, Text, TouchableOpacity, View,} from 'react-native';
import {
    addDays,
    addMonths,
    eachDayOfInterval,
    endOfMonth,
    format,
    isSameDay,
    isSameMonth,
    isWithinInterval,
    startOfMonth,
} from 'date-fns';
import {useGetEvents} from "@/hooks/firebase/useGetEvents";
import {useSetAtom} from "jotai";
import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms";
import {useAuthContext} from "@/contexts/AuthContext";
import {FlashList} from "@shopify/flash-list";
import * as Device from "expo-device";
import {CalendarController} from "@/components/pages/calendar/CalendarController";

interface CalendarEvent {
    id: string;
    title: string;
    start: Date;
    end: Date;
    color?: string;
}

interface CustomMonthCalendarProps {
    weekStartsOn?: 0 | 1;
}

const DAYS_OF_WEEK = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const MAX_VISIBLE_EVENTS = 3;
const CENTER_MONTH_INDEX = 12;


const Event = React.memo(({event, onPress}: { event: CalendarEvent; onPress: () => void }) => (
    <TouchableOpacity
        style={[styles.event, {backgroundColor: event?.eventColor || '#6200ee'}]}
        onPress={onPress}
    >
        <Text style={styles.eventText} numberOfLines={1}>
            {event.title}
        </Text>
    </TouchableOpacity>
));

interface CalendarEvent {
    id: string;
    title: string;
    start: Date;
    end: Date;
    color?: string;
    weekPosition?: number;
}

const MultiDayEvent = React.memo(({
                                      event,
                                      isStart,
                                      isEnd,
                                      onPress,
                                  }: {
    event: CalendarEvent;
    dayWidth: number;
    isStart: boolean;
    isEnd: boolean;
    onPress: () => void;
}) => {
    const style = {
        position: 'absolute' as const,
        height: 14,
        backgroundColor: event?.eventColor || '#6200ee',
        padding: 2,
        zIndex: 1,
        left: isStart ? 4 : -0.5, // Extend slightly into the border
        right: isEnd ? 4 : -0.5,  // Extend slightly into the border
        top: event.weekPosition ? event.weekPosition * 24 : 0,
        borderRadius: 4,
        borderTopLeftRadius: isStart ? 4 : 0,
        borderBottomLeftRadius: isStart ? 4 : 0,
        borderTopRightRadius: isEnd ? 4 : 0,
        borderBottomRightRadius: isEnd ? 4 : 0,
        justifyContent: 'center',
    };

    return (
        <TouchableOpacity style={style} onPress={onPress}>
            {isStart && (
                <Text style={[styles.eventText]} numberOfLines={1}>
                    {event.title}
                </Text>
            )}
        </TouchableOpacity>
    );
});

const Day = React.memo((
    {
        date,
        events,
        multiDayEvents,
        dayWidth,
        onPress
    }: {
        date: Date;
        events: CalendarEvent[];
        multiDayEvents: CalendarEvent[];
        dayWidth: number;
        onPress: (date: Date) => void;
    }) => {
    const isCurrentMonth = isSameMonth(date, new Date());
    const isToday = isSameDay(date, new Date());

    const remainingSlots = Math.max(0, MAX_VISIBLE_EVENTS - multiDayEvents.length);
    const singleDayEvents = events.filter(event => !event.isMultiDay);
    const visibleSingleDayEvents = singleDayEvents.slice(0, remainingSlots);
    const totalHiddenEvents = singleDayEvents.length - remainingSlots;

    const maxMultiDayPosition = multiDayEvents.length > 0
        ? Math.max(...multiDayEvents.map(e => e.weekPosition || 0)) + 1
        : 0;
    const multiDayEventsHeight = maxMultiDayPosition * 16; // Height for multi-day events

    return (
        <View style={[styles.day, {width: dayWidth}]}>
            <TouchableOpacity
                style={styles.dayContent}
                onPress={() => onPress(date)}
            >
                <View style={[
                    styles.dateContainer,
                    isToday && {backgroundColor: events?.[0]?.eventColor},
                ]}>
                    <Text style={[
                        styles.dateText,
                        !isCurrentMonth && styles.outsideMonthText,
                        isToday && styles.todayText,
                    ]}>
                        {format(date, 'd')}
                    </Text>
                </View>

                {/* Multi-day events container */}
                <View style={[styles.multiDayContainer, {height: multiDayEventsHeight}]}>
                    {multiDayEvents.map(event => (
                        <MultiDayEvent
                            key={event.id}
                            event={event}
                            dayWidth={dayWidth}
                            isStart={isSameDay(date, event.start)}
                            isEnd={isSameDay(date, event.end)}
                            onPress={() => onPress(event.start)}
                        />
                    ))}
                </View>

                {/* Single-day events container */}
                <View style={[styles.singleDayContainer, {marginTop: multiDayEventsHeight}]}>
                    {visibleSingleDayEvents.map(event => (
                        <Event
                            key={event.id}
                            event={event}
                            onPress={() => onPress(event.start)}
                        />
                    ))}
                    {totalHiddenEvents > 0 && (
                        <Text style={styles.moreEvents}>
                            {totalHiddenEvents} More
                        </Text>
                    )}
                </View>
            </TouchableOpacity>
        </View>
    );
});

export const MonthCalendar: React.FC<CustomMonthCalendarProps> = () => {
    const {data: rawEvents} = useGetEvents();
    const setSelectedDate = useSetAtom(selectedDateAtom);
    const setMode = useSetAtom(modeAtom);
    const {profileData} = useAuthContext();

    const scrollViewRef = useRef<FlashList<any>>(null);
    const isTablet = Device.deviceType === Device.DeviceType.TABLET;
    const screenWidth = isTablet ? Dimensions.get('window').width * 0.89 : Dimensions.get('window').width;
    const screenHeight = isTablet ? Dimensions.get('window').height * 0.89 : Dimensions.get('window').height;
    const dayWidth = screenWidth / 7;
    const centerMonth = useRef(new Date());

    const weekStartsOn = profileData?.firstDayOfWeek === "Sundays" ? 0 : 1;

    const onDayPress = useCallback(
        (date: Date) => {
            date && setSelectedDate(date);
            setTimeout(() => {
                setMode("day");
            }, 100)
        },
        [setSelectedDate, setMode]
    );

    const getMonthData = useCallback((date: Date) => {
        const start = startOfMonth(date);
        const end = endOfMonth(date);
        const days = eachDayOfInterval({start, end});

        const firstDay = days[0];
        const startPadding = [];
        let startDay = firstDay.getDay();
        while (startDay !== weekStartsOn) {
            startDay = (startDay - 1 + 7) % 7;
            startPadding.unshift(addDays(firstDay, -startPadding.length - 1));
        }

        const lastDay = days[days.length - 1];
        const endPadding = [];
        let endDay = lastDay.getDay();
        while (endDay !== (weekStartsOn + 6) % 7) {
            endDay = (endDay + 1) % 7;
            endPadding.push(addDays(lastDay, endPadding.length + 1));
        }

        return [...startPadding, ...days, ...endPadding];
    }, [weekStartsOn]);

    const monthsToRender = useMemo(() => {
        const months = [];
        for (let i = -CENTER_MONTH_INDEX; i <= CENTER_MONTH_INDEX; i++) {
            const monthDate = addMonths(centerMonth.current, i);
            months.push({
                date: monthDate,
                days: getMonthData(monthDate)
            });
        }
        return months;
    }, [getMonthData, rawEvents]);

    const processedEvents = useMemo(() => {
        if (!rawEvents?.length) return {
            eventMap: new Map(),
            multiDayEvents: []
        };

        const eventMap = new Map();
        const multiDayEvents: CalendarEvent[] = [];

        rawEvents.forEach((event) => {
            if (!event?.start || !event?.end) return;

            const startDate = event.start instanceof Date ? event.start : new Date(event.start);
            const endDate = event.end instanceof Date ? event.end : new Date(event.end);

            if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) return;

            const duration = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));

            if (duration > 1) {
                multiDayEvents.push({
                    ...event,
                    isMultiDay: true,
                    start: startDate,
                    end: endDate
                });
            } else {
                const dateStr = format(startDate, 'yyyy-MM-dd');
                const existing = eventMap.get(dateStr) || [];
                eventMap.set(dateStr, [...existing, {...event, start: startDate, end: endDate}]);
            }
        });

        multiDayEvents.sort((a, b) => {
            if (!a.start || !b.start || !a.end || !b.end) return 0;
            const durationA = a.end.getTime() - a.start.getTime();
            const durationB = b.end.getTime() - b.start.getTime();
            return durationB - durationA;
        });

        return {eventMap, multiDayEvents};
    }, [rawEvents]);

    const getMultiDayEventsForDay = useCallback((date: Date) => {
        return processedEvents.multiDayEvents.filter(event => {
            if (!event.start || !event.end) return false;
            return isWithinInterval(date, {
                start: event.start,
                end: event.end
            });
        });
    }, [processedEvents.multiDayEvents]);

    const getEventsForDay = useCallback((date: Date) => {
        const dateStr = format(date, 'yyyy-MM-dd');
        return processedEvents.eventMap.get(dateStr) || [];
    }, [processedEvents.eventMap]);

    const sortedDaysOfWeek = useMemo(() => {
        const days = [...DAYS_OF_WEEK];
        return days.slice(weekStartsOn).concat(days.slice(0, weekStartsOn));
    }, [weekStartsOn]);

    const renderMonth = useCallback(({item}: { item: MonthData }) => (
        <Month
            date={item.date}
            days={item.days}
            getEventsForDay={getEventsForDay}
            getMultiDayEventsForDay={getMultiDayEventsForDay}
            dayWidth={dayWidth}
            onPress={onDayPress}
            screenWidth={screenWidth}
            sortedDaysOfWeek={sortedDaysOfWeek}
        />
    ), [getEventsForDay, getMultiDayEventsForDay, dayWidth, onDayPress, screenWidth, weekStartsOn]);

    return (
        <View style={styles.container}>
            <CalendarController
                scrollViewRef={scrollViewRef}
                centerMonthIndex={CENTER_MONTH_INDEX}
            />
            <FlashList
                ref={scrollViewRef}
                data={monthsToRender}
                renderItem={renderMonth}
                keyExtractor={keyExtractor}
                horizontal
                pagingEnabled
                showsHorizontalScrollIndicator={false}
                initialScrollIndex={CENTER_MONTH_INDEX}
                removeClippedSubviews={true}
                estimatedItemSize={screenWidth}
                estimatedListSize={{width: screenWidth, height: screenHeight * 0.9}}
                maintainVisibleContentPosition={{
                    minIndexForVisible: 0,
                    autoscrollToTopThreshold: 10,
                }}
            />
        </View>
    );
};

type MonthData = {
    date: Date;
    days: Date[];
};

const keyExtractor = (item: MonthData, index: number) => `month-${index}`;

const Month = React.memo(({
                              date,
                              days,
                              getEventsForDay,
                              getMultiDayEventsForDay,
                              dayWidth,
                              onPress,
                              screenWidth,
                              sortedDaysOfWeek
                          }: {
    date: Date;
    days: Date[];
    getEventsForDay: (date: Date) => CalendarEvent[];
    getMultiDayEventsForDay: (date: Date) => CalendarEvent[];
    dayWidth: number;
    onPress: (date: Date) => void;
    screenWidth: number;
    sortedDaysOfWeek: string[];
}) => {
    const weeks = useMemo(() => {
        const result = [];
        for (let i = 0; i < days.length; i += 7) {
            result.push(days.slice(i, i + 7));
        }
        return result;
    }, [days]);

    const eventPositions = useMemo(() => {
        const positions = new Map<string, number>();
        const weekTracking = new Map<number, Set<string>>();

        weeks.forEach((week, weekIndex) => {
            const activeEvents = new Set<string>();

            week.forEach(day => {
                const events = getMultiDayEventsForDay(day);
                events.forEach(event => {
                    activeEvents.add(event.id);
                });
            });

            weekTracking.set(weekIndex, activeEvents);

            activeEvents.forEach(eventId => {
                if (!positions.has(eventId)) {
                    const prevWeekEvents = weekIndex > 0 ? weekTracking.get(weekIndex - 1) : new Set<string>();
                    const usedPositions = new Set<number>();

                    if (prevWeekEvents) {
                        prevWeekEvents.forEach(prevEventId => {
                            if (activeEvents.has(prevEventId)) {
                                usedPositions.add(positions.get(prevEventId) || 0);
                            }
                        });
                    }

                    let position = 0;
                    while (usedPositions.has(position)) {
                        position++;
                    }
                    positions.set(eventId, position);
                }
            });
        });

        return positions;
    }, [weeks, getMultiDayEventsForDay]);

    return (
        <View style={[styles.scrollView, {width: screenWidth}]}>
            <View style={styles.monthHeader}>
                <Text style={styles.monthText}>{format(date, 'MMMM yyyy')}</Text>
                <View style={styles.weekDayRow}>
                    {sortedDaysOfWeek.map((day, index) => (
                        <Text key={index} style={styles.weekDayText}>{day}</Text>
                    ))}
                </View>
            </View>
            <View style={styles.daysGrid}>
                {weeks.map((week, weekIndex) => (
                    <React.Fragment key={weekIndex}>
                        {week.map((date, dayIndex) => {
                            const multiDayEvents = getMultiDayEventsForDay(date).map(event => ({
                                ...event,
                                weekPosition: eventPositions.get(event.id) || 0
                            }));

                            return (
                                <Day
                                    key={`${weekIndex}-${dayIndex}`}
                                    date={date}
                                    events={getEventsForDay(date)}
                                    multiDayEvents={multiDayEvents}
                                    dayWidth={dayWidth}
                                    onPress={onPress}
                                />
                            );
                        })}
                    </React.Fragment>
                ))}
            </View>
        </View>
    );
});

const HEADER_HEIGHT = 40;

const styles = StyleSheet.create({
    multiDayContainer: {
        position: 'absolute',
        top: 29,
        left: 0,
        right: 0,
        bottom: 0,
        zIndex: 1,
    },
    dayContent: {
        flex: 1,
        padding: 4, // Move padding here instead
    },
    eventsContainer: {
        flex: 1,
        marginTop: 2,
        position: 'relative',
    },
    event: {
        borderRadius: 4,
        padding: 1,
        marginVertical: 1,
        height: 14,
    },
    eventText: {
        fontSize: 10,
        color: '#fff',
        fontWeight: '500',
    },
    day: {
        height: '14%',
        padding: 0,
        borderWidth: 0.5,
        borderColor: '#eee',
        position: 'relative',
        overflow: 'visible',
    },
    container: {
        flex: 1,
        height: '100%',
    },
    header: {
        flexDirection: 'row',
        justifyContent: 'center',
        paddingHorizontal: 16,
        paddingVertical: 8,
    },
    scrollView: {
        flex: 1,
    },
    monthContainer: {
        flex: 1,
    },
    daysGrid: {
        flex: 1,
        flexDirection: 'row',
        flexWrap: 'wrap',
        // justifyContent: 'center'
    },
    weekDay: {
        alignItems: 'center',
        justifyContent: 'center',
        height: HEADER_HEIGHT,
    },
    scrollContent: {
        flex: 1,
    },
    weekDayText: {
        fontSize: 12,
        fontWeight: '600',
        color: '#666',
    },
    dateContainer: {
        minWidth: 20,
        height: 24,
        alignItems: 'center',
        justifyContent: 'center',
        borderRadius: 12,
    },
    todayContainer: {
        backgroundColor: '#6200ee',
    },
    dateText: {
        fontSize: 12,
        fontWeight: '500',
        color: '#333',
    },
    todayText: {
        color: '#fff',
    },
    outsideMonthText: {
        color: '#ccc',
    },
    moreEvents: {
        fontSize: 10,
        color: '#666',
        textAlign: 'center',
    },
    monthHeader: {
        paddingVertical: 12,
    },
    monthText: {
        fontSize: 16,
        fontWeight: '600',
        color: '#333',
        textAlign: 'center',
        marginBottom: 8,
    },
    weekDayRow: {
        flexDirection: 'row',
        justifyContent: 'space-around',
        paddingHorizontal: 0,
    },
});

export default MonthCalendar;