import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/**
 * IntroScreen (v2) — Clean, minimal welcome with framework detection.
 *
 * Four states:
 *   0. Checkpoint restored: resume / start fresh / cancel picker
 *   1. Detecting: spinner while bin.ts runs detection
 *   2. Detection failed: auto-selects Generic, then continue/cancel
 *   3. Detection succeeded: show result, then continue/cancel
 *
 * Shows the AmplitudeTextLogo when terminal is wide/tall enough (>=75x20).
 * Calls store.concludeIntro() to advance past this screen.
 */
import { Box, Text } from 'ink';
import { useState, useEffect, useMemo } from 'react';
import { useWizardStore } from '../hooks/useWizardStore.js';
import { useScreenInput } from '../hooks/useScreenInput.js';
import { OutroKind } from '../session-constants.js';
import { Integration } from '../../../lib/constants.js';
import { AuthOnboardingPath } from '../../../lib/wizard-session.js';
import { clearCheckpoint } from '../../../lib/session-checkpoint.js';
import { analyzeWorkspace } from '../../../lib/workspace-analysis.js';
import { ampliConfigExists } from '../../../lib/ampli-config.js';
import { PickerMenu } from '../primitives/index.js';
import { PathInput } from '../components/PathInput.js';
import { Colors, Icons } from '../styles.js';
import { BrailleSpinner } from '../components/BrailleSpinner.js';
import { AmplitudeTextLogo } from '../components/AmplitudeTextLogo.js';
import { useStdoutDimensions } from '../hooks/useStdoutDimensions.js';
import { useScreenHints } from '../hooks/useScreenHints.js';
import { analytics } from '../../../utils/analytics.js';
import { logToFile } from '../../../utils/debug.js';
import { readPreviousRunSummary, humanizeAge, } from '../utils/welcome-back-context.js';
const INTRO_HINTS = Object.freeze([
    { key: '↑↓', label: 'Navigate' },
    { key: 'Enter', label: 'Select' },
]);
const LOGO_MIN_COLS = 75;
const LOGO_MIN_ROWS = 26;
const COMPACT_COLS = 85;
const COMPACT_ROWS = 24;
/**
 * Suffix shown after the framework name. Exported for unit tests.
 * - '' when the user manually picked the framework, or when we fell back
 *   (the main label already reads "none detected" in that case)
 * - ' (detected)' when auto-detection found a real framework
 */
export function getFrameworkLabelSuffix({ manuallySelected, autoFallback, }) {
    if (manuallySelected || autoFallback)
        return '';
    return ' (detected)';
}
export const IntroScreen = ({ store }) => {
    useWizardStore(store);
    useScreenHints(INTRO_HINTS);
    const [cols, rows] = useStdoutDimensions();
    const [pickingFramework, setPickingFramework] = useState(false);
    const [manuallySelected, setManuallySelected] = useState(false);
    // True while the user is typing a new install directory in the inline
    // PathInput. Suppresses the picker + spinner so the input gets the
    // full screen, then flips back to false when the user hits Enter or
    // Esc. The actual re-detection runs through the store action — this
    // local flag is purely UI-state.
    const [changingDirectory, setChangingDirectory] = useState(false);
    const [showResume, setShowResume] = useState(() => store.session._restoredFromCheckpoint);
    const { session } = store;
    // Workspace analysis runs once per installDir change. The checks are
    // sync filesystem reads — fine to do during render, and memoizing keeps
    // them off the hot path on every re-render.
    const workspace = useMemo(() => analyzeWorkspace(session.installDir), [session.installDir]);
    // "Welcome back" gate — true when the user is signed in AND this
    // directory has been instrumented before (ampli.json present). First-
    // time users with no email or no prior project still see the marketing
    // tagline below; the personalized panel only fires when we have real
    // signal that this isn't a fresh install. The disk reads are fast
    // (single stat) and re-run only when installDir / userEmail change.
    const welcomeBack = useMemo(() => {
        if (!session.userEmail)
            return null;
        if (!ampliConfigExists(session.installDir))
            return null;
        const previous = readPreviousRunSummary(session.installDir);
        return {
            email: session.userEmail,
            eventCount: previous.eventCount,
            lastRunAt: previous.lastRunAt,
        };
    }, [session.userEmail, session.installDir]);
    const config = session.frameworkConfig;
    const frameworkLabel = session.detectedFrameworkLabel ?? config?.metadata.name;
    const detecting = !session.detectionComplete;
    const needsFrameworkPick = session.detectionComplete && !session.frameworkConfig;
    // Derive fallback state from session so it survives component remount
    // (e.g. ScreenErrorBoundary retries). Generic is never reachable via
    // the manual picker — it's excluded from PICKER_ORDER — so integration
    // === generic uniquely identifies the auto-fallback path.
    const autoFallback = session.integration === Integration.generic;
    // Hide logo when framework picker is open — the long list overlaps in Ink.
    const pickerVisible = pickingFramework || (session.menu && needsFrameworkPick);
    const showLogo = cols >= LOGO_MIN_COLS && rows >= LOGO_MIN_ROWS && !pickerVisible;
    const compact = rows < COMPACT_ROWS || cols < COMPACT_COLS;
    const narrow = cols < COMPACT_COLS;
    // When detection fails and the user hasn't explicitly opened the picker,
    // auto-select the generic integration so the wizard can proceed.
    // NOTE: we deliberately do NOT call setDetectedFramework here — Generic is a
    // fallback, not a detection. The render derives its label from the config.
    useEffect(() => {
        if (needsFrameworkPick && !session.menu && !showResume) {
            void import('../../../lib/registry.js').then(({ FRAMEWORK_REGISTRY }) => {
                const genericConfig = FRAMEWORK_REGISTRY[Integration.generic];
                store.setFrameworkConfig(Integration.generic, genericConfig);
                logToFile('[intro] no framework matched — falling back to Generic');
            });
        }
    }, [needsFrameworkPick, session.menu, showResume]);
    const showContinue = session.frameworkConfig !== null && !detecting && !pickingFramework;
    // ── Resume-from-checkpoint prompt ─────────────────────────────────
    if (showResume) {
        const orgLabel = session.selectedOrgName ?? session.selectedProjectName ?? null;
        return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "flex-start", paddingTop: 2, children: [showLogo && _jsx(AmplitudeTextLogo, {}), _jsx(Box, { flexDirection: "column", alignItems: "center", marginBottom: 1, children: _jsx(Text, { bold: true, color: Colors.heading, children: "Amplitude Wizard" }) }), _jsxs(Box, { flexDirection: "column", alignItems: "flex-start", children: [_jsx(Text, { color: Colors.body, children: "A previous session was interrupted." }), frameworkLabel && (_jsxs(Text, { children: [_jsx(Text, { color: Colors.body, children: " Framework: " }), _jsx(Text, { color: Colors.secondary, children: frameworkLabel })] })), orgLabel && (_jsxs(Text, { children: [_jsx(Text, { color: Colors.body, children: " Organization: " }), _jsx(Text, { color: Colors.secondary, children: orgLabel })] })), _jsx(Box, { marginTop: 1, children: _jsx(PickerMenu, { options: [
                                    { label: 'Resume where you left off', value: 'resume' },
                                    { label: 'Start fresh', value: 'fresh' },
                                    { label: 'Cancel', value: 'cancel', hint: 'exit wizard' },
                                ], onSelect: (value) => {
                                    const choice = Array.isArray(value) ? value[0] : value;
                                    analytics.wizardCapture('checkpoint resume action', {
                                        action: choice,
                                        integration: session.integration,
                                        'detected framework': session.detectedFrameworkLabel,
                                    });
                                    if (choice === 'resume') {
                                        store.concludeIntro();
                                    }
                                    else if (choice === 'fresh') {
                                        // Clear checkpoint and reset restored flag so normal
                                        // flow takes over. Route through the explicit store
                                        // action so all per-key listeners fire (the previous
                                        // direct `store.session = {...}` assignment worked for
                                        // version-based subscribers but silently bypassed
                                        // nanostores' per-key change events).
                                        clearCheckpoint(store.session.installDir, 'manual');
                                        store.resetForFreshStart();
                                        setShowResume(false);
                                    }
                                    else {
                                        store.setOutroData({
                                            kind: OutroKind.Cancel,
                                            message: 'Setup cancelled.',
                                        });
                                    }
                                } }) })] })] }));
    }
    return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, alignItems: "center", justifyContent: "flex-start", paddingTop: compact ? 0 : 1, children: [showLogo && _jsx(AmplitudeTextLogo, {}), _jsx(Box, { flexDirection: "column", alignItems: "center", marginBottom: compact ? 0 : 1, children: welcomeBack ? (_jsx(WelcomeBackPanel, { email: welcomeBack.email, projectName: session.selectedProjectName, region: session.region, eventCount: welcomeBack.eventCount, lastRunAt: welcomeBack.lastRunAt, compact: compact })) : (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, color: Colors.heading, children: "Amplitude Wizard" }), !compact && (_jsx(Text, { color: Colors.muted, children: "AI-powered analytics setup in minutes" }))] })) }), changingDirectory && (_jsx(Box, { marginY: 1, flexDirection: "column", alignItems: "flex-start", children: _jsx(PathInput, { initialValue: session.installDir, onSubmit: (absolutePath) => {
                        analytics.wizardCapture('install dir change submitted', {
                            'detected framework': session.detectedFrameworkLabel,
                        });
                        setChangingDirectory(false);
                        // Reset framework selection state so the spinner re-runs
                        // cleanly against the new tree.
                        setManuallySelected(false);
                        store.changeInstallDir(absolutePath);
                    }, onCancel: () => {
                        analytics.wizardCapture('install dir change cancelled', {});
                        setChangingDirectory(false);
                    } }) })), !changingDirectory && !pickingFramework && (_jsx(TargetSummary, { displayPath: workspace.displayPath, frameworkLabel: frameworkLabel ?? null, frameworkGlyph: config?.metadata.glyph, frameworkGlyphColor: config?.metadata.glyphColor, frameworkBeta: config?.metadata.beta ?? false, frameworkSuffix: !detecting
                    ? getFrameworkLabelSuffix({ manuallySelected, autoFallback })
                    : '', region: session.region, hideRegionRow: Boolean(welcomeBack && !compact && session.region), detecting: detecting })), detecting && !changingDirectory && (_jsxs(Box, { marginY: 1, gap: 1, children: [_jsx(BrailleSpinner, {}), _jsxs(Text, { color: Colors.secondary, children: ["Scanning ", workspace.displayPath, Icons.ellipsis] })] })), !detecting && !changingDirectory && !pickingFramework && (_jsx(WorkspaceWarnings, { hasManifest: workspace.hasManifest, isMonorepo: workspace.isMonorepo, workspaceGlobs: workspace.workspaceGlobs })), config?.metadata.preRunNotice &&
                !detecting &&
                !changingDirectory &&
                !pickingFramework && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: Colors.warning, children: config.metadata.preRunNotice }) })), autoFallback &&
                !detecting &&
                !changingDirectory &&
                !pickingFramework && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: Colors.muted, children: "No framework detected. Continue with the generic guide or pick one below." }) })), (pickingFramework || (session.menu && needsFrameworkPick)) &&
                !changingDirectory && (_jsx(FrameworkPicker, { store: store, onComplete: (selected) => {
                    setPickingFramework(false);
                    if (selected) {
                        setManuallySelected(true);
                    }
                } })), showContinue && !changingDirectory && (_jsx(Box, { marginTop: compact ? 0 : 1, children: _jsx(PickerMenu, { message: session.userEmail
                        ? narrow
                            ? 'Signed in — continue'
                            : `You're signed in as ${session.userEmail}. Continue to workspace setup.`
                        : narrow
                            ? 'Sign in or create account'
                            : 'Sign in to an existing Amplitude account, or create a new one', options: [
                        {
                            label: session.userEmail
                                ? narrow
                                    ? 'Continue'
                                    : 'Continue — workspace setup'
                                : narrow
                                    ? 'Continue — sign in'
                                    : 'Continue — sign in to Amplitude',
                            value: 'continue_signin',
                            ...(!narrow
                                ? {
                                    hint: session.userEmail
                                        ? 'same login'
                                        : 'existing account',
                                }
                                : {}),
                        },
                        ...(!session.userEmail
                            ? [
                                {
                                    label: narrow
                                        ? 'Continue — new account'
                                        : 'Continue — create a new account',
                                    value: 'continue_create',
                                    ...(!narrow ? { hint: 'new organization' } : {}),
                                },
                            ]
                            : []),
                        {
                            label: 'Change framework',
                            value: 'framework',
                            ...(narrow ? {} : { hint: 'pick manually' }),
                        },
                        ...(session.region
                            ? [
                                {
                                    label: 'Change region',
                                    value: 'region',
                                    ...(narrow ? {} : { hint: 'pick US or EU' }),
                                },
                            ]
                            : []),
                        {
                            label: 'Change directory',
                            value: 'directory',
                            ...(narrow ? {} : { hint: 'point at another project' }),
                        },
                        {
                            label: 'Cancel',
                            value: 'cancel',
                            ...(narrow ? {} : { hint: 'exit wizard' }),
                        },
                    ], onSelect: (value) => {
                        const choice = Array.isArray(value) ? value[0] : value;
                        const analyticsBase = {
                            integration: session.integration,
                            'detected framework': session.detectedFrameworkLabel,
                            'has manifest': workspace.hasManifest,
                            'is monorepo': workspace.isMonorepo,
                        };
                        if (choice === 'continue_signin' ||
                            choice === 'continue_create') {
                            const path = choice === 'continue_create'
                                ? AuthOnboardingPath.CreateAccount
                                : AuthOnboardingPath.SignIn;
                            store.setAuthOnboardingPath(path);
                            analytics.wizardCapture('intro action', {
                                ...analyticsBase,
                                action: 'continue',
                                'auth onboarding path': path,
                            });
                            store.concludeIntro();
                            return;
                        }
                        analytics.wizardCapture('intro action', {
                            ...analyticsBase,
                            action: choice,
                        });
                        if (choice === 'cancel') {
                            store.setOutroData({
                                kind: OutroKind.Cancel,
                                message: 'Setup cancelled.',
                            });
                        }
                        else if (choice === 'directory') {
                            setChangingDirectory(true);
                        }
                        else if (choice === 'framework') {
                            setPickingFramework(true);
                        }
                        else if (choice === 'region') {
                            // Force RegionSelect to appear after Continue. Must
                            // conclude the intro so the main flow advances past it
                            // into the (now re-shown) RegionSelect screen.
                            store.setRegionForced();
                            store.concludeIntro();
                        }
                    } }) }))] }));
};
const WelcomeBackPanel = ({ email, projectName, region, eventCount, lastRunAt, compact, }) => {
    // Project + region read together — neither alone tells the user which
    // Amplitude environment we're about to write to. If we have only the
    // region (no project picked yet) we still show it on its own line so
    // the user can spot a wrong /region setting before pressing Continue.
    const projectLine = projectName
        ? region
            ? `${projectName} · ${region.toUpperCase()}`
            : projectName
        : region
            ? region.toUpperCase()
            : null;
    // Events line: only show when we know something concrete. "0 events
    // instrumented" by itself is misleading — it usually means the events
    // file just hasn't been written yet, not that the user did zero work.
    const eventsLine = eventCount > 0 && lastRunAt
        ? `${eventCount} event${eventCount === 1 ? '' : 's'} instrumented · last run ${humanizeAge(lastRunAt)}`
        : null;
    return (_jsxs(_Fragment, { children: [_jsxs(Text, { bold: true, color: Colors.heading, children: ["Welcome back, ", _jsx(Text, { color: Colors.accent, children: email })] }), !compact && projectLine && (_jsx(Text, { color: Colors.secondary, children: projectLine })), !compact && eventsLine && _jsx(Text, { color: Colors.muted, children: eventsLine })] }));
};
/**
 * Labels-first summary of the current target. Renders a compact 2-3
 * line block:
 *
 *   Target      ~/projects/my-app
 *   Framework   ▲  Next.js  (detected)
 *   Region      US
 *
 * Always shows the Target line — even during detection — so the user
 * can spot a wrong directory before the wizard finishes scanning it.
 * Framework / Region only appear once they're known. We keep the label
 * column at a fixed width so values align on a 9-character grid.
 */
const LABEL_WIDTH = 11;
function padLabel(label) {
    if (label.length >= LABEL_WIDTH)
        return label;
    return label + ' '.repeat(LABEL_WIDTH - label.length);
}
const TargetSummary = ({ displayPath, frameworkLabel, frameworkGlyph, frameworkGlyphColor, frameworkBeta, frameworkSuffix, region, hideRegionRow = false, detecting, }) => {
    return (_jsxs(Box, { flexDirection: "column", alignItems: "flex-start", children: [_jsxs(Box, { children: [_jsx(Text, { color: Colors.muted, children: padLabel('Target') }), _jsx(Text, { color: Colors.heading, children: displayPath })] }), !detecting && frameworkLabel && (_jsxs(Box, { children: [_jsx(Text, { color: Colors.muted, children: padLabel('Framework') }), frameworkGlyph && (_jsxs(Text, { color: frameworkGlyphColor, children: [frameworkGlyph, " "] })), _jsxs(Text, { color: Colors.body, children: [frameworkLabel, frameworkSuffix, frameworkBeta && ' [BETA]'] })] })), region && !hideRegionRow && (_jsxs(Box, { children: [_jsx(Text, { color: Colors.muted, children: padLabel('Region') }), _jsx(Text, { color: Colors.body, children: region.toUpperCase() })] }))] }));
};
const WorkspaceWarnings = ({ hasManifest, isMonorepo, workspaceGlobs, }) => {
    const showAny = !hasManifest || isMonorepo;
    if (!showAny)
        return null;
    return (_jsxs(Box, { marginTop: 1, flexDirection: "column", alignItems: "flex-start", children: [!hasManifest && (_jsxs(Text, { color: Colors.warning, children: [Icons.warning, " No project manifest found here. This may not be the project you meant."] })), isMonorepo && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: Colors.warning, children: [Icons.warning, " This looks like a monorepo root. Pick a workspace instead of instrumenting the whole tree."] }), workspaceGlobs.length > 0 && (_jsxs(Text, { color: Colors.muted, children: [' ', "Workspaces: ", workspaceGlobs.slice(0, 3).join(', '), workspaceGlobs.length > 3 ? ', …' : ''] }))] }))] }));
};
/**
 * Popularity-ordered list for the manual framework picker.
 * Excludes `generic` — the wizard auto-selects it when detection fails.
 *
 * Ordering rationale: put JavaScript (Web) at the top as the safest default
 * for users who aren't sure (it covers most React/Vanilla/SPA projects).
 * Then group web → mobile → backend → games so the list scans cleanly.
 * Number-key shortcuts [1]-[9],[0] map to the first ten entries.
 */
const PICKER_ORDER = [
    Integration.javascript_web,
    Integration.nextjs,
    Integration.reactRouter,
    Integration.vue,
    Integration.reactNative,
    Integration.javascriptNode,
    Integration.python,
    Integration.django,
    Integration.flask,
    Integration.fastapi,
    Integration.swift,
    Integration.android,
    Integration.flutter,
    Integration.go,
    Integration.java,
    Integration.unity,
    Integration.unreal,
];
/** Framework picker shown when auto-detection fails. */
const FrameworkPicker = ({ store, onComplete, }) => {
    const [options, setOptions] = useState([]);
    // Esc exits the picker without changing the selection.
    useScreenInput((_input, key) => {
        if (key.escape)
            onComplete?.(false);
    });
    useEffect(() => {
        void import('../../../lib/registry.js').then(({ FRAMEWORK_REGISTRY }) => {
            setOptions(PICKER_ORDER.map((integration) => {
                const { glyph, name } = FRAMEWORK_REGISTRY[integration].metadata;
                return {
                    label: glyph ? `${glyph}  ${name}` : name,
                    value: integration,
                };
            }));
        });
    }, []);
    if (options.length === 0)
        return null;
    return (_jsx(PickerMenu, { centered: true, message: "Select your framework (Esc to go back)", options: options, onSelect: (value) => {
            const integration = Array.isArray(value) ? value[0] : value;
            analytics.wizardCapture('framework manually selected', { integration });
            void import('../../../lib/registry.js').then(({ FRAMEWORK_REGISTRY }) => {
                const config = FRAMEWORK_REGISTRY[integration];
                store.setFrameworkConfig(integration, config);
                store.setDetectedFramework(config.metadata.name);
                onComplete?.(true);
            });
        } }));
};
