"use client";

import { createContext, useContext, useState, useCallback, useEffect, ReactNode } from "react";
import { TokenResponse, useGoogleLogin } from "@react-oauth/google";

interface CalendarEvent {
  id: string;
  summary: string;
  description?: string;
  start: {
    dateTime?: string;
    date?: string;
  };
  end: {
    dateTime?: string;
    date?: string;
  };
  location?: string;
  calendarId: string;
  calendarName: string;
}

interface Calendar {
  id: string;
  summary: string;
  primary?: boolean;
  selected?: boolean;
}

interface CalendarContextType {
  events: CalendarEvent[];
  calendars: Calendar[];
  isAuthenticated: boolean;
  isLoading: boolean;
  lastSynced: Date | null;
  selectedCalendars: string[];
  error: string | null;
  login: () => void;
  logout: () => void;
  refreshEvents: () => Promise<void>;
  toggleCalendar: (calendarId: string) => void;
  selectAllCalendars: () => void;
  deselectAllCalendars: () => void;
}

const CalendarContext = createContext<CalendarContextType | undefined>(undefined);

const STORAGE_KEY = "google_calendar_tokens";
const CACHE_KEY = "calendar_events_cache";
const CALENDARS_CACHE_KEY = "calendar_list_cache";
const CACHE_DURATION = 1000 * 60 * 15; // 15 minutes

interface CachedData {
  events: CalendarEvent[];
  timestamp: number;
  selectedCalendars: string[];
}

interface CachedCalendars {
  calendars: Calendar[];
  timestamp: number;
}

export function CalendarProvider({ children }: { children: ReactNode }) {
  const [events, setEvents] = useState<CalendarEvent[]>([]);
  const [calendars, setCalendars] = useState<Calendar[]>([]);
  const [accessToken, setAccessToken] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [lastSynced, setLastSynced] = useState<Date | null>(null);
  const [selectedCalendars, setSelectedCalendars] = useState<string[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [isInitialized, setIsInitialized] = useState(false);

  const isAuthenticated = !!accessToken;

  // Load cached data on mount
  useEffect(() => {
    if (typeof window === "undefined" || isInitialized) return;

    const tokens = localStorage.getItem(STORAGE_KEY);
    if (tokens) {
      try {
        const parsed = JSON.parse(tokens);
        if (parsed.access_token) {
          setAccessToken(parsed.access_token);
        }
      } catch (e) {
        console.error("Failed to parse stored tokens");
      }
    }

    // Load cached events
    const cached = localStorage.getItem(CACHE_KEY);
    if (cached) {
      try {
        const parsed: CachedData = JSON.parse(cached);
        const now = Date.now();
        if (now - parsed.timestamp < CACHE_DURATION * 4) { // Cache valid for 1 hour
          setEvents(parsed.events);
          setSelectedCalendars(parsed.selectedCalendars || []);
          setLastSynced(new Date(parsed.timestamp));
        }
      } catch (e) {
        console.error("Failed to parse cached events");
      }
    }

    // Load cached calendars
    const cachedCalendars = localStorage.getItem(CALENDARS_CACHE_KEY);
    if (cachedCalendars) {
      try {
        const parsed: CachedCalendars = JSON.parse(cachedCalendars);
        const now = Date.now();
        if (now - parsed.timestamp < CACHE_DURATION * 4) {
          setCalendars(parsed.calendars);
        }
      } catch (e) {
        console.error("Failed to parse cached calendars");
      }
    }

    setIsInitialized(true);
  }, [isInitialized]);

  const fetchCalendars = useCallback(async (token: string) => {
    try {
      const response = await fetch(
        "https://www.googleapis.com/calendar/v3/users/me/calendarList",
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      );

      if (!response.ok) {
        if (response.status === 401) {
          logout();
          throw new Error("Session expired. Please sign in again.");
        }
        throw new Error("Failed to fetch calendars");
      }

      const data = await response.json();
      const calendarList: Calendar[] = data.items.map((cal: any) => ({
        id: cal.id,
        summary: cal.summary,
        primary: cal.primary || false,
        selected: cal.selected !== false,
      }));

      setCalendars(calendarList);
      
      // Select primary calendar and any previously selected
      const primaryCal = calendarList.find((c) => c.primary);
      const defaultSelected = primaryCal ? [primaryCal.id] : calendarList.slice(0, 1).map(c => c.id);
      
      // Merge with cached selection if available
      const cached = localStorage.getItem(CACHE_KEY);
      let finalSelection = defaultSelected;
      if (cached) {
        try {
          const parsed: CachedData = JSON.parse(cached);
          const validCached = parsed.selectedCalendars?.filter(id => 
            calendarList.some(c => c.id === id)
          ) || [];
          if (validCached.length > 0) {
            finalSelection = validCached;
          }
        } catch (e) {}
      }
      
      setSelectedCalendars(finalSelection);

      // Cache calendars
      const cacheData: CachedCalendars = {
        calendars: calendarList,
        timestamp: Date.now(),
      };
      localStorage.setItem(CALENDARS_CACHE_KEY, JSON.stringify(cacheData));

      return calendarList;
    } catch (err) {
      console.error("Error fetching calendars:", err);
      throw err;
    }
  }, []);

  const fetchEventsForCalendar = useCallback(async (
    token: string,
    calendarId: string,
    calendarName: string
  ): Promise<CalendarEvent[]> => {
    const now = new Date();
    const oneMonthLater = new Date();
    oneMonthLater.setMonth(oneMonthLater.getMonth() + 1);

    const timeMin = now.toISOString();
    const timeMax = oneMonthLater.toISOString();

    const encodedCalendarId = encodeURIComponent(calendarId);
    const url = `https://www.googleapis.com/calendar/v3/calendars/${encodedCalendarId}/events?` +
      new URLSearchParams({
        timeMin,
        timeMax,
        singleEvents: "true",
        orderBy: "startTime",
        maxResults: "100",
      });

    const response = await fetch(url, {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    if (!response.ok) {
      if (response.status === 401) {
        logout();
        throw new Error("Session expired. Please sign in again.");
      }
      console.error(`Failed to fetch events for calendar ${calendarId}:`, response.statusText);
      return [];
    }

    const data = await response.json();
    return (data.items || []).map((event: any) => ({
      id: event.id,
      summary: event.summary || "(No title)",
      description: event.description,
      start: event.start,
      end: event.end,
      location: event.location,
      calendarId,
      calendarName,
    }));
  }, []);

  const refreshEvents = useCallback(async () => {
    if (!accessToken) return;

    setIsLoading(true);
    setError(null);

    try {
      // Fetch calendars if not already loaded
      if (calendars.length === 0) {
        await fetchCalendars(accessToken);
      }

      // Fetch events from selected calendars
      const activeCalendars = calendars.filter(
        (cal) => selectedCalendars.includes(cal.id) || (selectedCalendars.length === 0 && cal.primary)
      );

      if (activeCalendars.length === 0 && calendars.length > 0) {
        // If no calendars selected but we have calendars, use primary
        const primary = calendars.find(c => c.primary) || calendars[0];
        activeCalendars.push(primary);
      }

      const allEvents: CalendarEvent[] = [];
      for (const calendar of activeCalendars) {
        try {
          const events = await fetchEventsForCalendar(
            accessToken,
            calendar.id,
            calendar.summary
          );
          allEvents.push(...events);
        } catch (err) {
          console.error(`Error fetching events for ${calendar.summary}:`, err);
        }
      }

      // Sort by start time
      allEvents.sort((a, b) => {
        const dateA = new Date(a.start.dateTime || a.start.date || 0);
        const dateB = new Date(b.start.dateTime || b.start.date || 0);
        return dateA.getTime() - dateB.getTime();
      });

      setEvents(allEvents);
      const now = new Date();
      setLastSynced(now);

      // Cache the events
      const cacheData: CachedData = {
        events: allEvents,
        timestamp: now.getTime(),
        selectedCalendars,
      };
      localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData));
    } catch (err) {
      const message = err instanceof Error ? err.message : "Failed to refresh events";
      setError(message);
      console.error("Error refreshing events:", err);
    } finally {
      setIsLoading(false);
    }
  }, [accessToken, calendars, selectedCalendars, fetchCalendars, fetchEventsForCalendar]);

  // Auto-refresh when authenticated and initialized
  useEffect(() => {
    if (isAuthenticated && isInitialized && events.length === 0) {
      refreshEvents();
    }
  }, [isAuthenticated, isInitialized, refreshEvents, events.length]);

  const login = useGoogleLogin({
    onSuccess: async (tokenResponse: TokenResponse) => {
      setAccessToken(tokenResponse.access_token);
      localStorage.setItem(STORAGE_KEY, JSON.stringify(tokenResponse));
      setError(null);
      
      // Fetch calendars and events immediately after login
      try {
        await fetchCalendars(tokenResponse.access_token);
      } catch (err) {
        console.error("Error fetching calendars after login:", err);
      }
    },
    onError: () => {
      setError("Failed to sign in with Google");
    },
    scope: "https://www.googleapis.com/auth/calendar.readonly",
  });

  const logout = useCallback(() => {
    setAccessToken(null);
    setEvents([]);
    setCalendars([]);
    setSelectedCalendars([]);
    setLastSynced(null);
    localStorage.removeItem(STORAGE_KEY);
    localStorage.removeItem(CACHE_KEY);
    localStorage.removeItem(CALENDARS_CACHE_KEY);
    setError(null);
  }, []);

  const toggleCalendar = useCallback((calendarId: string) => {
    setSelectedCalendars((prev) => {
      const newSelection = prev.includes(calendarId)
        ? prev.filter((id) => id !== calendarId)
        : [...prev, calendarId];
      
      // Update cache with new selection
      const cached = localStorage.getItem(CACHE_KEY);
      if (cached) {
        try {
          const parsed: CachedData = JSON.parse(cached);
          parsed.selectedCalendars = newSelection;
          localStorage.setItem(CACHE_KEY, JSON.stringify(parsed));
        } catch (e) {}
      }
      
      return newSelection;
    });
  }, []);

  const selectAllCalendars = useCallback(() => {
    const allIds = calendars.map((c) => c.id);
    setSelectedCalendars(allIds);
  }, [calendars]);

  const deselectAllCalendars = useCallback(() => {
    setSelectedCalendars([]);
  }, []);

  return (
    <CalendarContext.Provider
      value={{
        events,
        calendars,
        isAuthenticated,
        isLoading,
        lastSynced,
        selectedCalendars,
        error,
        login,
        logout,
        refreshEvents,
        toggleCalendar,
        selectAllCalendars,
        deselectAllCalendars,
      }}
    >
      {children}
    </CalendarContext.Provider>
  );
}

export function useCalendar() {
  const context = useContext(CalendarContext);
  if (context === undefined) {
    throw new Error("useCalendar must be used within a CalendarProvider");
  }
  return context;
}
