import { QueryClient, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "@xtablo/shared";
import { useAuthedApi } from "./auth";

// Types for tablo data API responses
export interface TabloFile {
  fileName: string;
  content: string;
  contentType?: string;
  lastModified?: Date;
}

export interface TabloFileList {
  fileNames: string[];
}

export interface FileUploadRequest {
  content: string;
  contentType?: string;
}

export interface FileOperationResponse {
  message: string;
  fileName: string;
  tabloId: string;
}

const toastTimeout = 5000;

export const toastOptions = {
  timeout: toastTimeout,
};

export interface AllTablosFileNames {
  tablos: { tabloId: string; fileNames: string[] }[];
}

// Hook to get file names for all tablos in a single request
export function useAllTablosFileNames() {
  const api = useAuthedApi();
  return useQuery<AllTablosFileNames>({
    queryKey: ["all-tablo-files"],
    queryFn: async () => {
      const response = await api.get("/api/v1/tablo-data/all-filenames");
      if (response.status !== 200) {
        throw new Error("Failed to fetch all tablo files");
      }
      return response.data;
    },
  });
}

// Hook to get all file names for a tablo
export function useTabloFileNames(tabloId: string) {
  const api = useAuthedApi();
  const { data, isLoading, error } = useQuery<TabloFileList>({
    queryKey: ["tablo-files", tabloId],
    queryFn: async () => {
      const response = await api.get(`/api/v1/tablo-data/${tabloId}/filenames`);
      if (response.status !== 200) {
        throw new Error("Failed to fetch tablo files");
      }
      return response.data;
    },
    enabled: !!tabloId,
  });
  return { data, isLoading, error };
}

// Hook to get a specific file from a tablo
export function useTabloFile(tabloId: string, fileName: string) {
  const api = useAuthedApi();
  return useQuery<TabloFile>({
    queryKey: ["tablo-file", tabloId, fileName],
    queryFn: async () => {
      const response = await api.get(`/api/v1/tablo-data/${tabloId}/${fileName}`);
      if (response.status !== 200) {
        throw new Error("Failed to fetch file");
      }
      return response.data;
    },
    enabled: !!tabloId && !!fileName,
  });
}

// Hook to download a file from a tablo
export function useDownloadTabloFile() {
  const api = useAuthedApi();

  return useMutation<void, Error, { tabloId: string; fileName: string }>({
    mutationFn: async ({ tabloId, fileName }) => {
      try {
        const response = await api.get(`/api/v1/tablo-data/${tabloId}/${fileName}`);

        if (response.status !== 200) {
          throw new Error("Failed to download file");
        }

        const fileData = response.data;
        let blob: Blob;

        // Handle different content types
        if (fileData.content.startsWith("data:")) {
          // Handle data URLs (base64 encoded files)
          const response = await fetch(fileData.content);
          blob = await response.blob();
        } else {
          // Handle text content
          blob = new Blob([fileData.content], {
            type: fileData.contentType || "application/octet-stream",
          });
        }

        // Create download link
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
      } catch (error) {
        console.error("Download error:", error);
        throw error;
      }
    },
    onSuccess: (_, variables) => {
      toast.add(
        {
          title: "Téléchargement réussi",
          description: `Le fichier ${variables.fileName} a été téléchargé`,
          type: "success",
        },
        toastOptions
      );
    },
    onError: (error, variables) => {
      toast.add(
        {
          title: "Erreur de téléchargement",
          description: `Impossible de télécharger ${variables.fileName}: ${error.message}`,
          type: "error",
        },
        toastOptions
      );
    },
  });
}

// Hook to create a new file in a tablo
export function useCreateTabloFile() {
  const api = useAuthedApi();
  const queryClient = useQueryClient();

  return useMutation<
    FileOperationResponse,
    Error,
    { tabloId: string; fileName: string; data: FileUploadRequest }
  >({
    mutationFn: async ({ tabloId, fileName, data }) => {
      const response = await api.post(`/api/v1/tablo-data/${tabloId}/file/${fileName}`, data);
      if (response.status !== 200) {
        throw new Error("Failed to create file");
      }
      return response.data;
    },
    onSuccess: (_, variables) => {
      toast.add(
        {
          title: "Fichier créé",
          description: `Le fichier ${variables.fileName} a été créé avec succès`,
          type: "success",
        },
        toastOptions
      );
    },
    onError: (error, variables) => {
      toast.add(
        {
          title: "Erreur",
          description: `Échec de la création du fichier ${variables.fileName}: ${error.message}`,
          type: "error",
        },
        toastOptions
      );
    },
    onSettled: (_, _err, variables) => {
      if (variables) {
        invalidateTabloData(queryClient, variables.tabloId);
      }
    },
  });
}

// Hook to update an existing file in a tablo
export function useUpdateTabloFile() {
  const api = useAuthedApi();
  const queryClient = useQueryClient();

  return useMutation<
    FileOperationResponse,
    Error,
    { tabloId: string; fileName: string; data: FileUploadRequest }
  >({
    mutationFn: async ({ tabloId, fileName, data }) => {
      const response = await api.put(`/api/v1/tablo-data/${tabloId}/file/${fileName}`, data);
      if (response.status !== 200) {
        throw new Error("Failed to update file");
      }
      return response.data;
    },
    onSuccess: (_, variables) => {
      toast.add(
        {
          title: "Fichier mis à jour",
          description: `Le fichier ${variables.fileName} a été mis à jour avec succès`,
          type: "success",
        },
        toastOptions
      );
    },
    onError: (error, variables) => {
      toast.add(
        {
          title: "Erreur",
          description: `Échec de la mise à jour du fichier ${variables.fileName}: ${error.message}`,
          type: "error",
        },
        toastOptions
      );
    },
    onSettled: (_, _err, variables) => {
      if (variables) {
        invalidateTabloData(queryClient, variables.tabloId);
      }
    },
  });
}

// Hook to delete a file from a tablo
export function useDeleteTabloFile() {
  const api = useAuthedApi();
  const queryClient = useQueryClient();

  return useMutation<FileOperationResponse, Error, { tabloId: string; fileName: string }>({
    mutationFn: async ({ tabloId, fileName }) => {
      const response = await api.delete(`/api/v1/tablo-data/${tabloId}/file/${fileName}`);
      if (response.status !== 200) {
        const errorMessage =
          typeof response.data?.error === "string" ? response.data.error : "Failed to delete file";
        throw new Error(errorMessage);
      }
      return response.data;
    },
    onSuccess: (_, variables) => {
      toast.add(
        {
          title: "Fichier supprimé",
          description: `Le fichier ${variables.fileName} a été supprimé avec succès`,
          type: "success",
        },
        toastOptions
      );
    },
    onError: (error, variables) => {
      toast.add(
        {
          title: "Erreur",
          description: `Échec de la suppression du fichier ${variables.fileName}: ${error.message}`,
          type: "error",
        },
        toastOptions
      );
    },
    onSettled: (_, _err, variables) => {
      if (variables) {
        invalidateTabloData(queryClient, variables.tabloId);
      }
    },
  });
}

// Utility function to invalidate all tablo data queries for a specific tablo
export const invalidateTabloData = (queryClient: QueryClient, tabloId: string) => {
  queryClient.invalidateQueries({
    queryKey: ["tablo-files", tabloId],
  });
  queryClient.invalidateQueries({
    queryKey: ["tablo-file", tabloId],
  });
};

// Hook to upload or update a file (combines create/update logic)
export function useUploadTabloFile() {
  const createFile = useCreateTabloFile();
  const updateFile = useUpdateTabloFile();
  const queryClient = useQueryClient();

  return useMutation<
    FileOperationResponse,
    Error,
    {
      tabloId: string;
      fileName: string;
      data: FileUploadRequest;
      overwrite?: boolean;
    }
  >({
    mutationFn: async ({ tabloId, fileName, data, overwrite = false }) => {
      // Check if file exists first (unless overwrite is explicitly true)
      if (!overwrite) {
        try {
          const existingFile = queryClient.getQueryData(["tablo-file", tabloId, fileName]);
          if (existingFile) {
            // File exists, use update
            return await updateFile.mutateAsync({ tabloId, fileName, data });
          }
        } catch {
          // File doesn't exist, continue with create
        }
      }

      // Try create first, fall back to update if file exists
      try {
        return await createFile.mutateAsync({ tabloId, fileName, data });
      } catch (error) {
        // If create fails because file exists, try update
        if (error instanceof Error && error.message.includes("exists")) {
          return await updateFile.mutateAsync({ tabloId, fileName, data });
        }
        throw error;
      }
    },
    onSuccess: (_, variables) => {
      toast.add(
        {
          title: "Fichier téléchargé",
          description: `Le fichier ${variables.fileName} a été téléchargé avec succès`,
          type: "success",
        },
        toastOptions
      );
    },
    onError: (error, variables) => {
      toast.add(
        {
          title: "Erreur",
          description: `Échec du téléchargement du fichier ${variables.fileName}: ${error.message}`,
          type: "error",
        },
        toastOptions
      );
    },
  });
}
