import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { aiApi } from "../lib/api/ai";
import type { Character, CharacterState, Episode, Outline, Scene, ShotAsset, Storyboard, StructuredVideoRequest } from "../lib/api/ai";
import { useAuthStore } from "../stores/authStore";

// ---- Characters ----
const charactersKey = (pid: string) => ["characters", pid];
const allCharacterStatesKey = (pid: string) => ["character-states", pid];
const characterStatesKey = (pid: string, cid: string) => ["character-states", pid, cid];
const IMAGE_STATUS_POLL_MS = 3000;

function hasGeneratingItems<T extends { status: string }>(items: T[] | undefined) {
  return !!items?.some((item) => item.status === "generating");
}

function upsertById<T extends { id: string }>(items: T[] | undefined, next: T): T[] {
  if (!items || items.length === 0) {
    return [next];
  }
  let found = false;
  const merged = items.map((item) => {
    if (item.id !== next.id) {
      return item;
    }
    found = true;
    return { ...item, ...next };
  });
  return found ? merged : [...merged, next];
}

export function useCharacters(projectId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: charactersKey(projectId),
    queryFn: () => aiApi.listCharacters(projectId),
    enabled: isAuthReady && !!projectId,
    refetchInterval: (query) => hasGeneratingItems(query.state.data) ? IMAGE_STATUS_POLL_MS : false,
  });
}
export function useAllCharacterStates(projectId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: allCharacterStatesKey(projectId),
    queryFn: () => aiApi.listAllCharacterStates(projectId),
    enabled: isAuthReady && !!projectId,
    refetchInterval: (query) => hasGeneratingItems(query.state.data) ? IMAGE_STATUS_POLL_MS : false,
  });
}
export function useCharacterStates(projectId: string, characterId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: characterStatesKey(projectId, characterId),
    queryFn: () => aiApi.listCharacterStates(projectId, characterId),
    enabled: isAuthReady && !!projectId && !!characterId,
    refetchInterval: (query) => hasGeneratingItems(query.state.data) ? IMAGE_STATUS_POLL_MS : false,
  });
}
export function useExtractCharacters(projectId: string) {
  const qc = useQueryClient();
  return useMutation({ mutationFn: () => aiApi.extractCharacters(projectId), onSuccess: () => qc.invalidateQueries({ queryKey: charactersKey(projectId) }) });
}
export function useSaveCharacter(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (req: Partial<Character>) => aiApi.saveCharacter(projectId, req),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: charactersKey(projectId) });
      qc.invalidateQueries({ queryKey: allCharacterStatesKey(projectId) });
    },
  });
}
export function useSaveCharacterState(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ characterId, state }: { characterId: string; state: Partial<CharacterState> }) =>
      aiApi.saveCharacterState(projectId, characterId, state),
    onSuccess: (state) => {
      qc.invalidateQueries({ queryKey: allCharacterStatesKey(projectId) });
      qc.invalidateQueries({ queryKey: characterStatesKey(projectId, state.characterId) });
    },
  });
}
export function useGenerateCharacterImage(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => aiApi.generateCharacterImage(projectId, id),
    onMutate: async (id: string) => {
      await qc.cancelQueries({ queryKey: charactersKey(projectId) });
      const previous = qc.getQueryData<Character[]>(charactersKey(projectId));
      qc.setQueryData<Character[]>(charactersKey(projectId), (current) =>
        (current ?? []).map((item) =>
          item.id === id ? { ...item, status: "generating" } : item
        )
      );
      return { previous };
    },
    onSuccess: (character) => {
      qc.setQueryData<Character[]>(charactersKey(projectId), (current) => upsertById(current, character));
    },
    onError: (_error, _id, context) => {
      if (context?.previous) {
        qc.setQueryData(charactersKey(projectId), context.previous);
      }
    },
    onSettled: () => qc.invalidateQueries({ queryKey: charactersKey(projectId) }),
  });
}
export function useGenerateCharacterStateImage(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ characterId, stateId }: { characterId: string; stateId: string }) =>
      aiApi.generateCharacterStateImage(projectId, characterId, stateId),
    onMutate: async ({ characterId, stateId }) => {
      await Promise.all([
        qc.cancelQueries({ queryKey: allCharacterStatesKey(projectId) }),
        qc.cancelQueries({ queryKey: characterStatesKey(projectId, characterId) }),
      ]);
      const previousAll = qc.getQueryData<CharacterState[]>(allCharacterStatesKey(projectId));
      const previousCharacter = qc.getQueryData<CharacterState[]>(characterStatesKey(projectId, characterId));
      const mark = (items: CharacterState[] | undefined) =>
        (items ?? []).map((item) => item.id === stateId ? { ...item, status: "generating" } : item);
      qc.setQueryData<CharacterState[]>(allCharacterStatesKey(projectId), mark);
      qc.setQueryData<CharacterState[]>(characterStatesKey(projectId, characterId), mark);
      return { previousAll, previousCharacter };
    },
    onSuccess: (state) => {
      qc.setQueryData<CharacterState[]>(allCharacterStatesKey(projectId), (current) => upsertById(current, state));
      qc.setQueryData<CharacterState[]>(characterStatesKey(projectId, state.characterId), (current) => upsertById(current, state));
    },
    onError: (_error, variables, context) => {
      if (context?.previousAll) {
        qc.setQueryData(allCharacterStatesKey(projectId), context.previousAll);
      }
      if (context?.previousCharacter) {
        qc.setQueryData(characterStatesKey(projectId, variables.characterId), context.previousCharacter);
      }
    },
    onSettled: (_data, _error, variables) => {
      qc.invalidateQueries({ queryKey: allCharacterStatesKey(projectId) });
      qc.invalidateQueries({ queryKey: characterStatesKey(projectId, variables.characterId) });
    },
  });
}
export function useUploadCharacterImage(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ id, file, viewType }: { id: string; file: File; viewType: "front" | "side" | "back" }) =>
      aiApi.uploadCharacterImage(projectId, id, file, viewType),
    onSuccess: (character) => {
      qc.setQueryData<Character[]>(charactersKey(projectId), (current) => upsertById(current, character));
      qc.invalidateQueries({ queryKey: charactersKey(projectId) });
    },
  });
}
export function useDeleteCharacter(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => aiApi.deleteCharacter(projectId, id),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: charactersKey(projectId) });
      qc.invalidateQueries({ queryKey: allCharacterStatesKey(projectId) });
    },
  });
}
export function useDeleteCharacterState(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ characterId, stateId }: { characterId: string; stateId: string }) =>
      aiApi.deleteCharacterState(projectId, characterId, stateId),
    onSuccess: (_result, variables) => {
      qc.invalidateQueries({ queryKey: allCharacterStatesKey(projectId) });
      qc.invalidateQueries({ queryKey: characterStatesKey(projectId, variables.characterId) });
    },
  });
}

// ---- Scenes ----
const scenesKey = (pid: string) => ["scenes", pid];

export function useScenes(projectId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: scenesKey(projectId),
    queryFn: () => aiApi.listScenes(projectId),
    enabled: isAuthReady && !!projectId,
    refetchInterval: (query) => hasGeneratingItems(query.state.data) ? IMAGE_STATUS_POLL_MS : false,
  });
}
export function useExtractScenes(projectId: string) {
  const qc = useQueryClient();
  return useMutation({ mutationFn: () => aiApi.extractScenes(projectId), onSuccess: () => qc.invalidateQueries({ queryKey: scenesKey(projectId) }) });
}
export function useSaveScene(projectId: string) {
  const qc = useQueryClient();
  return useMutation({ mutationFn: (req: Partial<Scene>) => aiApi.saveScene(projectId, req), onSuccess: () => qc.invalidateQueries({ queryKey: scenesKey(projectId) }) });
}
export function useGenerateSceneImage(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => aiApi.generateSceneImage(projectId, id),
    onMutate: async (id: string) => {
      await qc.cancelQueries({ queryKey: scenesKey(projectId) });
      const previous = qc.getQueryData<Scene[]>(scenesKey(projectId));
      qc.setQueryData<Scene[]>(scenesKey(projectId), (current) =>
        (current ?? []).map((item) =>
          item.id === id ? { ...item, status: "generating" } : item
        )
      );
      return { previous };
    },
    onSuccess: (scene) => {
      qc.setQueryData<Scene[]>(scenesKey(projectId), (current) => upsertById(current, scene));
    },
    onError: (_error, _id, context) => {
      if (context?.previous) {
        qc.setQueryData(scenesKey(projectId), context.previous);
      }
    },
    onSettled: () => qc.invalidateQueries({ queryKey: scenesKey(projectId) }),
  });
}
export function useUploadSceneImage(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ id, file }: { id: string; file: File }) => aiApi.uploadSceneImage(projectId, id, file),
    onSuccess: (scene) => {
      qc.setQueryData<Scene[]>(scenesKey(projectId), (current) => upsertById(current, scene));
      qc.invalidateQueries({ queryKey: scenesKey(projectId) });
    },
  });
}
export function useDeleteScene(projectId: string) {
  const qc = useQueryClient();
  return useMutation({ mutationFn: (id: string) => aiApi.deleteScene(projectId, id), onSuccess: () => qc.invalidateQueries({ queryKey: scenesKey(projectId) }) });
}

const outlineKey = (pid: string) => ["outline", pid];
const episodesKey = (pid: string) => ["episodes", pid];
const storyboardsKey = (pid: string, eid: string) => ["storyboards", pid, eid];
const shotAssetsKey = (pid: string, sid: string) => ["shot-assets", pid, sid];
const videoTasksKey = (pid: string) => ["video-tasks", pid];
const assemblyKey = (pid: string, eid: string) => ["assembly", pid, eid];

// ---- Outline ----
export function useOutline(projectId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: outlineKey(projectId),
    queryFn: () => aiApi.getOutline(projectId),
    enabled: isAuthReady && !!projectId,
    retry: false,
  });
}

export function useGenerateOutline(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (script: string) => aiApi.generateOutline(projectId, script),
    onSuccess: () => qc.invalidateQueries({ queryKey: outlineKey(projectId) }),
  });
}

export function useUpdateOutline(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (patch: Partial<Outline>) => aiApi.updateOutline(projectId, patch),
    onSuccess: () => qc.invalidateQueries({ queryKey: outlineKey(projectId) }),
  });
}

// ---- Episodes ----
export function useEpisodes(projectId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: episodesKey(projectId),
    queryFn: () => aiApi.listEpisodes(projectId),
    enabled: isAuthReady && !!projectId,
    staleTime: 0,
  });
}

export function useGenerateEpisodes(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: () => aiApi.generateEpisodes(projectId),
    onSuccess: () => qc.invalidateQueries({ queryKey: episodesKey(projectId) }),
  });
}

export function useCreateEpisode(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (req: Partial<Episode>) => aiApi.createEpisode(projectId, req),
    onSuccess: () => qc.invalidateQueries({ queryKey: episodesKey(projectId) }),
  });
}

export function useUpdateEpisode(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ id, patch }: { id: string; patch: Partial<Episode> }) =>
      aiApi.updateEpisode(projectId, id, patch),
    onSuccess: () => qc.invalidateQueries({ queryKey: episodesKey(projectId) }),
  });
}

// ---- Storyboards ----
export function useStoryboards(projectId: string, episodeId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: storyboardsKey(projectId, episodeId),
    queryFn: () => aiApi.listStoryboards(projectId, episodeId),
    enabled: isAuthReady && !!projectId && !!episodeId,
  });
}

export function useCreateStoryboard(projectId: string, episodeId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (req: Partial<Storyboard> | Record<string, unknown>) =>
      aiApi.createStoryboard(projectId, episodeId, req as Storyboard),
    onSuccess: () => qc.invalidateQueries({ queryKey: storyboardsKey(projectId, episodeId) }),
  });
}

export function useGenerateStoryboards(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (episodeId: string) => aiApi.generateStoryboards(projectId, episodeId),
    onSuccess: (_, episodeId) => qc.invalidateQueries({ queryKey: storyboardsKey(projectId, episodeId) }),
  });
}

export function useUpdateStoryboard(projectId: string, episodeId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ id, patch }: { id: string; patch: Record<string, unknown> }) =>
      aiApi.updateStoryboard(projectId, id, patch),
    onSuccess: () => qc.invalidateQueries({ queryKey: storyboardsKey(projectId, episodeId) }),
  });
}

export function useUploadStoryboardFrameImage(projectId: string, episodeId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ id, frameType, file }: { id: string; frameType: "first" | "last"; file: File }) =>
      aiApi.uploadStoryboardFrameImage(projectId, id, frameType, file),
    onSuccess: () => qc.invalidateQueries({ queryKey: storyboardsKey(projectId, episodeId) }),
  });
}

export function useDeleteStoryboard(projectId: string, episodeId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id: string) => aiApi.deleteStoryboard(projectId, id),
    onSuccess: () => qc.invalidateQueries({ queryKey: storyboardsKey(projectId, episodeId) }),
  });
}

export function useReorderStoryboards(projectId: string, episodeId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (ids: string[]) => aiApi.reorderStoryboards(projectId, ids),
    onSuccess: () => qc.invalidateQueries({ queryKey: storyboardsKey(projectId, episodeId) }),
  });
}

export function useGenerateStoryboardPrompt(projectId: string) {
  return useMutation({
    mutationFn: (storyboardId: string) => aiApi.generateStoryboardPrompt(projectId, storyboardId),
  });
}

export function useShotAssets(projectId: string, storyboardId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: shotAssetsKey(projectId, storyboardId),
    queryFn: () => aiApi.listShotAssets(projectId, storyboardId),
    enabled: isAuthReady && !!projectId && !!storyboardId,
  });
}

export function useActivateShotAsset(projectId: string, storyboardId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (assetId: string) => aiApi.activateShotAsset(projectId, storyboardId, assetId),
    onSuccess: (asset: ShotAsset) => {
      qc.invalidateQueries({ queryKey: shotAssetsKey(projectId, storyboardId) });
      if (asset.sourceTaskId) {
        qc.invalidateQueries({ queryKey: videoTasksKey(projectId) });
      }
    },
  });
}

// ---- Video Tasks ----
export function useVideoTasks(projectId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: videoTasksKey(projectId),
    queryFn: () => aiApi.listVideoTasks(projectId),
    enabled: isAuthReady && !!projectId,
    staleTime: 0,
    refetchOnWindowFocus: true,
    refetchInterval: (query) => {
      const tasks = query.state.data;
      if (!tasks) return false;
      const hasActive = tasks.some(
        (t) => t.status === "pending" || t.status === "submitted" || t.status === "running"
      );
      return hasActive ? 5000 : false;
    },
  });
}

export function useGenerateVideo(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ episodeId, prompt, videoPrompt, storyboardId, imageKeys, duration }: {
      episodeId: string | null;
      prompt: string;
      videoPrompt?: string;
      storyboardId?: string | null;
      imageKeys?: string[] | null;
      duration?: number;
    }) => aiApi.generateVideo(projectId, episodeId, prompt, videoPrompt, storyboardId, imageKeys, duration),
    onSuccess: () => qc.invalidateQueries({ queryKey: videoTasksKey(projectId) }),
  });
}

export function useGenerateStructuredVideo(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (req: StructuredVideoRequest) => aiApi.generateStructuredVideo(projectId, req),
    onSuccess: () => qc.invalidateQueries({ queryKey: videoTasksKey(projectId) }),
  });
}

// ---- Assembly ----
export function useAssemblyTask(projectId: string, episodeId: string) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: assemblyKey(projectId, episodeId),
    queryFn: () => aiApi.getAssemblyTask(projectId, episodeId),
    enabled: isAuthReady && !!projectId && !!episodeId,
    refetchInterval: (query) => {
      const data = query.state.data;
      if (!data) return false;
      const { status } = data;
      return status === "succeeded" || status === "failed" ? false : 5000;
    },
  });
}

export function useStartAssembly(projectId: string, episodeId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (videoTaskIds?: string[]) => aiApi.startAssembly(projectId, episodeId, videoTaskIds),
    onSuccess: () => qc.invalidateQueries({ queryKey: assemblyKey(projectId, episodeId) }),
  });
}

export function useDeleteVideoTask(projectId: string) {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (taskId: string) => aiApi.deleteVideoTask(projectId, taskId),
    onSuccess: () => qc.invalidateQueries({ queryKey: videoTasksKey(projectId) }),
  });
}

export function usePollVideoTask(projectId: string, taskId: string, enabled: boolean) {
  const isAuthReady = useAuthStore((state) => state.hasHydrated && state.isAuthenticated);
  return useQuery({
    queryKey: ["video-task", projectId, taskId],
    queryFn: () => aiApi.pollVideoTask(projectId, taskId),
    enabled: isAuthReady && enabled && !!taskId,
    refetchInterval: (query) => {
      const status = query.state.data?.status;
      return status === "succeeded" || status === "failed" ? false : 5000;
    },
  });
}
