import { GATEWAY } from './../../config-global';
import { useMemo } from 'react';
import useSWR, { mutate } from 'swr';
import axios from 'axios';

import { fetcher, endpoints } from '@/utils/axios';
import { parse, addDays, addMonths, startOfDay, endOfDay, isWithinInterval } from 'date-fns';

import { DAYS_NAME_OPTIONS, DialogData, getDayOfWeek, ICalendarEvent, ICalendarEventWithRecurrent } from '@/shared/types/calendar';
import { getUserByEmail } from './user';
import { IUser } from '../types/user';
import axiosInstance from './server';
import moment from 'moment-timezone';

const URL = endpoints.calendar.getAll;

const options = {
  revalidateIfStale: true,
  revalidateOnFocus: false,
  revalidateOnReconnect: true,
};

export function useGetEvents() {
  const email = localStorage.getItem("email") as string;
  const { data: userData } = useSWR<{ id: string }>(email ? ['user', email] : null, async () => await getUserByEmail(email));

  const { data, error, isValidating } = useSWR<IBackendEventWithRecurrent[]>(() => userData ? `${URL}/${userData.id}` : null, fetcher, options);

  const memoizedValue = useMemo(() => {
    if (error) {
      console.error('Error fetching calendar events:', error);
      return {
        events: [],
        eventsLoading: false,
        eventsError: error,
        eventsValidating: false,
        eventsEmpty: true,
      };
    }

    if (!data) {
      console.log('Calendar data is still loading');
      return {
        events: [],
        eventsLoading: true,
        eventsError: null,
        eventsValidating: isValidating,
        eventsEmpty: true,
      };
    }

    const events = generateRecurringEvents(data);

    return {
      events,
      eventsLoading: false,
      eventsError: null,
      eventsValidating: isValidating,
      eventsEmpty: events.length === 0,
    };
  }, [data, error, isValidating]);

  return memoizedValue;
}



let userData: IUser;
(async () => {
  userData = await getUserByEmail(localStorage.getItem("email") as string);
})();

const refreshEvents = async (userId: string) => {
  await mutate(`${URL}/${userId}`, undefined, true);
};

export async function createEvent(eventData: ICalendarEvent) {
  const backendEventData = mapFrontendToBackend(eventData, parseInt(userData.id));
  const response = await axiosInstance.post(endpoints.calendar.add, backendEventData);

  await refreshEvents(userData.id);
  return response.data;
}

export async function updateEvent(eventData: Partial<ICalendarEventWithRecurrent>) {
  if (!eventData.id) {
    return;
  }

  const backendEventData = mapFrontendToBackendUpdate(eventData as ICalendarEventWithRecurrent, parseInt(userData.id));
  const response = await axiosInstance.put(endpoints.calendar.update + "/" + eventData.id, backendEventData);

  await refreshEvents(userData.id);
  return response.data;
}

export async function deleteEvent(eventId: string) {
  const response = await axiosInstance.delete(endpoints.calendar.delete + "/" + eventId, { data: { id: eventId } });

  await refreshEvents(userData.id);
  return response.data;
}


export async function createRecurrent(eventData: DialogData) {
  const selectedDateTime = moment(eventData.selectedDate);
  const recurrentData = {
    happens: eventData.happens,
    selectedDays: eventData.selectedDays,
    monthlyOptions: eventData.monthlyOptions,
    endState: eventData.ends,
    occurences: eventData.occurrences,
    selectedDate: selectedDateTime.format("YYYY-MM-DDTHH:mm:ss"),
  }
  const response = await axiosInstance.post(endpoints.recurrent.add, recurrentData);

  return response.data;
}

export async function updateRecurrent(id: number, eventData: DialogData) {
  const recurrentData = {
    happens: eventData.happens,
    selectedDays: eventData.selectedDays,
    monthlyOptions: eventData.monthlyOptions,
    endState: eventData.ends,
    occurences: eventData.occurrences,
    selectedDate: eventData.selectedDate ? moment(eventData.selectedDate).format("YYYY-MM-DDTHH:mm:ss") : null,
  };

  const response = await axiosInstance.put(`${endpoints.recurrent.update}/${id}`, recurrentData);


  return response.data;
}
export async function searchForUserByQuery(query: string) {
  const token = localStorage.getItem('token');
  try {
    const response = await axios.get(GATEWAY + endpoints.user.searchByEmailOrName + `?query=${query}`, {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    });

    return response.data.length === 0 ? [] : response.data;
  } catch (error) {
    console.error('Error searching for user:', error);
    throw error;
  }
}


interface IBackendEvent {
  id: number;
  title: string;
  color: string;
  startDate: string;
  endDate: string;
  description: string;
  guestIds: string[];
  timeZone: string;
  notification: string;
  eventRecurrent: number | null;
  userId: number;
}

interface IBackendEventWithRecurrent {
  id: number;
  title: string;
  color: string;
  startDate: string;
  endDate: string;
  description: string;
  guestIds: string[];
  timeZone: string;
  notification: string;
  eventRecurrent: DialogData;
  userId: number;
}

export function mapBackendToFrontend(backendEvent: IBackendEventWithRecurrent): ICalendarEventWithRecurrent {
  return {
    id: backendEvent.id.toString(),
    title: backendEvent.title,
    color: backendEvent.color,
    description: backendEvent.description,
    start: new Date(backendEvent.startDate),
    end: new Date(backendEvent.endDate),
    guests: backendEvent.guestIds,
    timeZone: backendEvent.timeZone,
    notification: backendEvent.notification,
    recurring: backendEvent.eventRecurrent,
    allDay: isAllDayEvent(new Date(backendEvent.startDate), new Date(backendEvent.endDate)),
  };
}

export function mapFrontendToBackend(frontendEvent: ICalendarEvent, userId: number): IBackendEvent {
  const startDateTime = moment(frontendEvent.start);
  const endDateTime = moment(frontendEvent.end);
  return {
    id: frontendEvent.id ? parseInt(frontendEvent.id) : 0,
    title: frontendEvent.title,
    color: frontendEvent.color,
    startDate: startDateTime.tz(frontendEvent.timeZone).format("YYYY-MM-DDTHH:mm:ss"),
    endDate: endDateTime.tz(frontendEvent.timeZone).format("YYYY-MM-DDTHH:mm:ss"),
    description: frontendEvent.description,
    guestIds: frontendEvent.guests,
    timeZone: frontendEvent.timeZone,
    notification: frontendEvent.notification,
    eventRecurrent: frontendEvent.recurring ? frontendEvent.recurring : null,
    userId: userId,
  };
}

export function mapFrontendToBackendUpdate(frontendEvent: ICalendarEventWithRecurrent, userId: number): IBackendEvent {
  const startDateTime = moment(frontendEvent.start);
  const endDateTime = moment(frontendEvent.end);
  return {
    id: frontendEvent.id ? parseInt(frontendEvent.id) : 0,
    title: frontendEvent.title,
    color: frontendEvent.color,
    startDate: startDateTime.tz(frontendEvent.timeZone).format("YYYY-MM-DDTHH:mm:ss"),
    endDate: endDateTime.tz(frontendEvent.timeZone).format("YYYY-MM-DDTHH:mm:ss"),
    description: frontendEvent.description,
    guestIds: frontendEvent.guests,
    timeZone: frontendEvent.timeZone,
    notification: frontendEvent.notification,
    eventRecurrent: frontendEvent.recurring.id ? frontendEvent.recurring.id : null,
    userId: userId,
  };
}

function isAllDayEvent(start: Date, end: Date): boolean {
  const isStartMidnight = start.getHours() === 0 && start.getMinutes() === 0;
  const isEndMidnight = end.getHours() === 0 && end.getMinutes() === 0;
  const durationHours = (end.getTime() - start.getTime()) / (1000 * 60 * 60);
  return isStartMidnight && isEndMidnight && durationHours % 24 === 0;
}

function generateRecurringEvents(events: IBackendEventWithRecurrent[]): ICalendarEventWithRecurrent[] {
  const generatedEvents: IBackendEventWithRecurrent[] = [];

  events.forEach(event => {
    if (!event.eventRecurrent) {
      generatedEvents.push(event);
      return;
    }
    if (event.eventRecurrent.happens === 'Daily') {
      generatedEvents.push(...dailyRecurrent(event));
    }
    if (event.eventRecurrent.happens === 'Weekly') {
      generatedEvents.push(...weeklyRecurrent(event));
    }
    if (event.eventRecurrent.happens === 'Monthly') {
      generatedEvents.push(...monthlyRecurrent(event));
    }
  });

  const eventsAll: ICalendarEventWithRecurrent[] = generatedEvents.map((event) => ({
    ...mapBackendToFrontend(event),
    textColor: event.color,
  }));
  return eventsAll;
}

function dailyRecurrent(event: IBackendEventWithRecurrent): IBackendEventWithRecurrent[] {
  const generatedEvents: IBackendEventWithRecurrent[] = [];
  const recurrence = event.eventRecurrent;

  if (recurrence?.selectedDate) {
    const startDate = new Date(event.startDate);
    const endDate = new Date(recurrence.selectedDate);

    while (startDate <= endDate) {
      generatedEvents.push({
        ...event,
        id: event.id,
        startDate: startDate.toISOString(),
        endDate: new Date(startDate.getTime() + (new Date(event.endDate).getTime() - new Date(event.startDate).getTime())).toISOString(),
      });

      startDate.setDate(startDate.getDate() + 1);
    }
  } else {
    generatedEvents.push(event);
  }

  return generatedEvents;
}

function weeklyRecurrent(event: IBackendEventWithRecurrent): IBackendEventWithRecurrent[] {
  const generatedEvents: IBackendEventWithRecurrent[] = [];
  if (event.eventRecurrent.selectedDate !== null || event.eventRecurrent.selectedDays.length !== 0) {
    event.eventRecurrent.selectedDays.forEach((day) => {
      const dayIndex = DAYS_NAME_OPTIONS.findIndex((option) => option.value === day);
      let currentDate = new Date(event.startDate);
      while (currentDate <= new Date(event.eventRecurrent.selectedDate?.toString() as string)) {
        if (currentDate.getDay() === dayIndex) {
          generatedEvents.push({
            ...event,
            id: event.id,
            startDate: currentDate.toISOString(),
            endDate: new Date(currentDate.getTime() + (new Date(event.endDate).getTime() - new Date(event.startDate).getTime())).toISOString(),
          });
        }
        currentDate = addDays(currentDate, 1);
      }
    });
  } else {
    generatedEvents.push(event);
  }
  return generatedEvents;
}

function monthlyRecurrent(event: IBackendEventWithRecurrent): IBackendEventWithRecurrent[] {
  const generatedEvents: IBackendEventWithRecurrent[] = [];
  if (event.eventRecurrent.selectedDate !== null || event.eventRecurrent.monthlyOptions !== null) {
    let monthlyGet = parseInt(event.eventRecurrent.monthlyOptions);
    if (Number.isNaN(monthlyGet) || monthlyGet === null) {
      const dates = getFirstSpecificWeekdayOfMonth(event.eventRecurrent.monthlyOptions, new Date(event.eventRecurrent.selectedDate?.toString() as string));
      dates.forEach((date) => {
        generatedEvents.push({
          ...event,
          id: event.id,
          startDate: date,
          endDate: new Date(new Date(date).getTime() + (new Date(event.endDate).getTime() - new Date(event.startDate).getTime())).toISOString(),
        });
      });
    } else {
      const dates = getSpecificDayOfMonth(monthlyGet, new Date(event.eventRecurrent.selectedDate?.toString() as string));
      dates.forEach((date) => {
        generatedEvents.push({
          ...event,
          id: event.id,
          startDate: date,
          endDate: new Date(new Date(date).getTime() + (new Date(event.endDate).getTime() - new Date(event.startDate).getTime())).toISOString(),
        });
      });
    }
  } else {
    generatedEvents.push(event);
  }
  return generatedEvents;
}

function getFirstSpecificWeekdayOfMonth(targetWeekday: string, endDate: Date): string[] {
  const resultDates: string[] = [];

  const weekdays: { [key: string]: number } = {
    'sunday': 0,
    'monday': 1,
    'tuesday': 2,
    'wednesday': 3,
    'thursday': 4,
    'friday': 5,
    'saturday': 6,
    'dimanche': 0,
    'lundi': 1,
    'mardi': 2,
    'mercredi': 3,
    'jeudi': 4,
    'vendredi': 5,
    'samedi': 6
  };

  const targetDay = targetWeekday ? weekdays[targetWeekday.toLowerCase()] : 0;

  let currentMonth = new Date();
  let year = currentMonth.getFullYear();
  let month = currentMonth.getMonth();

  while (currentMonth <= endDate) {
    let firstDayOfMonth = new Date(year, month, 1);
    let dayOfWeek = firstDayOfMonth.getDay();

    let dayDifference = (targetDay - dayOfWeek + 7) % 7;

    firstDayOfMonth.setDate(firstDayOfMonth.getDate() + dayDifference);

    if (firstDayOfMonth > endDate) {
      break;
    }

    resultDates.push(firstDayOfMonth.toString());

    month++;

    if (month > 11) {
      month = 0;
      year++;
    }
    currentMonth = new Date(year, month, 1);
  }

  return resultDates;
}

function getSpecificDayOfMonth(day: number, endDate: Date): string[] {
  const resultDates: string[] = [];

  let currentMonth = new Date();
  let year = currentMonth.getFullYear();
  let month = currentMonth.getMonth();

  while (currentMonth <= endDate) {
    let specificDayOfMonth = new Date(year, month, day);

    if (specificDayOfMonth.getMonth() !== month) {
      month++;
      if (month > 11) {
        month = 0;
        year++;
      }
      continue;
    }

    resultDates.push(specificDayOfMonth.toString());

    month++;

    if (month > 11) {
      month = 0;
      year++;
    }
    currentMonth = new Date(year, month, 1);
  }

  return resultDates;
}

export async function exportCalendarByUserId() {
  try {
    const response = await axios.get(GATEWAY + '/api/calendar/export', {
      params: { id: userData.id },
      responseType: 'blob',
    });

    if (response.status === 200) {
      const blob = new Blob([response.data], { type: 'text/calendar' });

      const url = window.URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.style.display = 'none';
      a.href = url;
      a.download = 'stunar_calendar.ics';
      document.body.appendChild(a);
      a.click();
      window.URL.revokeObjectURL(url);
    } else {
      alert('Failed to export the calendar');
    }
  } catch (error) {
    console.error('Error exporting calendar:', error);
    alert('An error occurred while exporting the calendar');
  }
}

export async function useImportCalendar(file: File) {
  try {

    if (!userData?.id) {
      alert("User ID not found");
      return;
    }

    const formData = new FormData();
    formData.append("file", file);
    formData.append("idUser", userData?.id);

    const response = await axios.post(`${GATEWAY}/api/calendar/import`, formData, {
      headers: {
        "Content-Type": "multipart/form-data",
      },
    });

    alert(response.data);
  } catch (error) {
    console.error("Error importing calendar:", error);
    alert("An error occurred while importing the calendar");
  }
}
