import {
  CheckCircleIcon,
  ExclamationTriangleIcon,
  ChevronDownIcon,
  ChevronUpIcon,
} from "@heroicons/react/20/solid";
import { useFetcher, useNavigation, useSearchParams } from "@remix-run/react";
import { useTypedFetcher } from "remix-typedjson";
import { Dialog, DialogContent, DialogHeader } from "~/components/primitives/Dialog";
import { Button, LinkButton } from "~/components/primitives/Buttons";
import { Callout } from "~/components/primitives/Callout";
import { FormButtons } from "~/components/primitives/FormButtons";
import { FormError } from "~/components/primitives/FormError";
import { Header3 } from "~/components/primitives/Headers";
import { Hint } from "~/components/primitives/Hint";
import { Label } from "~/components/primitives/Label";
import { Paragraph } from "~/components/primitives/Paragraph";
import { Select, SelectItem } from "~/components/primitives/Select";
import { SpinnerWhite } from "~/components/primitives/Spinner";
import { Switch } from "~/components/primitives/Switch";
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
  TooltipProvider,
} from "~/components/primitives/Tooltip";
import { VercelLogo } from "~/components/integrations/VercelLogo";
import { BuildSettingsFields } from "~/components/integrations/VercelBuildSettings";
import { OctoKitty } from "~/components/GitHubLoginButton";
import { ConnectGitHubRepoModal } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github";
import {
  type SyncEnvVarsMapping,
  type EnvSlug,
  ALL_ENV_SLUGS,
  shouldSyncEnvVarForAnyEnvironment,
  getAvailableEnvSlugs,
  getAvailableEnvSlugsForBuildSettings,
} from "~/v3/vercel/vercelProjectIntegrationSchema";
import { type VercelCustomEnvironment } from "~/models/vercelIntegration.server";
import { type VercelOnboardingData } from "~/presenters/v3/VercelSettingsPresenter.server";
import {
  vercelAppInstallPath,
  v3ProjectSettingsIntegrationsPath,
  githubAppInstallPath,
  vercelResourcePath,
} from "~/utils/pathBuilder";
import type { loader } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel";
import { useEffect, useState, useCallback, useRef } from "react";
import { usePostHogTracking } from "~/hooks/usePostHog";
import { TextLink } from "../primitives/TextLink";

function safeRedirectUrl(url: string): string | null {
  try {
    const parsed = new URL(url, window.location.origin);
    if (parsed.origin === window.location.origin) {
      return parsed.toString();
    }
    if (parsed.protocol === "https:" && /^([a-z0-9-]+\.)*vercel\.com$/i.test(parsed.hostname)) {
      return parsed.toString();
    }
  } catch {
    // Invalid URL
  }
  return null;
}

function formatVercelTargets(targets: string[]): string {
  const targetLabels: Record<string, string> = {
    production: "Production",
    preview: "Preview",
    development: "Development",
    staging: "Staging",
  };

  return targets.map((t) => targetLabels[t.toLowerCase()] || t).join(", ");
}

type OnboardingState =
  | "idle"
  | "installing"
  | "loading-projects"
  | "project-selection"
  | "loading-env-mapping"
  | "env-mapping"
  | "loading-env-vars"
  | "env-var-sync"
  | "build-settings"
  | "github-connection"
  | "completed";

export function VercelOnboardingModal({
  isOpen,
  onClose,
  onboardingData,
  organizationSlug,
  projectSlug,
  environmentSlug,
  hasStagingEnvironment,
  hasPreviewEnvironment,
  hasOrgIntegration,
  nextUrl,
  onDataReload,
  vercelManageAccessUrl,
}: {
  isOpen: boolean;
  onClose: () => void;
  onboardingData: VercelOnboardingData | null;
  organizationSlug: string;
  projectSlug: string;
  environmentSlug: string;
  hasStagingEnvironment: boolean;
  hasPreviewEnvironment: boolean;
  hasOrgIntegration: boolean;
  nextUrl?: string;
  onDataReload?: (vercelStagingEnvironment?: string) => void;
  vercelManageAccessUrl?: string;
}) {
  const { capture, startSessionRecording } = usePostHogTracking();
  const navigation = useNavigation();
  const fetcher = useTypedFetcher<typeof loader>();
  const envMappingFetcher = useFetcher();
  const completeOnboardingFetcher = useFetcher();
  const { Form: _CompleteOnboardingForm } = completeOnboardingFetcher;
  const [searchParams] = useSearchParams();
  const origin = searchParams.get("origin");
  const fromMarketplaceContext = origin === "marketplace";

  const availableProjects = onboardingData?.availableProjects || [];
  const _hasProjectSelected = onboardingData?.hasProjectSelected ?? false;
  const customEnvironments = onboardingData?.customEnvironments || [];
  const envVars = onboardingData?.environmentVariables || [];
  const existingVars = onboardingData?.existingVariables || {};
  const hasCustomEnvs = customEnvironments.length > 0 && hasStagingEnvironment;

  const computeInitialState = useCallback((): OnboardingState => {
    if (!hasOrgIntegration || onboardingData?.authInvalid) {
      return "idle";
    }
    const projectSelected = onboardingData?.hasProjectSelected ?? false;
    if (!projectSelected) {
      if (!onboardingData?.availableProjects || onboardingData.availableProjects.length === 0) {
        return "loading-projects";
      }
      return "project-selection";
    }
    // If onboarding was already completed but GitHub is not connected,
    // go directly to the github-connection step (e.g., returning from GitHub App installation)
    if (onboardingData?.isOnboardingComplete && !onboardingData?.isGitHubConnected) {
      return "github-connection";
    }
    // For marketplace origin, skip env-mapping step and go directly to env-var-sync
    if (!fromMarketplaceContext) {
      const customEnvs =
        (onboardingData?.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment;
      if (customEnvs) {
        return "env-mapping";
      }
    }
    if (!onboardingData?.environmentVariables || onboardingData.environmentVariables.length === 0) {
      return "loading-env-vars";
    }
    return "env-var-sync";
  }, [hasOrgIntegration, onboardingData, hasStagingEnvironment, fromMarketplaceContext]);

  const [state, setState] = useState<OnboardingState>(() => {
    if (!isOpen) return "idle";
    return computeInitialState();
  });

  const prevIsOpenRef = useRef(isOpen);
  const hasSyncedStagingRef = useRef(false);
  const hasSyncedPreviewRef = useRef(false);
  useEffect(() => {
    if (isOpen && !prevIsOpenRef.current) {
      setState(computeInitialState());
      hasSyncedStagingRef.current = false;
      hasSyncedPreviewRef.current = false;
    } else if (isOpen && state === "idle") {
      setState(computeInitialState());
    }
    prevIsOpenRef.current = isOpen;
  }, [isOpen, state, computeInitialState]);

  const trackOnboarding = useCallback(
    (eventName: string, extraProperties?: Record<string, unknown>) => {
      capture(eventName, {
        origin: fromMarketplaceContext ? "marketplace" : "dashboard",
        step: state,
        organization_slug: organizationSlug,
        project_slug: projectSlug,
        ...extraProperties,
      });
    },
    [capture, fromMarketplaceContext, state, organizationSlug, projectSlug]
  );

  const hasTrackedStartRef = useRef(false);
  const hasTrackedCompletionRef = useRef(false);
  useEffect(() => {
    if (isOpen && state === "project-selection" && !hasTrackedStartRef.current) {
      hasTrackedStartRef.current = true;
      startSessionRecording();
      trackOnboarding("vercel onboarding started");
    }
    if (!isOpen) {
      hasTrackedStartRef.current = false;
      hasTrackedCompletionRef.current = false;
    }
  }, [isOpen, state, trackOnboarding, startSessionRecording]);

  const [selectedVercelProject, setSelectedVercelProject] = useState<{
    id: string;
    name: string;
  } | null>(null);
  const [vercelStagingEnvironment, setVercelStagingEnvironment] = useState<{
    environmentId: string;
    displayName: string;
  } | null>(null);
  const _availableEnvSlugsForOnboarding = getAvailableEnvSlugs(
    hasStagingEnvironment,
    hasPreviewEnvironment
  );
  const availableEnvSlugsForOnboardingBuildSettings = getAvailableEnvSlugsForBuildSettings(
    hasStagingEnvironment,
    hasPreviewEnvironment
  );
  const [pullEnvVarsBeforeBuild, setPullEnvVarsBeforeBuild] = useState<EnvSlug[]>(
    () => availableEnvSlugsForOnboardingBuildSettings
  );
  const [atomicBuilds, setAtomicBuilds] = useState<EnvSlug[]>(() => ["prod"]);
  const [discoverEnvVars, setDiscoverEnvVars] = useState<EnvSlug[]>(
    () => availableEnvSlugsForOnboardingBuildSettings
  );

  // Sync pullEnvVarsBeforeBuild and discoverEnvVars when hasStagingEnvironment becomes true
  // AND a custom Vercel environment is mapped (once)
  useEffect(() => {
    if (hasStagingEnvironment && vercelStagingEnvironment && !hasSyncedStagingRef.current) {
      hasSyncedStagingRef.current = true;
      setPullEnvVarsBeforeBuild((prev) => {
        if (!prev.includes("stg")) {
          return [...prev, "stg"];
        }
        return prev;
      });
      setDiscoverEnvVars((prev) => {
        if (!prev.includes("stg")) {
          return [...prev, "stg"];
        }
        return prev;
      });
    }
  }, [hasStagingEnvironment, vercelStagingEnvironment]);

  // Strip "stg" from build settings when the staging environment mapping is cleared
  useEffect(() => {
    if (!vercelStagingEnvironment) {
      setPullEnvVarsBeforeBuild((prev) => prev.filter((s) => s !== "stg"));
      setDiscoverEnvVars((prev) => prev.filter((s) => s !== "stg"));
    }
  }, [vercelStagingEnvironment]);

  // Sync pullEnvVarsBeforeBuild and discoverEnvVars when hasPreviewEnvironment becomes true (once)
  useEffect(() => {
    if (hasPreviewEnvironment && !hasSyncedPreviewRef.current) {
      hasSyncedPreviewRef.current = true;
      setPullEnvVarsBeforeBuild((prev) => {
        if (!prev.includes("preview")) {
          return [...prev, "preview"];
        }
        return prev;
      });
      setDiscoverEnvVars((prev) => {
        if (!prev.includes("preview")) {
          return [...prev, "preview"];
        }
        return prev;
      });
    }
  }, [hasPreviewEnvironment]);
  const [syncEnvVarsMapping, setSyncEnvVarsMapping] = useState<SyncEnvVarsMapping>({});
  const [expandedEnvVars, setExpandedEnvVars] = useState(false);
  const [expandedSecretEnvVars, setExpandedSecretEnvVars] = useState(false);
  const [projectSelectionError, setProjectSelectionError] = useState<string | null>(null);
  const [isRedirecting, setIsRedirecting] = useState(false);

  const gitHubAppInstallations = onboardingData?.gitHubAppInstallations ?? [];
  const isGitHubConnectedForOnboarding = onboardingData?.isGitHubConnected ?? false;
  const isOnboardingComplete = onboardingData?.isOnboardingComplete ?? false;

  const hasTriggeredMarketplaceRedirectRef = useRef(false);

  // Auto-redirect for marketplace flow when returning from GitHub with everything complete
  useEffect(() => {
    if (hasTriggeredMarketplaceRedirectRef.current) {
      return;
    }

    if (
      isOpen &&
      fromMarketplaceContext &&
      nextUrl &&
      isOnboardingComplete &&
      isGitHubConnectedForOnboarding
    ) {
      hasTriggeredMarketplaceRedirectRef.current = true;
      const validUrl = safeRedirectUrl(nextUrl);
      if (validUrl) {
        setTimeout(() => {
          window.location.href = validUrl;
        }, 100);
      }
    }
  }, [
    isOpen,
    fromMarketplaceContext,
    nextUrl,
    isOnboardingComplete,
    isGitHubConnectedForOnboarding,
  ]);

  useEffect(() => {
    if (!isOpen) {
      hasTriggeredMarketplaceRedirectRef.current = false;
      setIsRedirecting(false);
    }
  }, [isOpen]);

  const loadingStateRef = useRef<OnboardingState | null>(null);

  useEffect(() => {
    if (!isOpen || state === "idle") {
      loadingStateRef.current = null;
      return;
    }

    if (onboardingData?.authInvalid) {
      onClose();
      return;
    }

    if (loadingStateRef.current === state) {
      return;
    }

    switch (state) {
      case "loading-projects":
        loadingStateRef.current = state;
        if (onDataReload) {
          onDataReload();
        }
        break;

      case "loading-env-mapping":
        loadingStateRef.current = state;
        if (onDataReload) {
          onDataReload();
        }
        break;

      case "loading-env-vars":
        loadingStateRef.current = state;
        if (onDataReload) {
          onDataReload(vercelStagingEnvironment?.environmentId || undefined);
        }
        break;

      case "installing":
      case "project-selection":
      case "env-mapping":
      case "env-var-sync":
      case "completed":
      case "build-settings":
      case "github-connection":
        loadingStateRef.current = null;
        break;
    }
  }, [isOpen, state, onboardingData?.authInvalid, vercelStagingEnvironment, onDataReload, onClose]);

  useEffect(() => {
    if (
      !onboardingData?.authInvalid &&
      state === "loading-projects" &&
      onboardingData?.availableProjects !== undefined
    ) {
      setState("project-selection");
    }
  }, [state, onboardingData?.availableProjects, onboardingData?.authInvalid]);

  useEffect(() => {
    if (
      !onboardingData?.authInvalid &&
      state === "loading-env-vars" &&
      onboardingData?.environmentVariables
    ) {
      setState("env-var-sync");
    }
  }, [state, onboardingData?.environmentVariables, onboardingData?.authInvalid]);

  useEffect(() => {
    if (
      state === "project-selection" &&
      fetcher.data &&
      "success" in fetcher.data &&
      fetcher.data.success &&
      fetcher.state === "idle"
    ) {
      trackOnboarding("vercel onboarding project selected", {
        vercel_project_name: selectedVercelProject?.name,
      });
      setState("loading-env-mapping");
      if (onDataReload) {
        onDataReload();
      }
    } else if (fetcher.data && "error" in fetcher.data && typeof fetcher.data.error === "string") {
      setProjectSelectionError(fetcher.data.error);
    }
  }, [
    state,
    fetcher.data,
    fetcher.state,
    onDataReload,
    trackOnboarding,
    selectedVercelProject?.name,
  ]);

  // For marketplace origin, skip env-mapping step
  useEffect(() => {
    if (state === "loading-env-mapping" && onboardingData) {
      const hasCustomEnvs =
        (onboardingData.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment;
      if (hasCustomEnvs && !fromMarketplaceContext) {
        setState("env-mapping");
      } else {
        setState("loading-env-vars");
      }
    }
  }, [state, onboardingData, hasStagingEnvironment, fromMarketplaceContext]);

  const secretEnvVars = envVars.filter((v) => v.isSecret);
  const syncableEnvVars = envVars.filter((v) => !v.isSecret);
  const enabledEnvVars = syncableEnvVars.filter((v) =>
    shouldSyncEnvVarForAnyEnvironment(syncEnvVarsMapping, v.key)
  );

  const overlappingEnvVarsCount = enabledEnvVars.filter((v) => existingVars[v.key]).length;

  const _isSubmitting = navigation.state === "submitting" || navigation.state === "loading";

  const actionUrl = vercelResourcePath(organizationSlug, projectSlug, environmentSlug);

  const handleToggleEnvVar = useCallback((key: string, enabled: boolean) => {
    setSyncEnvVarsMapping((prev) => {
      const newMapping = { ...prev };

      if (enabled) {
        for (const envSlug of ALL_ENV_SLUGS) {
          if (newMapping[envSlug]) {
            const { [key]: _, ...rest } = newMapping[envSlug];
            if (Object.keys(rest).length === 0) {
              delete newMapping[envSlug];
            } else {
              newMapping[envSlug] = rest;
            }
          }
        }
      } else {
        for (const envSlug of ALL_ENV_SLUGS) {
          newMapping[envSlug] = {
            ...(newMapping[envSlug] || {}),
            [key]: false,
          };
        }
      }

      return newMapping;
    });
  }, []);

  const handleToggleAllEnvVars = useCallback(
    (enabled: boolean, syncableVars: Array<{ key: string }>) => {
      if (enabled) {
        setSyncEnvVarsMapping({});
      } else {
        const newMapping: SyncEnvVarsMapping = {};
        for (const envSlug of ALL_ENV_SLUGS) {
          newMapping[envSlug] = {};
          for (const v of syncableVars) {
            newMapping[envSlug][v.key] = false;
          }
        }
        setSyncEnvVarsMapping(newMapping);
      }
    },
    []
  );

  const handleProjectSelection = useCallback(async () => {
    if (!selectedVercelProject) {
      setProjectSelectionError("Please select a Vercel project");
      return;
    }

    setProjectSelectionError(null);

    const formData = new FormData();
    formData.append("action", "select-vercel-project");
    formData.append("vercelProjectId", selectedVercelProject.id);
    formData.append("vercelProjectName", selectedVercelProject.name);

    fetcher.submit(formData, {
      method: "post",
      action: actionUrl,
    });
  }, [selectedVercelProject, fetcher, actionUrl]);

  const handleSkipOnboarding = useCallback(() => {
    onClose();

    if (fromMarketplaceContext) {
      return window.close();
    }

    const formData = new FormData();
    formData.append("action", "skip-onboarding");
    fetcher.submit(formData, {
      method: "post",
      action: actionUrl,
    });
  }, [actionUrl, fetcher, onClose, fromMarketplaceContext]);

  const handleSkipEnvMapping = useCallback(() => {
    trackOnboarding("vercel onboarding env mapping completed", {
      skipped: true,
      staging_environment: null,
    });
    setVercelStagingEnvironment(null);
    setState("loading-env-vars");
  }, [trackOnboarding]);

  const handleUpdateEnvMapping = useCallback(() => {
    trackOnboarding("vercel onboarding env mapping completed", {
      skipped: false,
      staging_environment: vercelStagingEnvironment?.displayName ?? null,
    });

    if (!vercelStagingEnvironment) {
      setState("loading-env-vars");
      return;
    }

    const formData = new FormData();
    formData.append("action", "update-env-mapping");
    formData.append("vercelStagingEnvironment", JSON.stringify(vercelStagingEnvironment));

    envMappingFetcher.submit(formData, {
      method: "post",
      action: actionUrl,
    });
  }, [vercelStagingEnvironment, envMappingFetcher, actionUrl, trackOnboarding]);

  const handleBuildSettingsNext = useCallback(() => {
    trackOnboarding("vercel onboarding build settings completed");

    if (nextUrl && fromMarketplaceContext && isGitHubConnectedForOnboarding) {
      setIsRedirecting(true);
    }

    const formData = new FormData();
    formData.append("action", "complete-onboarding");
    formData.append(
      "vercelStagingEnvironment",
      vercelStagingEnvironment ? JSON.stringify(vercelStagingEnvironment) : ""
    );
    formData.append("pullEnvVarsBeforeBuild", JSON.stringify(pullEnvVarsBeforeBuild));
    formData.append("atomicBuilds", JSON.stringify(atomicBuilds));
    formData.append("discoverEnvVars", JSON.stringify(discoverEnvVars));
    formData.append("syncEnvVarsMapping", JSON.stringify(syncEnvVarsMapping));
    if (fromMarketplaceContext) {
      formData.append("origin", "marketplace");
    }
    if (nextUrl && fromMarketplaceContext && isGitHubConnectedForOnboarding) {
      formData.append("next", nextUrl);
    }

    if (!isGitHubConnectedForOnboarding) {
      formData.append("skipRedirect", "true");
    }

    completeOnboardingFetcher.submit(formData, {
      method: "post",
      action: actionUrl,
    });

    if (!isGitHubConnectedForOnboarding) {
      setState("github-connection");
      capture("vercel onboarding github step viewed", {
        origin: fromMarketplaceContext ? "marketplace" : "dashboard",
        step: "github-connection",
        organization_slug: organizationSlug,
        project_slug: projectSlug,
        github_app_installed: gitHubAppInstallations.length > 0,
      });
    }
  }, [
    vercelStagingEnvironment,
    pullEnvVarsBeforeBuild,
    atomicBuilds,
    discoverEnvVars,
    syncEnvVarsMapping,
    nextUrl,
    fromMarketplaceContext,
    isGitHubConnectedForOnboarding,
    completeOnboardingFetcher,
    actionUrl,
    trackOnboarding,
    capture,
    organizationSlug,
    projectSlug,
    gitHubAppInstallations.length,
  ]);

  const _handleFinishOnboarding = useCallback(
    (e: React.FormEvent<HTMLFormElement>) => {
      e.preventDefault();
      const form = e.currentTarget;
      const formData = new FormData(form);
      completeOnboardingFetcher.submit(formData, {
        method: "post",
        action: actionUrl,
      });
    },
    [completeOnboardingFetcher, actionUrl]
  );

  useEffect(() => {
    if (
      completeOnboardingFetcher.data &&
      typeof completeOnboardingFetcher.data === "object" &&
      "success" in completeOnboardingFetcher.data &&
      completeOnboardingFetcher.data.success &&
      completeOnboardingFetcher.state === "idle"
    ) {
      if (state === "github-connection") {
        return;
      }
      if (
        "redirectTo" in completeOnboardingFetcher.data &&
        typeof completeOnboardingFetcher.data.redirectTo === "string"
      ) {
        const validRedirect = safeRedirectUrl(completeOnboardingFetcher.data.redirectTo);
        if (validRedirect) {
          window.location.href = validRedirect;
        }
        return;
      }
      setState("completed");
    }
  }, [completeOnboardingFetcher.data, completeOnboardingFetcher.state, state]);

  useEffect(() => {
    if (state === "github-connection" && isGitHubConnectedForOnboarding) {
      trackOnboarding("vercel onboarding github completed");
      if (fromMarketplaceContext && nextUrl) {
        const validUrl = safeRedirectUrl(nextUrl);
        if (validUrl) {
          window.location.href = validUrl;
          return;
        }
      }
      setState("completed");
    }
  }, [state, isGitHubConnectedForOnboarding, fromMarketplaceContext, nextUrl, trackOnboarding]);

  useEffect(() => {
    if (state === "completed" && !hasTrackedCompletionRef.current) {
      hasTrackedCompletionRef.current = true;
      trackOnboarding("vercel onboarding completed", {
        github_connected: isGitHubConnectedForOnboarding,
      });
      onClose();
    }
  }, [state, onClose, trackOnboarding, isGitHubConnectedForOnboarding]);

  useEffect(() => {
    if (state === "installing") {
      const installUrl = vercelAppInstallPath(organizationSlug, projectSlug);
      window.location.href = installUrl;
    }
  }, [state, organizationSlug, projectSlug]);

  useEffect(() => {
    if (
      envMappingFetcher.data &&
      typeof envMappingFetcher.data === "object" &&
      "success" in envMappingFetcher.data &&
      envMappingFetcher.data.success &&
      envMappingFetcher.state === "idle"
    ) {
      setState("loading-env-vars");
    }
  }, [envMappingFetcher.data, envMappingFetcher.state]);

  useEffect(() => {
    if (state === "env-mapping" && customEnvironments.length > 0 && !vercelStagingEnvironment) {
      let selectedEnv: VercelCustomEnvironment;

      if (customEnvironments.length === 1) {
        selectedEnv = customEnvironments[0];
      } else {
        const stagingEnv = customEnvironments.find((env) => env.slug.toLowerCase() === "staging");
        selectedEnv = stagingEnv ?? customEnvironments[0];
      }

      setVercelStagingEnvironment({ environmentId: selectedEnv.id, displayName: selectedEnv.slug });
    }
  }, [state, customEnvironments, vercelStagingEnvironment]);

  useEffect(() => {
    if (state === "project-selection" && availableProjects.length > 0 && !selectedVercelProject) {
      setSelectedVercelProject(availableProjects[0]);
    }
  }, [state, availableProjects, selectedVercelProject]);

  if (!isOpen || onboardingData?.authInvalid) {
    return null;
  }

  const isLoadingState =
    state === "loading-projects" ||
    state === "loading-env-mapping" ||
    state === "loading-env-vars" ||
    state === "installing" ||
    (state === "idle" && !onboardingData);

  if (isLoadingState) {
    return (
      <Dialog
        open={isOpen}
        onOpenChange={(open) => {
          if (!open && !fromMarketplaceContext) {
            if ((state as string) !== "completed") {
              trackOnboarding("vercel onboarding abandoned");
            }
            onClose();
          }
        }}
      >
        <DialogContent className="max-w-lg" onInteractOutside={(e) => e.preventDefault()}>
          <DialogHeader>
            <div className="flex items-center gap-2">
              <VercelLogo className="size-5" />
              <span>Set up Vercel Integration</span>
            </div>
          </DialogHeader>
          <div className="flex items-center justify-center py-8">
            <SpinnerWhite className="size-6" />
          </div>
        </DialogContent>
      </Dialog>
    );
  }

  const showProjectSelection = state === "project-selection";
  const showEnvMapping = state === "env-mapping";
  const showEnvVarSync = state === "env-var-sync";
  const showBuildSettings = state === "build-settings";
  const showGitHubConnection = state === "github-connection";

  const disabledEnvSlugsForBuildSettings =
    hasStagingEnvironment && !vercelStagingEnvironment
      ? ({ stg: "Map a custom Vercel environment to Staging to enable this" } as Partial<
          Record<EnvSlug, string>
        >)
      : undefined;

  return (
    <Dialog
      open={isOpen}
      onOpenChange={(open) => {
        if (!open && !fromMarketplaceContext) {
          if (state !== "completed") {
            trackOnboarding("vercel onboarding abandoned");
          }
          onClose();
        }
      }}
    >
      <DialogContent className="max-w-lg" onInteractOutside={(e) => e.preventDefault()}>
        <DialogHeader>
          <div className="flex items-center gap-2">
            <VercelLogo className="size-5" />
            <span>Set up Vercel Integration</span>
          </div>
        </DialogHeader>

        <div className="mt-4">
          {showProjectSelection && (
            <div className="flex flex-col gap-4">
              <Header3>Select Vercel Project</Header3>
              <Paragraph className="text-sm">
                Choose which Vercel project to connect with this Trigger.dev project. Your API keys
                will be automatically synced to Vercel.
              </Paragraph>

              {availableProjects.length === 0 ? (
                <Callout variant="warning">
                  No Vercel projects found. Please create a project in Vercel first.
                </Callout>
              ) : (
                <Select
                  disabled={availableProjects.length === 1}
                  value={selectedVercelProject?.id || ""}
                  setValue={(value) => {
                    if (!Array.isArray(value)) {
                      const project = availableProjects.find((p) => p.id === value);
                      setSelectedVercelProject(project || null);
                      setProjectSelectionError(null);
                    }
                  }}
                  items={availableProjects}
                  filter={availableProjects.length > 5 ? { keys: ["name"] } : undefined}
                  variant="tertiary/medium"
                  placeholder="Select a Vercel project"
                  dropdownIcon
                  text={selectedVercelProject?.name || "Select a project"}
                >
                  {availableProjects.map((project) => (
                    <SelectItem key={project.id} value={project.id}>
                      {project.name}
                    </SelectItem>
                  ))}
                </Select>
              )}

              {projectSelectionError && <FormError>{projectSelectionError}</FormError>}

              <Hint>
                Once connected, your{" "}
                <code className="text-xs rounded bg-background-raised px-1 py-0.5 text-text-bright">
                  TRIGGER_SECRET_KEY
                </code>{" "}
                will be automatically synced to Vercel for each environment.
              </Hint>

              <FormButtons
                confirmButton={
                  <div className="flex items-center gap-2">
                    {vercelManageAccessUrl && !origin && (
                      <LinkButton
                        to={vercelManageAccessUrl}
                        variant="tertiary/medium"
                        target="_self"
                      >
                        Manage access
                      </LinkButton>
                    )}
                    <Button
                      variant="primary/medium"
                      onClick={handleProjectSelection}
                      disabled={!selectedVercelProject || fetcher.state !== "idle"}
                      LeadingIcon={fetcher.state !== "idle" ? SpinnerWhite : undefined}
                    >
                      {fetcher.state !== "idle" ? "Connecting..." : "Connect Project"}
                    </Button>
                  </div>
                }
                cancelButton={
                  <Button variant="tertiary/medium" onClick={handleSkipOnboarding}>
                    Cancel
                  </Button>
                }
              />
            </div>
          )}

          {showEnvMapping && (
            <div className="flex flex-col gap-4">
              <Header3>Map Vercel Environment to Staging</Header3>
              <Paragraph className="text-sm">
                Select which custom Vercel environment should map to Trigger.dev's Staging
                environment. Production and Preview environments are mapped automatically. If you
                skip this step, the{" "}
                <code className="rounded bg-background-raised px-1 py-0.5 text-text-bright">
                  TRIGGER_SECRET_KEY
                </code>{" "}
                will not be installed for the staging environment in Vercel. You can configure this
                later in project settings.
              </Paragraph>

              <Select
                value={vercelStagingEnvironment?.environmentId || ""}
                setValue={(value) => {
                  if (!Array.isArray(value)) {
                    const env = customEnvironments.find((e) => e.id === value);
                    setVercelStagingEnvironment(
                      env ? { environmentId: env.id, displayName: env.slug } : null
                    );
                  }
                }}
                items={customEnvironments}
                variant="tertiary/medium"
                placeholder="Select environment"
                dropdownIcon
                text={vercelStagingEnvironment?.displayName || "Select environment"}
              >
                {customEnvironments.map((env) => (
                  <SelectItem key={env.id} value={env.id}>
                    {env.slug}
                  </SelectItem>
                ))}
              </Select>

              <Paragraph className="text-xs text-text-dimmed">
                Make sure the staging branch in your Vercel project's Git settings matches the
                staging branch configured in your GitHub integration.
              </Paragraph>

              <div className="flex items-center justify-between gap-2">
                <Button variant="tertiary/medium" onClick={handleSkipOnboarding}>
                  Cancel
                </Button>
                <div className="flex items-center gap-2">
                  {!fromMarketplaceContext && (
                    <Button variant="tertiary/medium" onClick={handleSkipEnvMapping}>
                      Skip
                    </Button>
                  )}
                  <Button
                    variant="primary/medium"
                    onClick={handleUpdateEnvMapping}
                    disabled={envMappingFetcher.state !== "idle"}
                    LeadingIcon={envMappingFetcher.state !== "idle" ? SpinnerWhite : undefined}
                  >
                    Next
                  </Button>
                </div>
              </div>
            </div>
          )}

          {showEnvVarSync && (
            <div className="flex flex-col gap-4">
              <div className="flex flex-col gap-1">
                <Header3>Pull Environment Variables</Header3>
                <Paragraph className="text-sm">
                  Choose which environment variables to import from Vercel. This runs as a one time
                  pull to prefill your project with the variables it needs. You’ll be able to pull
                  again later, or enable automatic syncing before each build if you prefer. If you
                  are using Supabase or Neon branching,{" "}
                  <TextLink
                    href="https://trigger.dev/docs/vercel-integration#supabase-and-neon-database-branching"
                    target="_blank"
                    rel="noopener noreferrer"
                  >
                    read the docs
                  </TextLink>{" "}
                  for the recommended setup.
                </Paragraph>
              </div>
              <div className="flex items-center justify-between rounded border bg-background-bright p-3">
                <div>
                  <Label>Pull all environment variables now</Label>
                  <Hint>Select all variables to pull from Vercel.</Hint>
                </div>
                <Switch
                  variant="small"
                  checked={enabledEnvVars.length === syncableEnvVars.length}
                  onCheckedChange={(checked) => handleToggleAllEnvVars(checked, syncableEnvVars)}
                />
              </div>

              {syncableEnvVars.length > 0 && (
                <div className="rounded border">
                  <button
                    type="button"
                    className="flex w-full items-center justify-between p-3 text-left"
                    onClick={() => setExpandedEnvVars(!expandedEnvVars)}
                  >
                    <span className="text-sm text-text-dimmed">
                      {enabledEnvVars.length} of {syncableEnvVars.length} variables will be pulled
                    </span>
                    {expandedEnvVars ? (
                      <ChevronUpIcon className="size-4" />
                    ) : (
                      <ChevronDownIcon className="size-4" />
                    )}
                  </button>

                  {expandedEnvVars && (
                    <div className="max-h-64 overflow-y-auto border-t">
                      {syncableEnvVars.map((envVar) => (
                        <div
                          key={envVar.id}
                          className="flex items-center justify-between gap-2 border-b px-3 py-2 last:border-b-0"
                        >
                          <div className="flex min-w-0 flex-1 flex-col gap-0.5">
                            {existingVars[envVar.key] ? (
                              <TooltipProvider>
                                <Tooltip>
                                  <TooltipTrigger asChild>
                                    <div className="min-w-0 max-w-full cursor-default text-left truncate font-mono text-xs underline decoration-yellow-500 decoration-dotted underline-offset-2">
                                      {envVar.key}
                                    </div>
                                  </TooltipTrigger>
                                  <TooltipContent className="flex items-center gap-1 text-xs">
                                    {`This variable is going to be replaced in: ${existingVars[
                                      envVar.key
                                    ].environments.join(", ")}`}
                                  </TooltipContent>
                                </Tooltip>
                              </TooltipProvider>
                            ) : (
                              <span className="truncate font-mono text-xs">{envVar.key}</span>
                            )}
                            {envVar.target && envVar.target.length > 0 && (
                              <span className="text-xs text-text-dimmed">
                                {formatVercelTargets(envVar.target)}
                                {envVar.isShared && " · Shared"}
                              </span>
                            )}
                          </div>
                          <Switch
                            variant="small"
                            checked={shouldSyncEnvVarForAnyEnvironment(
                              syncEnvVarsMapping,
                              envVar.key
                            )}
                            onCheckedChange={(checked) => handleToggleEnvVar(envVar.key, checked)}
                          />
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              )}

              {secretEnvVars.length > 0 && (
                <div className="rounded border">
                  <button
                    type="button"
                    className="flex w-full items-center justify-between p-3 text-left"
                    onClick={() => setExpandedSecretEnvVars(!expandedSecretEnvVars)}
                  >
                    <span className="text-sm text-text-dimmed">
                      {secretEnvVars.length} secret{" "}
                      {secretEnvVars.length === 1 ? "variable" : "variables"} (cannot be pulled)
                    </span>
                    {expandedSecretEnvVars ? (
                      <ChevronUpIcon className="size-4" />
                    ) : (
                      <ChevronDownIcon className="size-4" />
                    )}
                  </button>

                  {expandedSecretEnvVars && (
                    <div className="max-h-64 overflow-y-auto border-t">
                      {secretEnvVars.map((envVar) => (
                        <div
                          key={envVar.id}
                          className="flex items-center justify-between gap-2 border-b px-3 py-2 last:border-b-0"
                        >
                          <div className="flex min-w-0 flex-1 flex-col gap-0.5">
                            <span className="truncate font-mono text-xs">{envVar.key}</span>
                            {envVar.target && envVar.target.length > 0 && (
                              <span className="text-xs text-text-dimmed">
                                {formatVercelTargets(envVar.target)}
                                {envVar.isShared && " · Shared"}
                              </span>
                            )}
                          </div>
                          <span className="shrink-0 text-xs text-amber-400">Secret</span>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              )}

              {overlappingEnvVarsCount > 0 && enabledEnvVars.length > 0 && (
                <div className="flex items-center gap-2">
                  <ExclamationTriangleIcon className="h-4 w-4 text-amber-500" />
                  <span className="text-xs text-text-dimmed">
                    {overlappingEnvVarsCount} env vars are going to be updated (marked with{" "}
                    <span className="underline decoration-yellow-500 decoration-dotted underline-offset-2">
                      underline
                    </span>
                    )
                  </span>
                </div>
              )}

              <FormButtons
                confirmButton={
                  <Button
                    variant="primary/medium"
                    onClick={() => {
                      trackOnboarding("vercel onboarding env vars configured", {
                        env_vars_enabled: enabledEnvVars.length,
                        env_vars_total: syncableEnvVars.length,
                      });
                      if (fromMarketplaceContext) {
                        handleBuildSettingsNext();
                      } else {
                        setState("build-settings");
                      }
                    }}
                    disabled={
                      fromMarketplaceContext &&
                      (completeOnboardingFetcher.state !== "idle" || isRedirecting)
                    }
                    LeadingIcon={
                      fromMarketplaceContext &&
                      (completeOnboardingFetcher.state !== "idle" || isRedirecting)
                        ? SpinnerWhite
                        : undefined
                    }
                  >
                    {fromMarketplaceContext
                      ? isGitHubConnectedForOnboarding
                        ? "Finish"
                        : "Next"
                      : "Next"}
                  </Button>
                }
                cancelButton={
                  hasCustomEnvs && !fromMarketplaceContext ? (
                    <Button variant="tertiary/medium" onClick={() => setState("env-mapping")}>
                      Back
                    </Button>
                  ) : (
                    <Button
                      variant="tertiary/medium"
                      onClick={handleSkipOnboarding}
                      disabled={fetcher.state !== "idle"}
                    >
                      Cancel
                    </Button>
                  )
                }
              />
            </div>
          )}

          {showBuildSettings && (
            <div className="flex flex-col gap-4">
              <Header3>Build Settings</Header3>
              <Paragraph className="text-sm">
                Configure how environment variables are pulled during builds and atomic deployments.
              </Paragraph>

              <BuildSettingsFields
                availableEnvSlugs={availableEnvSlugsForOnboardingBuildSettings}
                pullEnvVarsBeforeBuild={pullEnvVarsBeforeBuild}
                onPullEnvVarsChange={setPullEnvVarsBeforeBuild}
                discoverEnvVars={discoverEnvVars}
                onDiscoverEnvVarsChange={setDiscoverEnvVars}
                atomicBuilds={atomicBuilds}
                onAtomicBuildsChange={setAtomicBuilds}
                disabledEnvSlugs={disabledEnvSlugsForBuildSettings}
              />

              <FormButtons
                confirmButton={
                  <Button
                    variant="primary/medium"
                    onClick={handleBuildSettingsNext}
                    disabled={completeOnboardingFetcher.state !== "idle" || isRedirecting}
                    LeadingIcon={
                      completeOnboardingFetcher.state !== "idle" || isRedirecting
                        ? SpinnerWhite
                        : undefined
                    }
                  >
                    {isGitHubConnectedForOnboarding ? "Finish" : "Next"}
                  </Button>
                }
                cancelButton={
                  <Button variant="tertiary/medium" onClick={() => setState("env-var-sync")}>
                    Back
                  </Button>
                }
              />
            </div>
          )}

          {showGitHubConnection && (
            <div className="flex flex-col gap-4">
              <Header3>Connect GitHub Repository</Header3>
              <Paragraph className="text-sm">
                To fully integrate with Vercel, Trigger.dev needs access to your source code. This
                allows automatic deployments and build synchronization.
              </Paragraph>

              <Callout variant="info">
                <p className="text-xs">
                  Connecting your GitHub repository enables Trigger.dev to read your source code and
                  automatically create deployments when you push changes to Vercel.
                </p>
              </Callout>

              {(() => {
                const baseSettingsPath = v3ProjectSettingsIntegrationsPath(
                  { slug: organizationSlug },
                  { slug: projectSlug },
                  { slug: environmentSlug }
                );
                const redirectParams = new URLSearchParams();
                redirectParams.set("vercelOnboarding", "true");
                if (fromMarketplaceContext) {
                  redirectParams.set("origin", "marketplace");
                }
                if (nextUrl) {
                  redirectParams.set("next", nextUrl);
                }
                const redirectUrlWithContext = `${baseSettingsPath}?${redirectParams.toString()}`;
                const nextDirectRedirect = nextUrl ? safeRedirectUrl(nextUrl) : null;

                return gitHubAppInstallations.length === 0 ? (
                  <div className="flex flex-col gap-3">
                    <LinkButton
                      to={githubAppInstallPath(
                        organizationSlug,
                        `${redirectUrlWithContext}&openGithubRepoModal=1`
                      )}
                      variant="secondary/medium"
                      LeadingIcon={OctoKitty}
                      onClick={() =>
                        trackOnboarding("vercel onboarding github app install clicked")
                      }
                    >
                      Install GitHub app
                    </LinkButton>
                  </div>
                ) : (
                  <div className="flex flex-col gap-3">
                    <div className="flex items-center gap-3">
                      <ConnectGitHubRepoModal
                        gitHubAppInstallations={gitHubAppInstallations}
                        organizationSlug={organizationSlug}
                        projectSlug={projectSlug}
                        environmentSlug={environmentSlug}
                        redirectUrl={
                          nextDirectRedirect ??
                          (fromMarketplaceContext ? redirectUrlWithContext : baseSettingsPath)
                        }
                        preventDismiss={fromMarketplaceContext}
                      />
                      <span className="flex items-center gap-1 text-xs text-text-dimmed">
                        <CheckCircleIcon className="size-4 text-success" /> GitHub app is installed
                      </span>
                    </div>
                  </div>
                );
              })()}

              <FormButtons
                confirmButton={
                  isGitHubConnectedForOnboarding && fromMarketplaceContext && nextUrl ? (
                    <Button
                      variant="primary/medium"
                      onClick={() => {
                        trackOnboarding("vercel onboarding github completed");
                        setState("completed");
                        const validUrl = safeRedirectUrl(nextUrl);
                        if (validUrl) {
                          window.location.href = validUrl;
                        }
                      }}
                    >
                      Complete
                    </Button>
                  ) : !fromMarketplaceContext ? (
                    <Button
                      variant="tertiary/medium"
                      onClick={() => {
                        trackOnboarding("vercel onboarding github skipped");
                        setState("completed");
                      }}
                    >
                      Skip for now
                    </Button>
                  ) : undefined
                }
              />
            </div>
          )}
        </div>
      </DialogContent>
    </Dialog>
  );
}
