'use client';

import { isDesktop } from '@lobechat/const';
import { MAX_ONBOARDING_STEPS } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { memo, useCallback, useEffect, useRef } from 'react';
import { Navigate, useSearchParams } from 'react-router-dom';

import Loading from '@/components/Loading/BrandTextLoading';
import { useOnboardingAgentTemplates } from '@/hooks/useOnboardingAgentTemplates';
import OnboardingContainer from '@/routes/onboarding/_layout';
import { deriveOnboardingBranchPath } from '@/routes/onboarding/branch';
import ResponseLanguageStep from '@/routes/onboarding/features/ResponseLanguageStep';
import TelemetryStep from '@/routes/onboarding/features/TelemetryStep';
import {
  trackOnboardingStepCompleted,
  trackOnboardingStepViewed,
} from '@/services/onboardingMetrics';
import { useServerConfigStore } from '@/store/serverConfig';
import { useUserStore } from '@/store/user';
import { onboardingSelectors } from '@/store/user/selectors';
import { clearStaleOnboardingCallbackUrl, isSafeRedirectPath } from '@/utils/onboardingRedirect';

/**
 * Remap a `currentStep` persisted under the old 5-step classic flow
 * (1=Telemetry, 2=FullName, 3=Interests, 4=Language, 5=ProSettings) onto
 * the current classic flow (1=FullName, 2=Interests, 3=ProSettings,
 * 4=AgentPicker).
 *
 * Telemetry/Language are extracted into the shared prefix, so an in-progress
 * legacy user must skip those positions when resuming classic. Legacy
 * Language/ProSettings (raw >= 4) resume at the new ProSettings step
 * (MAX_ONBOARDING_STEPS - 1) — never the trailing agent-picker step.
 */
const remapLegacyClassicStep = (raw: number): number => {
  if (raw <= 2) return 1;
  if (raw === 3) return 2;
  return MAX_ONBOARDING_STEPS - 1;
};

const COMMON_STEP_TRACKING = {
  1: { flow: 'common', step: 'telemetry', stepIndex: 1 },
  2: { flow: 'common', step: 'response_language', stepIndex: 2 },
} as const;

const appendCallbackUrl = (path: string, searchParams: URLSearchParams): string => {
  const callbackUrl = searchParams.get('callbackUrl');
  if (!callbackUrl || !isSafeRedirectPath(callbackUrl)) return path;

  const params = new URLSearchParams();
  params.set('callbackUrl', callbackUrl);
  return `${path}?${params.toString()}`;
};

const CommonOnboardingPage = memo(() => {
  const isUserStateInit = useUserStore((s) => s.isUserStateInit);
  const commonStepsCompleted = useUserStore(onboardingSelectors.commonStepsCompleted);
  const enableAgentOnboarding = useServerConfigStore((s) => s.featureFlags.enableAgentOnboarding);
  const serverConfigInit = useServerConfigStore((s) => s.serverConfigInit);

  const [searchParams, setSearchParams] = useSearchParams();
  const step: 1 | 2 = searchParams.get('step') === '2' ? 2 : 1;
  const hasStepParam = searchParams.has('step');
  const viewedStepKeysRef = useRef<Set<string>>(new Set());

  useOnboardingAgentTemplates(isUserStateInit && (!commonStepsCompleted || hasStepParam));

  // One-time legacy migration: when the user lands on the shared prefix, if
  // their persisted `currentStep` was authored under the old 5-step schema,
  // remap it onto the new 3-step schema before classic ever mounts. Gated by
  // `isUserStateInit` so we don't act on an empty initial slice. Skips when
  // onboarding is already finished or unset — mid-flow legacy users only.
  const remappedRef = useRef(false);
  useEffect(() => {
    if (!isUserStateInit || remappedRef.current) return;
    const state = useUserStore.getState();
    const persisted = state.onboarding?.currentStep;
    if (persisted === undefined || state.onboarding?.finishedAt) {
      remappedRef.current = true;
      return;
    }
    const remapped = remapLegacyClassicStep(persisted);
    if (remapped !== persisted) {
      void state.setOnboardingStep(remapped);
    }
    remappedRef.current = true;
  }, [isUserStateInit]);

  // This component only mounts on top-level entries to `/onboarding` (fresh
  // signup landings and `?step` re-entries from the branch's back button), so
  // mount is the one safe point to drop a stale callback stashed by a
  // previously abandoned attempt — later search changes are internal step
  // navigations that must keep the stash.
  useEffect(() => {
    clearStaleOnboardingCallbackUrl(window.location.pathname, window.location.search);
  }, []);

  useEffect(() => {
    if (__TEST__) return;
    void import('@/routes/onboarding/agent');
    void import('@/routes/onboarding/classic');
  }, []);

  useEffect(() => {
    if (!isUserStateInit || (commonStepsCompleted && !hasStepParam)) return;

    const payload = COMMON_STEP_TRACKING[step];
    if (viewedStepKeysRef.current.has(payload.step)) return;

    viewedStepKeysRef.current.add(payload.step);
    trackOnboardingStepViewed(payload);
  }, [commonStepsCompleted, hasStepParam, isUserStateInit, step]);

  const goNextFromTelemetry = useCallback(() => {
    trackOnboardingStepCompleted(COMMON_STEP_TRACKING[1]);
    setSearchParams({ step: '2' }, { replace: true });
  }, [setSearchParams]);

  const goBackFromLanguage = useCallback(() => {
    setSearchParams({ step: '1' }, { replace: true });
  }, [setSearchParams]);

  const finishCommon = useCallback(() => {
    trackOnboardingStepCompleted(COMMON_STEP_TRACKING[2]);
    setSearchParams({}, { replace: true });
  }, [setSearchParams]);

  if (!isUserStateInit) {
    return <Loading debugId="CommonOnboarding/userState" />;
  }

  // With the prefix complete, a bare `/onboarding` resumes the branch — but an
  // explicit `?step` (FullNameStep's back button) re-enters the shared prefix.
  if (commonStepsCompleted && !hasStepParam) {
    if (!serverConfigInit) {
      return <Loading debugId="CommonOnboarding/serverConfig" />;
    }
    const branchPath = deriveOnboardingBranchPath({
      enableAgentOnboarding: !!enableAgentOnboarding,
      isDesktop,
    });
    return <Navigate replace to={appendCallbackUrl(branchPath, searchParams)} />;
  }

  return (
    <OnboardingContainer>
      <Flexbox gap={24} style={{ maxWidth: 600, width: '100%' }}>
        {step === 1 ? (
          <TelemetryStep onNext={goNextFromTelemetry} />
        ) : (
          <ResponseLanguageStep onBack={goBackFromLanguage} onNext={finishCommon} />
        )}
      </Flexbox>
    </OnboardingContainer>
  );
});

CommonOnboardingPage.displayName = 'CommonOnboardingPage';

export default CommonOnboardingPage;
