import { ActivityIndicator, Pressable, Text, View } from "react-native";
import {
  DAYS,
  MONTHS,
  Month,
  ExpandedEvent,
  RecurringDeleteMode,
} from "@calchat/shared";
import Header from "../../components/Header";
import { EventCard } from "../../components/EventCard";
import { DeleteEventModal } from "../../components/DeleteEventModal";
import { ModalBase } from "../../components/ModalBase";
import { ScrollableDropdown } from "../../components/ScrollableDropdown";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { router, useFocusEffect } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import { useThemeStore } from "../../stores/ThemeStore";
import BaseBackground from "../../components/BaseBackground";
import { AuthService, EventService } from "../../services";
import { useEventsStore } from "../../stores";
import { useDropdownPosition } from "../../hooks/useDropdownPosition";
import { CaldavConfigService } from "../../services/CaldavConfigService";
import { useCaldavConfigStore } from "../../stores/CaldavConfigStore";

// MonthSelector types and helpers
type MonthItem = {
  id: string; // Format: "YYYY-MM"
  year: number;
  monthIndex: number; // 0-11
  label: string; // e.g. "January 2024"
};

/**
 * Formats a Date object to a string key in YYYY-MM-DD format.
 * Used for grouping and looking up events by date.
 */
const getDateKey = (date: Date): string => {
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
};

const generateMonths = (
  centerYear: number,
  centerMonth: number,
  range: number,
): MonthItem[] => {
  const months: MonthItem[] = [];
  for (let offset = -range; offset <= range; offset++) {
    let year = centerYear;
    let month = centerMonth + offset;

    while (month < 0) {
      month += 12;
      year--;
    }
    while (month > 11) {
      month -= 12;
      year++;
    }

    months.push({
      id: `${year}-${String(month + 1).padStart(2, "0")}`,
      year,
      monthIndex: month,
      label: `${MONTHS[month]} ${year}`,
    });
  }
  return months;
};

const Calendar = () => {
  const [monthIndex, setMonthIndex] = useState(new Date().getMonth());
  const [currentYear, setCurrentYear] = useState(new Date().getFullYear());
  const [selectedDate, setSelectedDate] = useState<Date | null>(null);
  const [overlayVisible, setOverlayVisible] = useState(false);

  // State for delete modal
  const [deleteModalVisible, setDeleteModalVisible] = useState(false);
  const [eventToDelete, setEventToDelete] = useState<ExpandedEvent | null>(
    null,
  );

  const { events, setEvents, deleteEvent } = useEventsStore();

  // Load events from local DB (fast, no network sync)
  const loadEvents = useCallback(async () => {
    try {
      // Calculate first visible day (up to 6 days before month start)
      const firstOfMonth = new Date(currentYear, monthIndex, 1);
      const dayOfWeek = firstOfMonth.getDay();
      const daysFromPrevMonth = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
      const startDate = new Date(
        currentYear,
        monthIndex,
        1 - daysFromPrevMonth,
      );

      // Calculate last visible day (6 weeks * 7 days = 42 days total)
      const endDate = new Date(startDate);
      endDate.setDate(startDate.getDate() + 41);
      endDate.setHours(23, 59, 59);

      const loadedEvents = await EventService.getByDateRange(
        startDate,
        endDate,
      );
      setEvents(loadedEvents);
    } catch (error) {
      console.error("Failed to load events:", error);
    }
  }, [monthIndex, currentYear, setEvents]);

  // Load events from DB on focus
  useFocusEffect(
    useCallback(() => {
      loadEvents();
    }, [loadEvents]),
  );

  // Re-open overlay after back navigation from editEvent
  useFocusEffect(
    useCallback(() => {
      if (selectedDate) {
        setOverlayVisible(true);
      }
    }, [selectedDate]),
  );

  // Group events by date (YYYY-MM-DD format)
  // Multi-day events are added to all days they span
  const eventsByDate = useMemo(() => {
    const map = new Map<string, ExpandedEvent[]>();
    events.forEach((e) => {
      const start = new Date(e.occurrenceStart);
      const end = new Date(e.occurrenceEnd);

      // Iterate through each day the event spans
      const current = new Date(start);
      current.setHours(0, 0, 0, 0);
      while (current <= end) {
        const key = getDateKey(current);
        if (!map.has(key)) map.set(key, []);
        map.get(key)!.push(e);
        current.setDate(current.getDate() + 1);
      }
    });
    return map;
  }, [events]);

  const changeMonth = (delta: number) => {
    setMonthIndex((prev) => {
      const newIndex = prev + delta;
      if (newIndex > 11) {
        setCurrentYear((y) => y + 1);
        return 0;
      }
      if (newIndex < 0) {
        setCurrentYear((y) => y - 1);
        return 11;
      }
      return newIndex;
    });
  };

  const handleDayPress = (date: Date) => {
    setSelectedDate(date);
    setOverlayVisible(true);
  };

  const handleCloseOverlay = () => {
    setSelectedDate(null);
    setOverlayVisible(false);
  };

  const handleCreateEvent = () => {
    setOverlayVisible(false);
    router.push({
      pathname: "/editEvent",
      params: { date: selectedDate?.toISOString() },
    });
  };

  const handleEditEvent = (event?: ExpandedEvent) => {
    router.push({
      pathname: "/editEvent",
      params: {
        mode: "calendar",
        id: event?.id,
      },
    });
  };

  const handleDeleteEvent = (event: ExpandedEvent) => {
    // Show delete modal for both recurring and non-recurring events
    setEventToDelete(event);
    setDeleteModalVisible(true);
  };

  const handleDeleteConfirm = async (mode: RecurringDeleteMode) => {
    if (!eventToDelete) return;

    setDeleteModalVisible(false);
    const event = eventToDelete;
    const occurrenceDate = getDateKey(new Date(event.occurrenceStart));

    try {
      if (event.recurrenceRule) {
        // Recurring event: use mode and occurrenceDate
        await EventService.delete(event.id, mode, occurrenceDate);
        // Reload events to reflect changes
        await loadEvents();
      } else {
        // Non-recurring event: simple delete
        await EventService.delete(event.id);
        deleteEvent(event.id);
      }
    } catch (error) {
      console.error("Failed to delete event:", error);
    }
    // Note: Don't clear eventToDelete here - it will be overwritten when opening a new modal.
    // Clearing it during fade-out animation causes the modal content to flash from recurring to single.
  };

  const handleDeleteCancel = () => {
    setDeleteModalVisible(false);
    // Note: Don't clear eventToDelete - keeps modal content stable during fade-out animation
  };

  // Get events for selected date
  const selectedDateEvents = useMemo(() => {
    if (!selectedDate) return [];
    const key = getDateKey(selectedDate);
    return eventsByDate.get(key) || [];
  }, [selectedDate, eventsByDate]);

  return (
    <BaseBackground>
      <CalendarHeader
        changeMonth={changeMonth}
        monthIndex={monthIndex}
        currentYear={currentYear}
        setMonthIndex={setMonthIndex}
        setYear={setCurrentYear}
      />
      <CalendarToolbar loadEvents={loadEvents} />
      <WeekDaysLine />
      <CalendarGrid
        month={MONTHS[monthIndex]}
        year={currentYear}
        eventsByDate={eventsByDate}
        onDayPress={handleDayPress}
      />
      <EventOverlay
        visible={overlayVisible && !deleteModalVisible}
        date={selectedDate}
        events={selectedDateEvents}
        onClose={handleCloseOverlay}
        onEditEvent={handleEditEvent}
        onDeleteEvent={handleDeleteEvent}
        onCreateEvent={handleCreateEvent}
      />
      <DeleteEventModal
        visible={deleteModalVisible}
        eventTitle={eventToDelete?.title || ""}
        isRecurring={!!eventToDelete?.recurrenceRule}
        onConfirm={handleDeleteConfirm}
        onCancel={handleDeleteCancel}
      />
    </BaseBackground>
  );
};

type EventOverlayProps = {
  visible: boolean;
  date: Date | null;
  events: ExpandedEvent[];
  onClose: () => void;
  onEditEvent: (event?: ExpandedEvent) => void;
  onDeleteEvent: (event: ExpandedEvent) => void;
  onCreateEvent: () => void;
};

const EventOverlay = ({
  visible,
  date,
  events,
  onClose,
  onEditEvent,
  onDeleteEvent,
  onCreateEvent,
}: EventOverlayProps) => {
  const { theme } = useThemeStore();

  if (!date) return null;

  const dateString = date.toLocaleDateString("de-DE", {
    weekday: "long",
    day: "2-digit",
    month: "long",
    year: "numeric",
  });

  const subtitle = `${events.length} ${events.length === 1 ? "Termin" : "Termine"}`;

  const addEventAttachment = (
    <Pressable
      className="flex flex-row justify-center items-center py-3"
      style={{ backgroundColor: theme.confirmButton }}
      onPress={onCreateEvent}
    >
      <Ionicons name="add-outline" size={24} color={theme.buttonText} />
      <Text style={{ color: theme.buttonText }} className="font-semibold ml-1">
        Neuen Termin erstellen
      </Text>
    </Pressable>
  );

  return (
    <ModalBase
      visible={visible}
      onClose={onClose}
      title={dateString}
      subtitle={subtitle}
      attachment={addEventAttachment}
      footer={{ label: "Schliessen", onPress: onClose }}
      scrollable={true}
      maxContentHeight={400}
    >
      {events.map((event, index) => (
        <EventCard
          key={`${event.id}-${index}`}
          event={event}
          onEdit={() => onEditEvent(event)}
          onDelete={() => onDeleteEvent(event)}
        />
      ))}
    </ModalBase>
  );
};

type MonthSelectorProps = {
  modalVisible: boolean;
  onClose: () => void;
  position: { top: number; left: number; width: number };
  currentYear: number;
  currentMonthIndex: number;
  onSelectMonth: (year: number, monthIndex: number) => void;
};

const INITIAL_RANGE = 12; // 12 months before and after current

const MonthSelector = ({
  modalVisible,
  onClose,
  position,
  currentYear,
  currentMonthIndex,
  onSelectMonth,
}: MonthSelectorProps) => {
  const [monthSelectorData, setMonthSelectorData] = useState<MonthItem[]>([]);

  const appendMonths = useCallback(
    (direction: "start" | "end", count: number) => {
      setMonthSelectorData((prevData) => {
        if (prevData.length === 0) return prevData;

        const newMonths: MonthItem[] = [];
        const referenceMonth =
          direction === "start" ? prevData[0] : prevData[prevData.length - 1];

        for (let i = 1; i <= count; i++) {
          const offset = direction === "start" ? -i : i;
          let year = referenceMonth.year;
          let month = referenceMonth.monthIndex + offset;

          while (month < 0) {
            month += 12;
            year--;
          }
          while (month > 11) {
            month -= 12;
            year++;
          }

          const newMonth: MonthItem = {
            id: `${year}-${String(month + 1).padStart(2, "0")}`,
            year,
            monthIndex: month,
            label: `${MONTHS[month]} ${year}`,
          };

          if (direction === "start") {
            newMonths.unshift(newMonth);
          } else {
            newMonths.push(newMonth);
          }
        }

        return direction === "start"
          ? [...newMonths, ...prevData]
          : [...prevData, ...newMonths];
      });
    },
    [],
  );

  // Generate fresh data when modal opens, clear when closes
  useEffect(() => {
    if (modalVisible) {
      setMonthSelectorData(
        generateMonths(currentYear, currentMonthIndex, INITIAL_RANGE),
      );
    } else {
      setMonthSelectorData([]);
    }
  }, [modalVisible, currentYear, currentMonthIndex]);

  const handleSelect = useCallback(
    (item: MonthItem) => {
      onSelectMonth(item.year, item.monthIndex);
      onClose();
    },
    [onSelectMonth, onClose],
  );

  return (
    <ScrollableDropdown
      visible={modalVisible}
      onClose={onClose}
      position={position}
      data={monthSelectorData}
      keyExtractor={(item) => item.id}
      renderItem={(item, theme) => (
        <View
          className="w-full flex justify-center items-center py-2"
          style={{
            backgroundColor:
              item.monthIndex % 2 === 0 ? theme.primeBg : theme.secondaryBg,
          }}
        >
          <Text className="text-xl" style={{ color: theme.primeFg }}>
            {item.label}
          </Text>
        </View>
      )}
      onSelect={handleSelect}
      height={200}
      initialScrollIndex={INITIAL_RANGE}
      onEndReached={() => appendMonths("end", 12)}
      onStartReached={() => appendMonths("start", 12)}
    />
  );
};

type CalendarHeaderProps = {
  changeMonth: (delta: number) => void;
  monthIndex: number;
  currentYear: number;
  setMonthIndex: (index: number) => void;
  setYear: (year: number) => void;
};

const CalendarHeader = (props: CalendarHeaderProps) => {
  const { theme } = useThemeStore();
  const dropdown = useDropdownPosition();

  const prevMonth = () => props.changeMonth(-1);
  const nextMonth = () => props.changeMonth(1);

  return (
    <Header className="flex flex-row items-center justify-between">
      <ChangeMonthButton onPress={prevMonth} icon="chevron-back" />
      <View
        ref={dropdown.ref}
        className="relative flex flex-row items-center justify-around"
      >
        <Text className="text-4xl px-1" style={{ color: theme.textPrimary }}>
          {MONTHS[props.monthIndex]} {props.currentYear}
        </Text>
        <Pressable
          className="flex justify-center items-center w-12 h-12 border rounded-lg"
          style={{
            borderColor: theme.primeFg,
            backgroundColor: theme.chatBot,
            // iOS shadow
            shadowColor: theme.shadowColor,
            shadowOffset: { width: 0, height: 3 },
            shadowOpacity: 0.35,
            shadowRadius: 5,
            // Android shadow
            elevation: 6,
          }}
          onPress={dropdown.open}
        >
          <Ionicons name="chevron-down" size={28} color={theme.primeFg} />
        </Pressable>
      </View>
      <MonthSelector
        modalVisible={dropdown.visible}
        onClose={dropdown.close}
        position={dropdown.position}
        currentYear={props.currentYear}
        currentMonthIndex={props.monthIndex}
        onSelectMonth={(year, month) => {
          props.setYear(year);
          props.setMonthIndex(month);
        }}
      />
      <ChangeMonthButton onPress={nextMonth} icon="chevron-forward" />
    </Header>
  );
};

type ChangeMonthButtonProps = {
  onPress: () => void;
  icon: "chevron-back" | "chevron-forward";
};

const ChangeMonthButton = (props: ChangeMonthButtonProps) => {
  const { theme } = useThemeStore();
  return (
    <Pressable
      onPress={props.onPress}
      className="w-16 h-16 flex items-center justify-center mx-2 rounded-xl border border-solid"
      style={{
        backgroundColor: theme.chatBot,
        borderColor: theme.primeFg,
        // iOS shadow
        shadowColor: theme.shadowColor,
        shadowOffset: { width: 0, height: 4 },
        shadowOpacity: 0.3,
        shadowRadius: 8,
        // Android shadow
        elevation: 6,
      }}
    >
      <Ionicons
        name={props.icon}
        size={48}
        color={theme.primeFg}
        style={{
          marginLeft: props.icon === "chevron-forward" ? 4 : 0,
          marginRight: props.icon === "chevron-back" ? 4 : 0,
        }}
      />
    </Pressable>
  );
};

type CalendarToolbarProps = {
  loadEvents: () => Promise<void>;
};

const CalendarToolbar = ({ loadEvents }: CalendarToolbarProps) => {
  const { theme } = useThemeStore();
  const { config } = useCaldavConfigStore();
  const [isSyncing, setIsSyncing] = useState(false);
  const [syncResult, setSyncResult] = useState<"success" | "error" | null>(
    null,
  );

  const handleSync = async () => {
    if (!config || isSyncing) return;
    setSyncResult(null);
    setIsSyncing(true);
    try {
      await CaldavConfigService.sync();
      await loadEvents();
      setSyncResult("success");
    } catch (error) {
      console.error("CalDAV sync failed:", error);
      setSyncResult("error");
    } finally {
      setIsSyncing(false);
    }
  };

  useEffect(() => {
    if (!syncResult) return;
    const timer = setTimeout(() => setSyncResult(null), 3000);
    return () => clearTimeout(timer);
  }, [syncResult]);

  const handleLogout = async () => {
    await AuthService.logout();
    router.replace("/login");
  };

  const syncIcon = () => {
    if (isSyncing) {
      return <ActivityIndicator size="small" color={theme.primeFg} />;
    }
    if (syncResult === "success") {
      return (
        <Ionicons
          name="checkmark-circle"
          size={20}
          color={theme.confirmButton}
        />
      );
    }
    if (syncResult === "error") {
      return (
        <Ionicons name="close-circle" size={20} color={theme.rejectButton} />
      );
    }
    return (
      <Ionicons
        name="sync-outline"
        size={20}
        color={config ? theme.primeFg : theme.textMuted}
      />
    );
  };

  const buttonStyle = {
    backgroundColor: theme.chatBot,
    borderColor: theme.borderPrimary,
    borderWidth: 1,
    shadowColor: theme.shadowColor,
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 4,
  };

  const className = "flex flex-row items-center gap-2 px-3 py-1 rounded-lg";

  return (
    <View
      className="flex flex-row items-center justify-around py-2"
      style={{
        backgroundColor: theme.primeBg,
        borderBottomWidth: 0,
        borderBottomColor: theme.borderPrimary,
      }}
    >
      <Pressable
        onPress={handleSync}
        disabled={!config || isSyncing}
        className={className}
        style={{
          ...buttonStyle,
          ...(config
            ? {}
            : {
                backgroundColor: theme.disabledButton,
                borderColor: theme.disabledButton,
              }),
        }}
      >
        {syncIcon()}
        <Text
          style={{ color: config ? theme.textPrimary : theme.textMuted }}
          className="font-medium"
        >
          Sync
        </Text>
      </Pressable>

      <Pressable
        onPress={handleLogout}
        className={className}
        style={buttonStyle}
      >
        <Ionicons name="log-out-outline" size={20} color={theme.primeFg} />
        <Text style={{ color: theme.textPrimary }} className="font-medium">
          Logout
        </Text>
      </Pressable>
    </View>
  );
};

const WeekDaysLine = () => {
  const { theme } = useThemeStore();
  return (
    <View className="flex flex-row items-center justify-around px-2 gap-2">
      {/* TODO: px and gap need fine tuning to perfectly align with the grid */}
      {DAYS.map((day, i) => (
        <Text key={i} style={{ color: theme.textPrimary }}>
          {day.substring(0, 2).toUpperCase()}
        </Text>
      ))}
    </View>
  );
};

type CalendarGridProps = {
  month: Month;
  year: number;
  eventsByDate: Map<string, ExpandedEvent[]>;
  onDayPress: (date: Date) => void;
};

const CalendarGrid = (props: CalendarGridProps) => {
  const { theme } = useThemeStore();
  const { baseDate, dateOffset } = useMemo(() => {
    const monthIndex = MONTHS.indexOf(props.month);
    const base = new Date(props.year, monthIndex, 1);
    const offset = base.getDay() === 0 ? 6 : base.getDay() - 1;
    return { baseDate: base, dateOffset: offset };
  }, [props.month, props.year]);

  // TODO: create array beforehand in a useMemo
  const createDateFromOffset = (offset: number): Date => {
    const date = new Date(baseDate);
    date.setDate(date.getDate() + offset);
    return date;
  };

  return (
    <View
      className="h-full flex-1 flex-col flex-wrap gap-2 p-2"
      style={{
        backgroundColor: theme.calenderBg,
      }}
    >
      {Array.from({ length: 6 }).map((_, i) => (
        <View
          key={i}
          className="w-full flex-1 flex-row justify-around items-center gap-2"
        >
          {Array.from({ length: 7 }).map((_, j) => {
            const date = createDateFromOffset(i * 7 + j - dateOffset);
            const dateKey = getDateKey(date);
            const hasEvents = props.eventsByDate.has(dateKey);
            return (
              <SingleDay
                key={j}
                date={date}
                month={props.month}
                hasEvents={hasEvents}
                onPress={() => props.onDayPress(date)}
              />
            );
          })}
        </View>
      ))}
    </View>
  );
};

type SingleDayProps = {
  date: Date;
  month: Month;
  hasEvents: boolean;
  onPress: () => void;
};

const SingleDay = (props: SingleDayProps) => {
  const { theme } = useThemeStore();
  const isSameMonth = MONTHS[props.date.getMonth()] === props.month;

  return (
    <Pressable
      onPress={props.onPress}
      className="h-full flex-1 aspect-auto rounded-xl items-center justify-between py-1"
      style={{
        backgroundColor: theme.primeBg,
      }}
    >
      <Text
        className="text-xl"
        style={{ color: theme.textPrimary, opacity: isSameMonth ? 1 : 0.5 }}
      >
        {props.date.getDate()}
      </Text>

      {/* Event indicator dot */}
      {props.hasEvents && (
        <View
          className="w-2 h-2 rounded-full"
          style={{ backgroundColor: theme.eventIndicator }}
        />
      )}
    </Pressable>
  );
};

export default Calendar;
