/* global React, Button, Icon, OnboardingConnectForm, WorkflowOnboardingUseCase, WorkflowOnboardingReview, WorkflowOnboardingManual, apiFetch, persistOnboardingState, useWorkflowDraftFlow, trackOnboardingEvent, useApiResource, useAuth, useToast, TERMINAL_ONBOARDING_STEPS */
const {
  useEffect: useEffectTour,
  useMemo: useMemoTour,
  useRef: useRefTour,
  useState: useStateTour,
} = React;

const TOUR_ALLOWED_ROLES = new Set(['owner', 'admin']);
const TOUR_ALLOWED_STATUSES = new Set(['active', 'trialing']);
// Tour is the *post*-onboarding walkthrough. It must not fire while the user
// is still on (or before) the blocking workspace/plan steps — otherwise a
// fresh signup whose onboarding_state hasn't been normalized yet sees the
// "You're in" confetti modal flash on /dashboard before the redirect to
// /onboarding/workspace lands. 'role' is included because for paid users the
// server keeps currentStep at 'role' after role-step completion (api/onboarding/
// state.js writes an empty patch); 'monitor' remains eligible as a legacy
// resume state, but the tour now skips directly from connect to workflow.
const TOUR_ELIGIBLE_STEPS = new Set(['role', 'connect', 'monitor']);

function tourNextStep(state) {
  if (!state) return null;
  if (state.skippedConnect) return null;
  if (!state.lastServerId) return 'connect';
  if (!state.lastWorkflowId && !state.skippedWorkflow) return 'workflow';
  return null;
}

function shouldShowTour(auth) {
  const me = auth?.me;
  if (!me) return false;
  if (!TOUR_ALLOWED_ROLES.has(me.role)) return false;
  const status = me.organization?.subscriptionStatus;
  if (!TOUR_ALLOWED_STATUSES.has(status)) return false;
  const state = me.profile?.onboardingState || {};
  if (TERMINAL_ONBOARDING_STEPS.has(state.currentStep)) return false;
  if (!TOUR_ELIGIBLE_STEPS.has(state.currentStep)) return false;
  return tourNextStep(state) !== null;
}

const TOUR_STEPS = [
  { key: 'connect', label: 'Connect MCP', index: 1 },
  { key: 'workflow', label: 'First workflow', index: 2 },
];

function newWorkflowRunsPath(workflowId) {
  const params = new URLSearchParams();
  if (workflowId) params.set('workflowId', workflowId);
  params.set('status', 'running');
  return `/runs?${params.toString()}`;
}

function OnboardingTourProgress({ activeStep }) {
  const activeIndex = TOUR_STEPS.findIndex((s) => s.key === activeStep);
  return (
    <div className="onboarding-tour-progress" role="presentation">
      {TOUR_STEPS.map((step) => {
        const reached = activeIndex >= step.index - 1;
        return (
          <span
            key={step.key}
            className={`onboarding-tour-dot ${reached ? 'is-active' : ''}`}
            aria-label={`Step ${step.index}: ${step.label}`} />
        );
      })}
    </div>
  );
}

function OnboardingTourHeader({ activeStep, complete }) {
  if (complete) {
    return (
      <div className="onboarding-tour-header">
        <span className="onboarding-tour-header-label">Setup complete</span>
      </div>
    );
  }
  const step = TOUR_STEPS.find((s) => s.key === activeStep);
  const indexLabel = step ? `Step ${String(step.index).padStart(2, '0')} / 02` : 'Tour';
  const stepLabel = step?.label || '';
  return (
    <div className="onboarding-tour-header">
      <span className="onboarding-tour-header-label">{indexLabel}{stepLabel ? ` · ${stepLabel}` : ''}</span>
      <OnboardingTourProgress activeStep={activeStep} />
    </div>
  );
}

const SKIP_COPY = {
  connect: {
    title: 'Skip MCP setup?',
    body: 'Without a connected server we can’t draft workflows. With one, our agent picks the right tools, drafts a workflow, and you’re live in under a minute.',
    confirmLabel: 'Skip anyway',
    cancelLabel: 'Keep going',
  },
  workflow: {
    title: 'Skip workflow creation?',
    body: 'Workflows are agents that take action on your MCP. Our agent can suggest one for your use case in a few seconds.',
    confirmLabel: 'Skip anyway',
    cancelLabel: 'Keep going',
  },
};

function OnboardingTourSkipCard({ stepKey, busy, onConfirm, onCancel }) {
  const copy = SKIP_COPY[stepKey];
  if (!copy) return null;
  return (
    <div className="onboarding-tour-skip-card" role="alertdialog" aria-modal="true">
      <div className="onboarding-kicker">Hold on</div>
      <h2>{copy.title}</h2>
      <p>{copy.body}</p>
      <div className="onboarding-tour-skip-actions">
        <Button onClick={onCancel} disabled={busy}>{copy.cancelLabel}</Button>
        <Button variant="primary" loading={busy} loadingLabel="Skipping..." onClick={onConfirm}>
          {copy.confirmLabel}
        </Button>
      </div>
    </div>
  );
}

function useStepTelemetry(stepKey) {
  const enteredAtRef = useRefTour(Date.now());
  useEffectTour(() => {
    enteredAtRef.current = Date.now();
    trackOnboardingEvent('onboarding.step.enter', { step: stepKey });
    return () => {
      trackOnboardingEvent('onboarding.step.exit', {
        step: stepKey,
        ms_in_step: Date.now() - enteredAtRef.current,
      });
    };
  }, [stepKey]);
}

function OnboardingTourConnectStep({ onConnected, onAskSkip }) {
  useStepTelemetry('connect');
  return (
    <>
      <header className="onboarding-tour-step-head">
        <div className="onboarding-kicker">Connect</div>
        <h1>Point us at your MCP server</h1>
        <p className="muted">We'll test the connection and discover the tools you can wire up.</p>
      </header>
      <div className="onboarding-tour-step-body onboarding-tour-connect-stage">
        <OnboardingConnectForm onConnected={onConnected} onSkip={onAskSkip} />
      </div>
    </>
  );
}

function OnboardingTourWorkflowStep({ serverId, onCreated, onPrimaryActionChange = null }) {
  useStepTelemetry('workflow');
  const auth = useAuth();
  const [stage, setStage] = useStateTour('usecase'); // 'usecase' | 'review' | 'manual'
  const servers = useApiResource('/api/mcp-servers');
  const catalog = useApiResource('/api/catalog/models');
  const modelRows = catalog.data?.models || [];
  const selectedServer = (servers.data?.rows || []).find((s) => s.id === serverId) || null;
  const toast = useToast();

  // Same hook the in-app "Create new workflow" modal uses. The tour-only
  // `onCreated` callback wraps the shared lifecycle with onboarding-state
  // persistence so the tour can resume on the workflow step after a reload.
  const flow = useWorkflowDraftFlow({
    mcpServerId: serverId,
    mcpServerIds: null,
    modelRows,
    orgDefaultTargets: auth?.me?.organization?.workflowDefaultTargets,
    selectedServer,
    toast,
    onCreated: async (workflowId, name) => {
      await persistOnboardingState({ lastWorkflowId: workflowId });
      onCreated?.(workflowId, name);
    },
  });

  const {
    usecase, setUsecase,
    suggestState,
    draftState, setDraftState,
    draft, setDraft,
    manualDraft, setManualDraft,
    creating,
    testerModelIds, setTesterModelIds,
    testerTargets, setTesterTargets,
    ensureManualDraft,
    suggestUsecases, resuggestUsecases,
    loadAgentDraft,
    createFromAgent, createFromManual,
  } = flow;

  // The shared hook drives loadAgentDraft idempotently — same effect runs
  // in `WorkflowOnboardingModal` so the tour's flow stays in lockstep with
  // it (no duplicate POSTs while animating between stages).
  useEffectTour(() => {
    if (stage !== 'review') return;
    if (draftState.status === 'loading' || draftState.status === 'ready') return;
    loadAgentDraft();
  }, [stage]); // eslint-disable-line

  useEffectTour(() => {
    if (stage !== 'manual') return;
    ensureManualDraft();
  }, [stage]); // eslint-disable-line

  // The Create workflow primary action lives in the tour's sticky footer next
  // to "Skip for now" — but only the review/manual stages have something to
  // create. The use-case stage uses an inline "Continue" inside the body, so
  // we publish null for that stage.
  //
  // The handlers `createFromAgent` / `createFromManual` are recreated on every
  // render (they close over `draft`, `manualDraft`, `testerModelIds`, etc.).
  // Storing them directly in parent state via the publish effect would make
  // the effect fire on every render, set parent state, re-render this child,
  // and loop. Instead we keep the latest handler in a ref and publish a
  // stable wrapper as `onClick` — so the effect's dependency list contains
  // only primitive descriptor fields and the ref always points to the most
  // up-to-date closure when the user finally clicks Create.
  const primaryClickRef = useRefTour(/** @type {(() => void) | null} */ (null));
  primaryClickRef.current = stage === 'review'
    ? createFromAgent
    : stage === 'manual'
      ? createFromManual
      : null;

  const stablePrimaryClick = useRefTour(() => {
    const fn = primaryClickRef.current;
    if (typeof fn === 'function') fn();
  }).current;

  let primaryAction = null;
  if (stage === 'review' && draftState.status === 'ready' && draft) {
    primaryAction = {
      label: 'Create workflow',
      loading: creating,
      loadingLabel: 'Creating',
      disabled: creating || !draft.name?.trim() || !draft.testerPrompt?.trim() || testerModelIds.length === 0,
      onClick: stablePrimaryClick,
    };
  } else if (stage === 'manual' && manualDraft) {
    const canCreate = !creating
      && (manualDraft.name || '').trim().length > 0
      && (manualDraft.testerPrompt || '').trim().length > 0
      && testerModelIds.length > 0;
    primaryAction = {
      label: 'Create workflow',
      loading: creating,
      loadingLabel: 'Creating',
      disabled: !canCreate,
      onClick: stablePrimaryClick,
    };
  }

  // Effect deps only track descriptor fields that actually need to repaint
  // the footer button. `onClick` is a stable wrapper so we deliberately omit
  // it. (eslint react-hooks/exhaustive-deps would flag this, but listing the
  // unstable inner handlers would re-introduce the loop described above.)
  useEffectTour(() => {
    if (typeof onPrimaryActionChange !== 'function') return undefined;
    onPrimaryActionChange(primaryAction);
    return () => onPrimaryActionChange(null);
  }, [
    onPrimaryActionChange,
    primaryAction?.label,
    primaryAction?.disabled,
    primaryAction?.loading,
  ]);

  return (
    <>
      {stage === 'usecase' && (
        <header className="onboarding-tour-step-head">
          <div className="onboarding-kicker">Workflow</div>
          <h1>Watch an agent use your MCP end-to-end</h1>
          <p className="muted">A workflow runs an AI agent through a real user task on your MCP, then grades whether it picked the right tools, chained them correctly, and got the user to a good answer — covering the multi-step paths a single tool check can't see.</p>
        </header>
      )}
      <div className={`onboarding-tour-step-body onboarding-tour-workflow-stage onboarding-tour-workflow-stage--${stage}`}>
        {stage === 'usecase' && (
          <WorkflowOnboardingUseCase
            animDir="forward"
            server={selectedServer}
            suggestState={suggestState}
            usecase={usecase}
            setUsecase={setUsecase}
            onSuggest={suggestUsecases}
            onResuggest={resuggestUsecases}
            onBack={() => {}}
            onSwitchManual={() => setStage('manual')}
            onContinue={() => setStage('review')}
          />
        )}
        {stage === 'review' && (
          <WorkflowOnboardingReview
            animDir="forward"
            draftState={draftState}
            draft={draft}
            setDraft={setDraft}
            testerModelIds={testerModelIds}
            testerTargets={testerTargets}
            onChangeTesterModels={setTesterModelIds}
            onChangeTesterTargets={setTesterTargets}
            models={modelRows}
            saving={creating}
            onBack={() => { setDraftState({ status: 'idle', error: '', ourSide: false, draft: null }); setStage('usecase'); }}
            onRetry={loadAgentDraft}
            onSwitchManual={() => setStage('manual')}
            onCreate={createFromAgent}
            hidePrimaryAction
          />
        )}
        {stage === 'manual' && manualDraft && (
          <WorkflowOnboardingManual
            animDir="forward"
            server={selectedServer}
            draft={manualDraft}
            setDraft={setManualDraft}
            testerModelIds={testerModelIds}
            testerTargets={testerTargets}
            onChangeTesterModels={setTesterModelIds}
            onChangeTesterTargets={setTesterTargets}
            models={modelRows}
            saving={creating}
            onBack={() => { setDraftState({ status: 'idle', error: '', ourSide: false, draft: null }); setStage('usecase'); }}
            onCreate={createFromManual}
            hidePrimaryAction
          />
        )}
      </div>
    </>
  );
}

function TourConfetti() {
  const pieces = useMemoTour(() => {
    const tones = ['brick', 'ink', 'bone', 'brick', 'brick-soft', 'brick'];
    const out = [];
    for (let i = 0; i < 32; i += 1) {
      out.push({
        i,
        left: Math.random() * 100,
        dx: (Math.random() - 0.5) * 90,
        rot: 240 + Math.random() * 720,
        delay: 240 + Math.random() * 420,
        duration: 1900 + Math.random() * 900,
        tone: tones[i % tones.length],
        w: 4 + Math.floor(Math.random() * 4),
        h: 9 + Math.floor(Math.random() * 14),
      });
    }
    return out;
  }, []);
  return (
    <div className="onboarding-tour-confetti" aria-hidden="true">
      {pieces.map((p) => (
        <span
          key={p.i}
          className={`onboarding-tour-confetti-piece is-${p.tone}`}
          style={/** @type {any} */ ({
            left: `${p.left}%`,
            width: `${p.w}px`,
            height: `${p.h}px`,
            animationDelay: `${p.delay}ms`,
            animationDuration: `${p.duration}ms`,
            '--dx': `${p.dx}px`,
            '--rot': `${p.rot}deg`,
          })}
        />
      ))}
    </div>
  );
}

function OnboardingTourWelcomeStep({ onContinue, busy }) {
  useStepTelemetry('welcome');
  return (
    <div className="onboarding-tour-welcome">
      <TourConfetti />
      <div className="onboarding-tour-welcome-copy">
        <div className="onboarding-tour-welcome-mark" aria-hidden="true">
          <img src="/frontend/assets/armature-icon.svg" alt="" />
        </div>
        <div className="onboarding-kicker onboarding-tour-welcome-kicker">Welcome</div>
        <h1>You're in.</h1>
        <p className="muted">
          Two short steps and you'll have a workflow running on your MCP server. Let's start by connecting your first server.
        </p>
        <div className="onboarding-tour-welcome-actions">
          <Button variant="primary" onClick={onContinue} disabled={busy} loading={busy} loadingLabel="Starting...">
            Connect your first MCP<Icon name="chevronRight" size={13} />
          </Button>
        </div>
      </div>
    </div>
  );
}

function OnboardingTourComplete({ workflowName, onBack, onSeeRuns }) {
  return (
    <div className="onboarding-tour-complete">
      <div className="onboarding-tour-complete-mark" aria-hidden="true"><Icon name="check" size={26} /></div>
      <div className="onboarding-kicker">Live</div>
      <h1>You're shipping.</h1>
      <p className="muted">
        {workflowName
          ? `${workflowName} is on the schedule.`
          : 'Your first workflow is ready.'}
      </p>
      <div className="onboarding-tour-complete-actions">
        <Button onClick={onBack}>Back to workflows</Button>
        {workflowName && (
          <Button variant="primary" onClick={onSeeRuns}><Icon name="list" size={13} />See runs</Button>
        )}
      </div>
    </div>
  );
}

function OnboardingTour({ navigate }) {
  const auth = useAuth();
  const toast = useToast();
  const [pendingSkip, setPendingSkip] = useStateTour(null);
  const [dismissedTourKey, setDismissedTourKey] = useStateTour('');
  const [skipBusy, setSkipBusy] = useStateTour(false);
  const [welcomeBusy, setWelcomeBusy] = useStateTour(false);
  const [completedWorkflowId, setCompletedWorkflowId] = useStateTour(null);
  const [completedWorkflowName, setCompletedWorkflowName] = useStateTour('');
  const [showComplete, setShowComplete] = useStateTour(false);
  const [workflowPrimaryAction, setWorkflowPrimaryAction] = useStateTour(null);

  const visible = useMemoTour(() => shouldShowTour(auth), [auth?.me]); // eslint-disable-line
  const persistedState = auth?.me?.profile?.onboardingState || null;
  const state = persistedState || {};
  const activeStep = useMemoTour(() => tourNextStep(state), [persistedState]); // eslint-disable-line
  const showWelcome = visible && !showComplete && !state.welcomeSeen;
  const tourKey = [
    activeStep,
    state.lastServerId || '',
    state.lastWorkflowId || '',
    state.skippedConnect ? 'skipped-connect' : '',
    state.skippedWorkflow ? 'skipped-workflow' : '',
  ].join(':');
  const dismissed = Boolean(visible && !showComplete && dismissedTourKey === tourKey);

  useEscapeToClose({
    enabled: (visible || showComplete) && !dismissed,
    disabled: skipBusy || welcomeBusy,
    onClose: () => {
      if (pendingSkip) {
        setPendingSkip(null);
        return;
      }
      if (showWelcome) {
        dismissWelcome();
        return;
      }
      setShowComplete(false);
      setDismissedTourKey(tourKey);
    },
  });

  if ((!visible && !showComplete) || dismissed) return null;

  async function dismissWelcome() {
    setWelcomeBusy(true);
    try {
      await persistOnboardingState({ welcomeSeen: true }, { throwOnError: true });
      trackOnboardingEvent('onboarding.welcome.dismiss');
      await auth.refreshMe();
    } catch (error) {
      toast.show({
        tone: 'bad',
        title: 'Could not start the tour',
        description: error?.message || 'Try again in a moment.',
      });
    } finally {
      setWelcomeBusy(false);
    }
  }

  async function handleConnected() {
    // OnboardingConnectForm does not call refreshMe internally; the host
    // refreshes so the next render advances the tour to the workflow step.
    await auth.refreshMe();
  }

  async function handleWorkflowCreated(workflowId, workflowName) {
    setCompletedWorkflowId(workflowId);
    setCompletedWorkflowName(workflowName || '');
    setShowComplete(true);
    await auth.refreshMe();
  }

  function askSkip(stepKey) {
    setPendingSkip(stepKey);
  }

  async function confirmSkip() {
    if (!pendingSkip) return;
    setSkipBusy(true);
    try {
      const patch = pendingSkip === 'connect' ? { skippedConnect: true }
        : pendingSkip === 'workflow' ? { skippedWorkflow: true }
        : null;
      if (patch) await persistOnboardingState(patch, { throwOnError: true });
      trackOnboardingEvent('onboarding.step.skip', { step: pendingSkip });
      await auth.refreshMe();
      setPendingSkip(null);
    } catch (error) {
      toast.show({
        tone: 'bad',
        title: 'Could not skip this step',
        description: error?.message || 'Try again in a moment.',
      });
    } finally {
      setSkipBusy(false);
    }
  }

  function cancelSkip() {
    setPendingSkip(null);
  }

  function backToWorkflows() {
    setShowComplete(false);
    setCompletedWorkflowId(null);
    setCompletedWorkflowName('');
    navigate?.('/workflows');
  }

  function seeRuns() {
    setShowComplete(false);
    if (completedWorkflowId) navigate?.(newWorkflowRunsPath(completedWorkflowId));
  }

  const showFooter = !showWelcome
    && !showComplete
    && activeStep === 'workflow';
  const stepPrimaryAction = activeStep === 'workflow' ? workflowPrimaryAction : null;

  return (
    <div className={`onboarding-tour-backdrop ${showWelcome ? 'is-welcome' : ''}`} role="presentation">
      <div
        className={`onboarding-tour-modal ${showWelcome ? 'is-welcome' : ''}`}
        role="dialog"
        aria-modal="true"
        aria-labelledby="onboarding-tour-title">
        {!showWelcome && <OnboardingTourHeader activeStep={activeStep} complete={showComplete} />}
        <div className="onboarding-tour-content" id="onboarding-tour-title">
          {showWelcome ? (
            <OnboardingTourWelcomeStep onContinue={dismissWelcome} busy={welcomeBusy} />
          ) : showComplete ? (
            <OnboardingTourComplete
              workflowName={completedWorkflowName}
              onBack={backToWorkflows}
              onSeeRuns={seeRuns}
            />
          ) : activeStep === 'connect' ? (
            <OnboardingTourConnectStep
              onConnected={handleConnected}
              onAskSkip={() => askSkip('connect')}
            />
          ) : activeStep === 'workflow' ? (
            <OnboardingTourWorkflowStep
              serverId={state.lastServerId}
              onCreated={handleWorkflowCreated}
              onPrimaryActionChange={setWorkflowPrimaryAction}
            />
          ) : null}
        </div>
        {showFooter && (
          <footer className="onboarding-tour-modal-foot">
            <button
              type="button"
              className="onboarding-tour-skip-link"
              onClick={() => askSkip(activeStep)}>
              Skip for now
            </button>
            {stepPrimaryAction && (
              <div className="onboarding-tour-modal-foot-primary">
                <Button
                  variant="primary"
                  disabled={stepPrimaryAction.disabled}
                  loading={stepPrimaryAction.loading}
                  loadingLabel={stepPrimaryAction.loadingLabel}
                  onClick={stepPrimaryAction.onClick}>
                  <Icon name="check" size={12} />{stepPrimaryAction.label}
                </Button>
              </div>
            )}
          </footer>
        )}
        {pendingSkip && (
          <div className="onboarding-tour-skip-overlay" role="presentation">
            <OnboardingTourSkipCard
              stepKey={pendingSkip}
              busy={skipBusy}
              onConfirm={confirmSkip}
              onCancel={cancelSkip}
            />
          </div>
        )}
      </div>
    </div>
  );
}

window.OnboardingTour = OnboardingTour;
window.tourNextStep = tourNextStep;
window.shouldShowTour = shouldShowTour;
