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

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 CALENDAR_BUFFER_DAYS = 40;

export const MonthCalendar: React.FC<CustomMonthCalendarProps> = () => {
    const {data: rawEvents, isLoading} = useGetEvents();
    const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
    const setSelectedNewEndDate = useSetAtom(selectedNewEventDateAtom);
    const [mode, setMode] = useAtom(modeAtom);
    const {profileData} = useAuthContext();

    const scrollViewRef = useRef<ScrollView>(null);
    const isTablet = Device.deviceType === Device.DeviceType.TABLET;
    const screenWidth = isTablet ? Dimensions.get('window').width * 0.89 : Dimensions.get('window').width;
    const dayWidth = (screenWidth - 32) / 7;
    const isScrolling = useRef(false);
    const currentScrollX = useRef(screenWidth);

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

    const events = useMemo(() => {
        if (!rawEvents?.length) return [];
        if (!selectedDate) return [];

        const rangeStart = subDays(selectedDate, CALENDAR_BUFFER_DAYS);
        const rangeEnd = addDays(selectedDate, CALENDAR_BUFFER_DAYS);

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

            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 false;
            }

            return startDate <= rangeEnd && endDate >= rangeStart;
        });
    }, [rawEvents, selectedDate]);

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

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

        // Add padding days at the start
        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));
        }

        // Add padding days at the end
        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 prevMonth = subMonths(selectedDate, 1);
        const nextMonth = addMonths(selectedDate, 1);

        return [
            {
                date: prevMonth,
                days: getMonthData(prevMonth)
            },
            {
                date: selectedDate,
                days: getMonthData(selectedDate)
            },
            {
                date: nextMonth,
                days: getMonthData(nextMonth)
            }
        ];
    }, [selectedDate, getMonthData]);

    const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
        if (!isScrolling.current) {
            currentScrollX.current = event.nativeEvent.contentOffset.x;
        }
    }, []);

    const onMomentumScrollEnd = useCallback(() => {
        if (isScrolling.current) return;

        const pageWidth = screenWidth;
        const currentX = currentScrollX.current;

        if (currentX < pageWidth / 2) {
            isScrolling.current = true;
            setSelectedDate(prev => {
                const newDate = subMonths(prev, 1);
                // Immediately scroll back to center
                scrollViewRef.current?.scrollTo({
                    x: pageWidth,
                    animated: false,
                });
                isScrolling.current = false;
                currentScrollX.current = pageWidth;
                return newDate;
            });
        } else if (currentX > pageWidth * 1.5) {
            isScrolling.current = true;
            setSelectedDate(prev => {
                const newDate = addMonths(prev, 1);
                // Immediately scroll back to center
                scrollViewRef.current?.scrollTo({
                    x: pageWidth,
                    animated: false,
                });
                isScrolling.current = false;
                currentScrollX.current = pageWidth;
                return newDate;
            });
        }
    }, [screenWidth, setSelectedDate]);

    const getEventsForDay = useCallback((date: Date) => {
        return events?.filter(event =>
            isSameDay(new Date(event.start), date)
        ) ?? [];
    }, [events]);

    const renderEvent = useCallback((event: CalendarEvent) => {
        return (
            <TouchableOpacity
                key={event.id}
                style={[styles.event, {backgroundColor: event.color || '#6200ee'}]}
                onPress={() => onDayPress(event.start)}
            >
                <Text style={styles.eventText} numberOfLines={1}>
                    {event.title}
                </Text>
            </TouchableOpacity>
        );
    }, [onDayPress]);

    const renderDay = useCallback((date: Date, index: number) => {
        const dayEvents = getEventsForDay(date);
        const isCurrentMonth = isSameMonth(date, selectedDate);
        const isToday = isSameDay(date, new Date());
        const hasMoreEvents = dayEvents.length > MAX_VISIBLE_EVENTS;

        return (
            <TouchableOpacity
                key={index}
                style={[styles.day, {width: dayWidth}]}
                onPress={() => onDayPress(date)}
            >
                <View style={[
                    styles.dateContainer,
                    isToday && styles.todayContainer,
                ]}>
                    <Text style={[
                        styles.dateText,
                        !isCurrentMonth && styles.outsideMonthText,
                        isToday && styles.todayText,
                    ]}>
                        {format(date, 'd')}
                    </Text>
                </View>
                <View style={styles.eventsContainer}>
                    {dayEvents.slice(0, MAX_VISIBLE_EVENTS).map(renderEvent)}
                    {hasMoreEvents && (
                        <Text style={styles.moreEvents}>
                            {dayEvents.length - MAX_VISIBLE_EVENTS} More
                        </Text>
                    )}
                </View>
            </TouchableOpacity>
        );
    }, [dayWidth, selectedDate, getEventsForDay, onDayPress, renderEvent]);

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

    return (
        <View style={styles.container}>
            <View style={styles.header}>
                {sortedDaysOfWeek.map((day, index) => (
                    <View key={index} style={[styles.weekDay, {width: `${100 / 7}%`}]}>
                        <Text style={styles.weekDayText}>{day}</Text>
                    </View>
                ))}
            </View>
            <ScrollView
                ref={scrollViewRef}
                horizontal
                pagingEnabled
                showsHorizontalScrollIndicator={false}
                onScroll={handleScroll}
                onMomentumScrollEnd={onMomentumScrollEnd}
                scrollEventThrottle={16}
                contentOffset={{x: screenWidth, y: 0}}
                style={{flex: 1}}
            >
                {monthsToRender.map(({date, days}, monthIndex) => (
                    <View
                        key={monthIndex}
                        style={[styles.scrollView, {width: screenWidth}]}
                    >
                        <View style={styles.daysGrid}>
                            {days.map((day, index) => renderDay(day, index))}
                        </View>
                    </View>
                ))}
            </ScrollView>
        </View>
    );
};

const HEADER_HEIGHT = 40;

const styles = StyleSheet.create({
    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'
    },
    day: {
        height: '15%', // 100% / 6 weeks
        padding: 4,
        borderWidth: 0.5,
        borderColor: '#eee',
    },
    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',
    },
    eventsContainer: {
        flex: 1,
        marginTop: 2,
    },
    event: {
        borderRadius: 4,
        padding: 2,
        marginVertical: 1,
    },
    eventText: {
        fontSize: 10,
        color: '#fff',
    },
    moreEvents: {
        fontSize: 10,
        color: '#666',
        textAlign: 'center',
    },
});