import * as Application from "expo-application";
import { Directory, Paths } from "expo-file-system";
import { atom, useAtom } from "jotai";
import { createContext, useCallback, useContext, useMemo, useRef } from "react";
import { Platform } from "react-native";
import { useHaptic } from "@/hooks/useHaptic";
import {
  getAllDownloadedItems,
  getDownloadedItemById,
  getDownloadsDatabase,
  updateDownloadedItem,
} from "./Downloads/database";
import { getDownloadedItemSize } from "./Downloads/fileOperations";
import { useDownloadEventHandlers } from "./Downloads/hooks/useDownloadEventHandlers";
import { useDownloadOperations } from "./Downloads/hooks/useDownloadOperations";
import type { JobStatus } from "./Downloads/types";
import { apiAtom } from "./JellyfinProvider";

export const processesAtom = atom<JobStatus[]>([]);
export const downloadsRefreshAtom = atom<number>(0);

const DownloadContext = createContext<ReturnType<
  typeof useDownloadProvider
> | null>(null);

function useDownloadProvider() {
  const [api] = useAtom(apiAtom);
  const [processes, setProcesses] = useAtom<JobStatus[]>(processesAtom);
  const [refreshKey, setRefreshKey] = useAtom(downloadsRefreshAtom);
  const successHapticFeedback = useHaptic("success");

  // Track task ID to process ID mapping
  const taskMapRef = useRef<Map<number | string, string>>(new Map());

  // Reactive downloaded items that updates when refreshKey changes
  const downloadedItems = useMemo(() => {
    return getAllDownloadedItems();
  }, [refreshKey]);

  // Trigger refresh of download lists
  const triggerRefresh = useCallback(() => {
    setRefreshKey((prev) => prev + 1);
  }, [setRefreshKey]);

  const authHeader = useMemo(() => {
    return api?.accessToken;
  }, [api]);

  const APP_CACHE_DOWNLOAD_DIRECTORY = new Directory(
    Paths.cache,
    `${Application.applicationId}/Downloads/`,
  );

  const updateProcess = useCallback(
    (
      processId: string,
      updater:
        | Partial<JobStatus>
        | ((current: JobStatus) => Partial<JobStatus>),
    ) => {
      setProcesses((prev) => {
        const processIndex = prev.findIndex((p) => p.id === processId);
        if (processIndex === -1) return prev;

        const currentProcess = prev[processIndex];
        if (!currentProcess) return prev;

        const newStatus =
          typeof updater === "function" ? updater(currentProcess) : updater;

        // Create new array with updated process
        const newProcesses = [...prev];
        newProcesses[processIndex] = {
          ...currentProcess,
          ...newStatus,
        };

        return newProcesses;
      });
    },
    [setProcesses],
  );

  const removeProcess = useCallback(
    (id: string) => {
      // Use setTimeout to defer removal and avoid race conditions during rendering
      setTimeout(() => {
        setProcesses((prev) => prev.filter((process) => process.id !== id));

        // Find and remove from task map
        taskMapRef.current.forEach((processId, taskId) => {
          if (processId === id) {
            taskMapRef.current.delete(taskId);
          }
        });
      }, 0);
    },
    [setProcesses],
  );

  // Set up download event handlers
  useDownloadEventHandlers({
    taskMapRef,
    processes,
    updateProcess,
    removeProcess,
    onSuccess: successHapticFeedback,
    onDataChange: triggerRefresh,
    api: api || undefined,
  });

  // Get download operation functions
  const {
    startBackgroundDownload,
    cancelDownload,
    deleteFile,
    deleteItems,
    deleteAllFiles,
    deleteFileByType,
    appSizeUsage,
  } = useDownloadOperations({
    taskMapRef,
    processes,
    setProcesses,
    removeProcess,
    api,
    authHeader,
    onDataChange: triggerRefresh,
  });

  return {
    processes,
    startBackgroundDownload,
    downloadedItems, // Reactive value that auto-updates
    getDownloadedItems: getAllDownloadedItems, // Keep for backward compatibility
    getDownloadsDatabase,
    deleteAllFiles,
    deleteFile,
    deleteItems,
    deleteFileByType,
    removeProcess,
    cancelDownload,
    getDownloadedItemSize,
    getDownloadedItemById,
    updateDownloadedItem,
    triggerRefresh,
    APP_CACHE_DOWNLOAD_DIRECTORY: APP_CACHE_DOWNLOAD_DIRECTORY.uri,
    appSizeUsage,
    // Deprecated/not implemented in simple version
    startDownload: async () => {},
    cleanCacheDirectory: async () => {},
    dumpDownloadDiagnostics: async () => "",
  };
}

export function useDownload() {
  const context = useContext(DownloadContext);

  if (Platform.isTV) {
    return {
      processes: [],
      startBackgroundDownload: async () => {},
      downloadedItems: [],
      getDownloadedItems: () => [],
      getDownloadsDatabase: () => ({ movies: {}, series: {}, other: {} }),
      deleteAllFiles: async () => {},
      deleteFile: async () => {},
      deleteItems: async () => {},
      deleteFileByType: async () => {},
      removeProcess: () => {},
      cancelDownload: async () => {},
      triggerRefresh: () => {},
      startDownload: async () => {},
      getDownloadedItemSize: () => 0,
      getDownloadedItemById: () => undefined,
      updateDownloadedItem: () => {},
      APP_CACHE_DOWNLOAD_DIRECTORY: "",
      cleanCacheDirectory: async () => {},
      appSizeUsage: async () => ({ total: 0, remaining: 0, appSize: 0 }),
      dumpDownloadDiagnostics: async () => "",
    };
  }

  if (context === null) {
    throw new Error("useDownload must be used within a DownloadProvider");
  }

  return context;
}

export function DownloadProvider({ children }: { children: React.ReactNode }) {
  const downloadUtils = useDownloadProvider();

  return (
    <DownloadContext.Provider value={downloadUtils}>
      {children}
    </DownloadContext.Provider>
  );
}
