/**
 * Hook to auto-install the mcp-github connection from registry and run OAuth.
 * Used by the GitHub repo picker when no GitHub connection exists.
 */

import { useRef, useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import {
  useConnectionActions,
  useProjectContext,
  type ConnectionEntity,
} from "@/sdk";
import { authenticateAndPersistOAuth } from "@/lib/authenticate-and-persist-oauth";
import { authClient } from "@/lib/auth-client";
import { useRegistryApp } from "@/hooks/use-registry-app";
import { extractConnectionData } from "@/utils/extract-connection-data";
import { invalidateVirtualMcpQueries } from "@/lib/query-keys";

type Status = "idle" | "installing" | "authenticating" | "ready" | "error";

interface UseAutoInstallGitHubResult {
  status: Status;
  error: string | null;
  connection: ConnectionEntity | null;
  retry: () => void;
}

const GITHUB_APP_ID = "deco/mcp-github";

export function useAutoInstallGitHub(opts: {
  enabled: boolean;
}): UseAutoInstallGitHubResult {
  const { org } = useProjectContext();
  const { data: session } = authClient.useSession();
  const actions = useConnectionActions();
  const queryClient = useQueryClient();

  const [status, setStatus] = useState<Status>("idle");
  const [error, setError] = useState<string | null>(null);
  const [connection, setConnection] = useState<ConnectionEntity | null>(null);

  const { data: registryItem, isLoading: isRegistryLoading } = useRegistryApp(
    GITHUB_APP_ID,
    { enabled: opts.enabled },
  );

  // Track whether we've started the flow to avoid re-triggering.
  // useRef (not useState) because refs mutate synchronously — prevents
  // duplicate fires under React 19 concurrent rendering / Strict Mode.
  const startedRef = useRef(false);

  // Auto-trigger when registry data arrives and we haven't started yet
  if (
    opts.enabled &&
    registryItem &&
    !isRegistryLoading &&
    // oxlint-disable-next-line ban-ref-current-assignment/ban-ref-current-assignment -- TODO: refactor render-time .current access
    !startedRef.current &&
    session?.user?.id &&
    status === "idle"
  ) {
    // oxlint-disable-next-line ban-ref-current-assignment/ban-ref-current-assignment -- TODO: refactor render-time .current access
    startedRef.current = true;
    runInstallFlow();
  }

  async function runInstallFlow() {
    if (!registryItem || !session?.user?.id || !org) return;

    try {
      // Step 1: Create connection from registry
      setStatus("installing");
      setError(null);

      const connectionData = extractConnectionData(
        registryItem,
        org.id,
        session.user.id,
        { remoteIndex: 0 },
      );

      const remoteUrl = connectionData.connection_url;
      if (!remoteUrl) {
        throw new Error("Registry item is missing a remote URL for mcp-github");
      }

      const { id } = await actions.create.mutateAsync(connectionData);

      // Step 2: OAuth (if needed) + persist token, via the shared helper.
      setStatus("authenticating");
      const auth = await authenticateAndPersistOAuth({
        connectionId: id,
        orgId: org.id,
        orgSlug: org.slug,
        persistFallback: (token) =>
          actions.update
            .mutateAsync({
              id,
              data: { connection_token: token },
            })
            .then(() => undefined),
      });

      if (auth.ran && !auth.ok) {
        // OAuth failed or was cancelled — clean up the connection
        try {
          await actions.delete.mutateAsync(id);
        } catch {
          // Best-effort cleanup
        }
        // auth.error is the generic sentinel "no token received" when the
        // OAuth flow returned no token; surface the GitHub-specific message
        // in that case (and the null-error case), but pass through any real
        // OAuth error message as-is.
        throw new Error(
          auth.error && auth.error !== "no token received"
            ? auth.error
            : "No token received from GitHub",
        );
      }

      // Step 3: Invalidate connection queries so picker re-renders
      invalidateVirtualMcpQueries(queryClient, org.id);

      setConnection(connectionData as ConnectionEntity);
      setStatus("ready");
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
      setStatus("error");
    }
  }

  function retry() {
    setStatus("idle");
    setError(null);
    setConnection(null);
    startedRef.current = false;
  }

  // While registry is loading, show installing status
  if (opts.enabled && isRegistryLoading && status === "idle") {
    return { status: "installing", error: null, connection: null, retry };
  }

  return { status, error, connection, retry };
}
