import { useEffect } from "react";
import { keepPreviousData, useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query";

import type { ProjectBinding } from "../../api";
import { errorMessage } from "../../api/errors";
import type { NativeProjectBinding } from "@app/native-bridge";
import { queryKeys } from "../../app/queryKeys";
import { useAppServices } from "../../app/useAppServices";
import { useConnectionSnapshot } from "../../app/useConnectionSnapshot";
import {
  workflowProjectEventCanChangeAttention,
  workflowProjectQuestionTaskID,
} from "../../app/workflowProjectEvents";

export function useProjectPages() {
  const { api } = useAppServices();
  const queryClient = useQueryClient();
  useEffect(
    () => () => {
      queryClient.removeQueries({ queryKey: queryKeys.projects, exact: true });
    },
    [queryClient],
  );
  return useInfiniteQuery({
    queryKey: queryKeys.projects,
    queryFn: async ({ pageParam }) => api.listProjects(pageParam),
    initialPageParam: "",
    getNextPageParam: (lastPage) => (lastPage.nextPageToken.length > 0 ? lastPage.nextPageToken : undefined),
    placeholderData: keepPreviousData,
  });
}

export function useGlobalAttentionPages() {
  const { api } = useAppServices();
  return useInfiniteQuery({
    queryKey: queryKeys.attention(""),
    queryFn: async ({ pageParam }) => api.listAttention("", pageParam),
    initialPageParam: "",
    getNextPageParam: (lastPage) => (lastPage.nextPageToken.length > 0 ? lastPage.nextPageToken : undefined),
    placeholderData: keepPreviousData,
  });
}

export function useGlobalAttentionEvents() {
  const { api, logger } = useAppServices();
  const connection = useConnectionSnapshot();
  const queryClient = useQueryClient();

  useEffect(() => {
    if (connection.phase !== "connected") {
      return;
    }
    let refreshFrame: number | null = null;
    const refreshAttention = () => {
      if (refreshFrame !== null) {
        return;
      }
      refreshFrame = window.requestAnimationFrame(() => {
        refreshFrame = null;
        void queryClient.invalidateQueries({ queryKey: queryKeys.allAttention, refetchType: "active" });
      });
    };
    const refreshQuestionTask = (params: unknown) => {
      const taskID = workflowProjectQuestionTaskID(params);
      if (taskID === null) {
        return;
      }
      void queryClient.invalidateQueries({ queryKey: queryKeys.task(taskID), refetchType: "active" });
      void queryClient.invalidateQueries({ queryKey: queryKeys.activity(taskID), refetchType: "active" });
      void queryClient.invalidateQueries({ queryKey: queryKeys.allPendingAsks, refetchType: "active" });
    };
    const subscription = api.subscribeProject("", {
      onOpen() {
        refreshAttention();
      },
      onEvent(_method, params) {
        refreshQuestionTask(params);
        if (workflowProjectEventCanChangeAttention(params)) {
          refreshAttention();
        }
      },
      onComplete() {
        return;
      },
      onError(error) {
        refreshAttention();
        void logger.append("warn", "Global attention subscription failed.", { error: errorMessage(error) });
      },
    });
    return () => {
      if (refreshFrame !== null) {
        window.cancelAnimationFrame(refreshFrame);
      }
      subscription.close();
    };
  }, [api, connection.generation, connection.phase, logger, queryClient]);
}

export function useProjectCreationEvents(onCreated: (binding: NativeProjectBinding) => void) {
  const { logger, nativeBridge } = useAppServices();
  useEffect(() => {
    if (!nativeBridge.capabilities.projectCreationWindow) {
      return undefined;
    }
    let active = true;
    let unlisten: (() => void) | undefined;
    void nativeBridge.projectCreation.onCreated(onCreated).then((nextUnlisten) => {
      if (!active) {
        nextUnlisten();
        return;
      }
      unlisten = nextUnlisten;
    }).catch((error: unknown) => {
      void logger.append("warn", "Project creation event listener failed.", { error: errorMessage(error) });
    });
    return () => {
      active = false;
      unlisten?.();
    };
  }, [logger, nativeBridge, onCreated]);
}

export function useProjectCreation() {
  const { api } = useAppServices();
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: async (input: ProjectCreateInput) =>
      api.createProject(input.name, input.key, input.workspaceRoot),
    onSuccess: async () => {
      await queryClient.invalidateQueries({ queryKey: queryKeys.projects });
    },
  });
}

export function useWorkspaceAttach() {
  const { api } = useAppServices();
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: async (input: WorkspaceAttachInput): Promise<ProjectBinding> =>
      api.attachWorkspace(input.projectID, input.workspaceRoot),
    onSuccess: async (_binding, input) => {
      await queryClient.invalidateQueries({ queryKey: queryKeys.workspaces(input.projectID) });
      await queryClient.invalidateQueries({ queryKey: queryKeys.projects });
    },
  });
}

export type ProjectCreateInput = Readonly<{
  name: string;
  key: string;
  workspaceRoot: string;
}>;

export type WorkspaceAttachInput = Readonly<{
  projectID: string;
  workspaceRoot: string;
}>;
