import { useQuery } from '@tanstack/react-query';
import { useEffect } from 'react';
import { useLocalStorage } from 'usehooks-ts';

import queryClient from '@/components/studio/queryClient';
import { ApiClient, useApiClient } from '@/lib/apiClient';
import sanitizeNumber from '@/utils/sanitizeNumber';
import { sleep } from '@/utils/utils';

import fetchDownbeats from '../fetchers/fetchDownbeats';
import getTimingFromDownbeats from '../getTimingFromDownbeats';

export const useCleanupDownbeatsFromLocalStorage = () => {
  const [hasCleanedUp, setHasCleanedUp] = useLocalStorage(
    'jul-30-2025-has-cleaned-up-downbeats-v2',
    false
  );

  useEffect(() => {
    if (hasCleanedUp) return;

    const cacheKeyPrefix = 'march-30-2025-clipDownbeats';
    const keys = Object.keys(localStorage);
    for (const key of keys) {
      if (key.startsWith(cacheKeyPrefix)) {
        localStorage.removeItem(key);
      }
    }

    setHasCleanedUp(true);
  }, [hasCleanedUp]);
};

const pollDownbeatsFetch = async (
  fetchFn: () => ReturnType<typeof fetchDownbeats>,
  firstRequestTime: number = Date.now()
): Promise<[number, number][]> => {
  const result = await fetchFn();
  if (Date.now() - firstRequestTime > 1000 * 60 * 5) {
    // stop trying after 5 minutes
    throw new Error('Timeout');
  }
  if (result.data?.state === 'running') {
    await sleep(2500);
    return pollDownbeatsFetch(fetchFn, firstRequestTime);
  } else if (result.data?.state === 'complete') {
    return (result.data.downbeats as [number, number][]).map(
      ([seconds, index]) =>
        [sanitizeNumber(seconds, 4), index] as [number, number]
    );
  } else {
    console.error(result.error);
    throw new Error(result.error || 'Unknown error');
  }
};

export async function fetchAndPollDownbeats(
  apiClient: ApiClient,
  clipId: string
): Promise<[number, number][]> {
  return await pollDownbeatsFetch(() => fetchDownbeats(apiClient, clipId));
}

const downbeatsFetchPromises: Partial<{
  [key: string]: Promise<[number, number][]>;
}> = {};

export function getCachedDownbeats(clipId: string): [number, number][] | null {
  const cachedDownbeats = localStorage.getItem(
    `march-30-2025-clipDownbeats-${clipId}`
  );
  return cachedDownbeats ? JSON.parse(cachedDownbeats) : null;
}

export async function cachedFetchAndPollDownbeats(
  apiClient: ApiClient,
  clipId: string
): Promise<[number, number][]> {
  if (downbeatsFetchPromises[clipId]) {
    return downbeatsFetchPromises[clipId];
  }

  downbeatsFetchPromises[clipId] = queryClient.fetchQuery({
    queryKey: ['downbeats', clipId, 'sep-23-2025'],
    queryFn: () => fetchAndPollDownbeats(apiClient, clipId),
  });

  return downbeatsFetchPromises[clipId];
}

export const useDownbeatsQuery = (clipId: string | null) => {
  const apiClient = useApiClient();
  const result = useQuery({
    queryKey: ['downbeats', clipId, 'sep-23-2025'],
    staleTime: ({ state }) => (state.data?.length ? Infinity : 0),
    queryFn: () => {
      const result = fetchAndPollDownbeats(apiClient, clipId!);
      return result;
    },
    enabled: !!clipId,
  });
  return result;
};

export default function useEditTimingQuery(
  clipId: string,
  skipCache: boolean = false
) {
  const apiClient = useApiClient();

  return useQuery({
    queryKey: ['timing', clipId],
    queryFn: async () => {
      const downbeats = skipCache
        ? await fetchAndPollDownbeats(apiClient, clipId)
        : await cachedFetchAndPollDownbeats(apiClient, clipId);
      return getTimingFromDownbeats(downbeats, 0.025);
    },
  });
}
