import { DeviceType } from 'expo-device';
import * as Device from 'expo-device';
import { format } from 'date-fns';
import { useQuery } from "@tanstack/react-query";

interface EventTimestamp {
    seconds: number;
    nanoseconds: number;
}

interface Event {
    id: string;
    title: string;
    start: Date | EventTimestamp;
    startDate?: EventTimestamp;
    end: Date | EventTimestamp;
    endDate?: EventTimestamp;
    allDay: boolean;
    eventColor: string;
    attendees?: string[];
    creatorId?: string;
}

interface FormattedEvent {
    id: string;
    title: string;
    start: { date: string } | { dateTime: string; timeZone: string };
    end: { date: string } | { dateTime: string; timeZone: string };
    color: string;
}

const createEventHash = (event: FormattedEvent): string => {
    const startTime = 'date' in event.start ? event.start.date : event.start.dateTime;
    const endTime = 'date' in event.end ? event.end.date : event.end.dateTime;
    const str = `${startTime}-${endTime}-${event.title}`;

    let hash = 0;
    for (let i = 0; i < str.length; i++) {
        const char = str.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash = hash & hash;
    }
    return hash.toString(36);
};


// Precompute time constants
const DAY_IN_MS = 24 * 60 * 60 * 1000;
const PERIOD_IN_MS = 180 * DAY_IN_MS;
const TIME_ZONE = Intl.DateTimeFormat().resolvedOptions().timeZone;

// Memoize date range calculations
const getDateRangeKey = (timestamp: number) => Math.floor(timestamp / PERIOD_IN_MS);

// Optimized date validation and conversion
const getValidDate = (date: any, timestamp?: EventTimestamp): Date | null => {
    try {
        if (timestamp?.seconds) {
            return new Date(timestamp.seconds * 1000);
        }

        if (date instanceof Date) {
            return date;
        }

        if (typeof date === 'string') {
            return new Date(date);
        }

        return null;
    } catch {
        return null;
    }
};

// Batch process events

const processEvents = async (
    events: Event[],
    selectedDate: Date,
    selectedUser: { uid: string } | null
): Promise<FormattedEvent[]> => {
    if (!events.length) return [];

    const currentRangeKey = getDateRangeKey(selectedDate.getTime());
    const isTablet = Device.deviceType === DeviceType.TABLET;
    const userId = selectedUser?.uid;

    const uniqueEvents = new Map<string, FormattedEvent>();
    const processedHashes = new Set<string>();

    const CHUNK_SIZE = 100;

    for (let i = 0; i < events.length; i += CHUNK_SIZE) {
        const chunk = events.slice(i, i + CHUNK_SIZE);
        await new Promise(resolve => setTimeout(resolve, 0));

        for (const event of chunk) {
            try {
                if (isTablet && userId &&
                    !event.attendees?.includes(userId) &&
                    event.creatorId !== userId) {
                    continue;
                }

                const startDate = getValidDate(event.start, event.startDate);
                if (!startDate) continue;

                const rangeKey = getDateRangeKey(startDate.getTime());
                if (Math.abs(rangeKey - currentRangeKey) > 1) continue;

                const endDate = getValidDate(event.end, event.endDate);
                if (!endDate) continue;

                const formattedEvent = event.allDay ? {
                    id: event.id,
                    title: event.title,
                    start: { date: format(startDate, 'yyyy-MM-dd') },
                    end: { date: format(endDate, 'yyyy-MM-dd') },
                    color: event.eventColor
                } : {
                    id: event.id,
                    title: event.title,
                    start: {
                        dateTime: startDate.toISOString(),
                        timeZone: TIME_ZONE
                    },
                    end: {
                        dateTime: endDate.toISOString(),
                        timeZone: TIME_ZONE
                    },
                    color: event.eventColor
                };

                const hash = createEventHash(formattedEvent);
                if (!processedHashes.has(hash)) {
                    processedHashes.add(hash);
                    uniqueEvents.set(event.id, formattedEvent);
                }

            } catch (error) {
                console.error('Error processing event:', event.id, error);
                continue;
            }
        }
    }

    return Array.from(uniqueEvents.values());
};

export const useFormattedEvents = (
    events: Event[],
    selectedDate: Date,
    selectedUser: { uid: string } | null
) => {
    return useQuery({
        queryKey: ['formattedEvents', events, selectedDate, selectedUser?.uid],
        queryFn: () => processEvents(events, selectedDate, selectedUser),
        enabled: events.length > 0,
        staleTime: Infinity,
        placeholderData: (previousData) => previousData,
        gcTime: Infinity
    });
};