import { useQuery } from "@tanstack/react-query";
import { DateTime, Duration } from "luxon";

const BASE_URL = import.meta.env.VITE_API_URL;

const makePostRequest = async (
  url: string,
  body: object,
  error = "Une erreur est survenue",
  method = "POST"
) => {
  const response = await fetch(BASE_URL + url, {
    method,
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    body: JSON.stringify(body),
    credentials: "include", // Include cookies for authentication
  });

  const data = await response.json();
  if (!response.ok) throw new Error(data.error || error);
  return data?.data || data;
};

// TODO: Use swr or react-query for caching and revalidation
// TODO: Cache all to localStorage or IndexedDB for offline support
const makeRequest = async (url: string, error = "Une erreur est survenue") => {
  const response = await fetch(BASE_URL + url, { credentials: "include" });

  const data = await response.json();
  if (!response.ok) throw new Error(data.error || error);
  return data?.data || data;
};

/**
 * === AUTH API ===
 */
export const requestLogin = async (email: string, token: string) => {
  return makePostRequest(
    "/auth/request",
    { email, token },
    "Échec de la demande de connexion"
  );
};

export const verifyOtp = async ({
  otpCode,
  email,
}: {
  otpCode: string;
  email: string;
}) => {
  return makePostRequest(
    "/auth/verify",
    { email, code: otpCode },
    "Code de vérification invalide"
  );
};

export const getClasses = async () => {
  try {
    const res = await fetch("/classes.json");
    return res.json();
  } catch (error) {
    console.error("Error fetching classes:", error);
    return [];
  }
};

export const registerUser = async (
  name: string,
  className: string,
  token: string
) => {
  return makePostRequest(
    "/auth/register",
    { name, className, token },
    "Échec de l'inscription"
  );
};

export const getNames = async (className: string) => {
  return makeRequest(
    `/auth/autocomplete?className=${encodeURIComponent(className)}`,
    "Échec de la récupération des noms"
  );
};

/**
 * === USER API ===
 */
const fetchUser = async () => {
  return makeRequest(
    "/users/@me",
    "Échec de la récupération des informations utilisateur"
  );
};

const defaultUser = {
  id: 0,
  firstName: "",
  lastName: "",
  fullName: "",
  email: "",
  className: "",
  preferences: [] as UserPreferences,
};

export type User = typeof defaultUser;

export const AUTH_ERROR = "Unauthorized access";

export const useUser = () => {
  const { data, ...props } = useQuery({
    queryKey: ["user"],
    queryFn: fetchUser,
    staleTime: Duration.fromObject({
      minutes: 5, // 5 minutes
    }).toMillis(),
    gcTime: Duration.fromObject({
      days: 3, // 3 days
    }).toMillis(),
  });
  return {
    user: (data ? Object.assign(defaultUser, data) : defaultUser) as User,
    ...props,
  };
};

export const logout = async () => {
  return makePostRequest("/auth/logout", {}, "Échec de la déconnexion", "POST");
};

export const updateUserPreferences = async (
  preferences: { name: string; emoji: string; color: string }[]
) => {
  return makePostRequest(
    "/users/@me",
    { preferences },
    "Échec de la mise à jour des préférences utilisateur",
    "POST"
  );
};

interface UserPreference {
  name: string;
  emoji: string;
  color: string;
}
export type UserPreferences = UserPreference[];

/**
 * === COLLES API ===
 */
export const getColles = async (startDate: DateTime) => {
  return makeRequest(
    `/colles?startDate=${startDate.toISODate()}`,
    "Échec de la récupération des colles"
  );
};

export interface Colle {
  id: number;
  date: string; // ISO date string
  subject: {
    id: number;
    name: string;
  };
  examiner: {
    id: number;
    name: string;
  };
  room: {
    id: number;
    name: string;
  };
  student: User;
  grade?: number; // Nullable grade
  bjsecret?: string; // Optional field
  bjid?: string; // Optional field
  content?: string; // Optional field
  comment?: string; // Optional field
  attachments?: Attachment[]; // Optional field, array of attachment URLs
}

interface CollePayload {
  classColles: Colle[];
  studentColles: Colle[];
  favoriteColles: Colle[];
  healthyUntil: Date;
  lastSync: Date;
}

export const useColles = (startDate: DateTime) => {
  // Check if the start date is the current week
  // This is used to determine if the data is "actual" or not
  const isActual = DateTime.now()
    .startOf("week")
    .equals(startDate.startOf("week"));

  const options = isActual
    ? {
        refetchOnWindowFocus: true,
        refetchOnReconnect: true,
        staleTime: 0,
        gcTime: 0,
      }
    : {
        staleTime: Duration.fromObject({
          hours: 1, // 1 hour
        }).toMillis(),
        gcTime: Duration.fromObject({
          days: 3, // 3 days
        }).toMillis(),
      };

  const { data, ...props } = useQuery({
    queryKey: ["colles", startDate.toISODate()],
    queryFn: () => getColles(startDate),
    ...options,
  });
  const mergedData = Object.assign(
    {
      classColles: [],
      studentColles: [],
      favoriteColles: [],
      healthyUntil: new Date(0),
      lastSync: new Date(0),
    },
    data || {}
  ) as CollePayload;

  return {
    ...mergedData,
    ...props,
  };
};

const fetchColle = async (id: number) => {
  return makeRequest(`/colles/${id}`, "Échec de la récupération de la colle");
};

const defaultColle = {
  id: 0,
  date: "",
  subject: {
    id: 0,
    name: "",
  },
  examiner: {
    id: 0,
    name: "",
  },
  room: {
    id: 0,
    name: "",
  },
  student: defaultUser,
  attachments: [],
};

export const useColle = (id: number) => {
  const { data, ...props } = useQuery({
    queryKey: ["colle", id],
    queryFn: () => fetchColle(id),
    staleTime: Duration.fromObject({
      seconds: 30, // 30 seconds
    }).toMillis(),
    gcTime: Duration.fromObject({
      days: 3, // 3 days
    }).toMillis(),
  });
  return {
    colle: (data || defaultColle) as Colle,
    ...props,
  };
};

export const refreshColle = async (id: number) => {
  return makePostRequest(
    `/colles/${id}/refresh`,
    {},
    "Échec de la demande de rafraîchissement de la colle",
    "POST"
  );
};

export interface Attachment {
  path: string;
  name: string;
}

/**
 * === SUBJECTS API ===
 */
export const getSubjects = async () => {
  return makeRequest("/subjects", "Échec de la récupération des matières");
};
export const useSubjects = () => {
  const { data, ...props } = useQuery({
    queryKey: ["subjects"],
    queryFn: getSubjects,
    staleTime: Duration.fromObject({
      hours: 1, // 1 day
    }).toMillis(),
    gcTime: Duration.fromObject({
      days: 3, // 3 days
    }).toMillis(),
  });
  return {
    subjects: (data as string[]) || [],
    ...props,
  };
};

/**
 * === NOTIFICATIONS API ===
 */
export const subscribe = async (data: any) => {
  return makePostRequest(
    "/notifications/subscribe",
    data,
    "Échec de l'abonnement aux notifications"
  );
}

export const unsubscribe = async (data: any) => {
  return makePostRequest(
    "/notifications/unsubscribe",
    data,
    "Échec de la désinscription des notifications"
  );
};
