/**
 * Tutorial system — generic engine, steps, tooltip positioning.
 */

import { activeTutorial, setActiveTutorial } from '../core/state.ts';
import { t } from '../core/i18n.ts';

interface TutorialStep {
    selector: string;
    textKey: string;
    position: string;
    global?: boolean;
    /** Optional sub-tab to switch to before highlighting this step. */
    subTab?: string;
}

interface TutorialConfig {
    steps: TutorialStep[];
    overlayId: string;
    mode: string;
    container: Element | null;
    resolveTarget: (step: TutorialStep) => Element | null;
    onClose?: (() => void) | null;
    /** Called with `step.subTab` before each step. */
    switchSubTab?: (key: string) => void;
    /** CSS selector for the element whose textContent is the current
     *  sub-tab label — shown as a breadcrumb in the tooltip header. */
    breadcrumbSelector?: string;
    /** Called with the current step before resolving the target. */
    prepare?: (step: TutorialStep) => void;
}

const calibrationTutorialSteps: TutorialStep[] = [
    { selector: '#cal-top-leds', textKey: 'calibration.tip.led_count', position: 'bottom' },
    { selector: '.corner-bottom-left', textKey: 'calibration.tip.start_corner', position: 'right' },
    { selector: '.direction-toggle', textKey: 'calibration.tip.direction', position: 'bottom' },
    { selector: '.edge-span-bar[data-edge="top"]', textKey: 'calibration.tip.span', position: 'bottom' },
    { selector: '.toggle-top', textKey: 'calibration.tip.test', position: 'top' },
    { selector: '#calibration-overlay-btn', textKey: 'calibration.tip.overlay', position: 'bottom' },
    { selector: '.preview-screen-total', textKey: 'calibration.tip.toggle_inputs', position: 'top' },
    { selector: '.preview-screen-border-width', textKey: 'calibration.tip.border_width', position: 'bottom' },
    { selector: '#cal-offset', textKey: 'calibration.tip.offset', position: 'top' },
    { selector: '#cal-skip-start', textKey: 'calibration.tip.skip_leds_start', position: 'top' },
    { selector: '#cal-skip-end', textKey: 'calibration.tip.skip_leds_end', position: 'top' }
];

const TOUR_KEY = 'tour_completed';

const gettingStartedSteps: TutorialStep[] = [
    { selector: 'header .header-title', textKey: 'tour.welcome', position: 'bottom' },
    { selector: '#tab-btn-dashboard', textKey: 'tour.dashboard', position: 'bottom' },
    { selector: '#tab-btn-automations', textKey: 'tour.automations', position: 'bottom' },
    { selector: '#tab-btn-targets', textKey: 'tour.targets', position: 'bottom' },
    { selector: '#tab-btn-streams', textKey: 'tour.sources', position: 'bottom' },
    { selector: '#tab-btn-integrations', textKey: 'tour.integrations', position: 'bottom' },
    { selector: '#tab-btn-graph', textKey: 'tour.graph', position: 'bottom' },
    { selector: 'a.header-link[href="/docs"]', textKey: 'tour.api', position: 'bottom' },
    { selector: '[onclick*="openCommandPalette"]', textKey: 'tour.search', position: 'bottom' },
    { selector: '[onclick*="toggleTheme"]', textKey: 'tour.theme', position: 'bottom' },
    { selector: '#cp-wrap-accent', textKey: 'tour.accent', position: 'bottom' },
    { selector: '[onclick*="openSettingsModal"]', textKey: 'tour.settings', position: 'bottom' },
    // #locale-select is enhanced via IconSelect (native select is hidden with display:none);
    // target the adjacent trigger button so the tour highlight has a visible bounding box.
    { selector: '#locale-select + .icon-select-trigger', textKey: 'tour.language', position: 'bottom' }
];

const dashboardTutorialSteps: TutorialStep[] = [
    { selector: '[data-dashboard-section="perf"]', textKey: 'tour.dash.perf', position: 'bottom' },
    { selector: '[data-dashboard-section="targets"]', textKey: 'tour.dash.targets', position: 'bottom' },
    { selector: '[data-dashboard-section="running"]', textKey: 'tour.dash.running', position: 'bottom' },
    { selector: '[data-dashboard-section="stopped"]', textKey: 'tour.dash.stopped', position: 'bottom' },
    { selector: '[data-dashboard-section="automations"]', textKey: 'tour.dash.automations', position: 'bottom' },
    { selector: '[data-dashboard-section="scenes"]', textKey: 'tour.dash.scenes', position: 'bottom' },
    { selector: '[data-dashboard-section="sync-clocks"]', textKey: 'tour.dash.sync_clocks', position: 'bottom' },
    { selector: '[data-dashboard-section="integrations"]', textKey: 'tour.dash.integrations', position: 'bottom' },
    // Customize panel walkthrough — panel is opened/closed via the
    // tour's `prepare` hook based on whether the step targets it.
    { selector: '[onclick*="openDashboardCustomize"]', textKey: 'tour.dash.customize_btn', position: 'bottom' },
    { selector: '#dashboard-customize-panel .dash-cust-header', textKey: 'tour.dash.customize_panel', position: 'left' },
    { selector: '#dashboard-customize-panel .dash-cust-section:nth-of-type(1)', textKey: 'tour.dash.customize_presets', position: 'left' },
    { selector: '#dashboard-customize-panel .dash-cust-section:nth-of-type(2)', textKey: 'tour.dash.customize_global', position: 'left' },
    { selector: '#dashboard-customize-panel .dash-cust-section:nth-of-type(3)', textKey: 'tour.dash.customize_sections', position: 'left' },
    { selector: '#dashboard-customize-panel .dash-cust-section:nth-of-type(4)', textKey: 'tour.dash.customize_perf_cells', position: 'left' }
];

const targetsTutorialSteps: TutorialStep[] = [
    { selector: '#targets-tree-nav .tree-dd-trigger', textKey: 'tour.tgt.nav', position: 'right' },
    { selector: '[data-card-section="led-devices"]', textKey: 'tour.tgt.devices', position: 'bottom', subTab: 'led-devices' },
    { selector: '[data-card-section="led-targets"]', textKey: 'tour.tgt.targets', position: 'bottom', subTab: 'led-targets' },
    { selector: '[data-card-section="ha-light-targets"]', textKey: 'tour.tgt.ha_light_targets', position: 'bottom', subTab: 'ha-light-targets' }
];

const sourcesTourSteps: TutorialStep[] = [
    { selector: '#streams-tree-nav .tree-dd-trigger', textKey: 'tour.src.nav', position: 'right' },
    { selector: '[data-card-section="raw-streams"]', textKey: 'tour.src.raw', position: 'bottom', subTab: 'raw' },
    { selector: '[data-card-section="raw-templates"]', textKey: 'tour.src.templates', position: 'bottom', subTab: 'raw_templates' },
    { selector: '[data-card-section="static-streams"]', textKey: 'tour.src.static', position: 'bottom', subTab: 'static_image' },
    { selector: '[data-card-section="proc-streams"]', textKey: 'tour.src.processed', position: 'bottom', subTab: 'processed' },
    { selector: '[data-card-section="color-strips"]', textKey: 'tour.src.color_strip', position: 'bottom', subTab: 'color_strip' },
    { selector: '[data-card-section="audio-capture"]', textKey: 'tour.src.audio', position: 'bottom', subTab: 'audio_capture' },
    { selector: '[data-card-section="value-sources"]', textKey: 'tour.src.value', position: 'bottom', subTab: 'value' },
    { selector: '[data-card-section="sync-clocks"]', textKey: 'tour.src.sync', position: 'bottom', subTab: 'sync' }
];

const automationsTutorialSteps: TutorialStep[] = [
    { selector: '[data-card-section="automations"]', textKey: 'tour.auto.list', position: 'bottom', subTab: 'automations' },
    { selector: '[data-cs-add="automations"]', textKey: 'tour.auto.add', position: 'bottom', subTab: 'automations' },
    { selector: '.card[data-automation-id]', textKey: 'tour.auto.card', position: 'bottom', subTab: 'automations' },
    { selector: '[data-card-section="scenes"]', textKey: 'tour.auto.scenes_list', position: 'bottom', subTab: 'scenes' },
    { selector: '[data-cs-add="scenes"]', textKey: 'tour.auto.scenes_add', position: 'bottom', subTab: 'scenes' },
    { selector: '.card[data-scene-id]', textKey: 'tour.auto.scenes_card', position: 'bottom', subTab: 'scenes' },
];

const integrationsTutorialSteps: TutorialStep[] = [
    { selector: '#integrations-tree-nav .tree-dd-trigger', textKey: 'tour.int.nav', position: 'right' },
    { selector: '[data-card-section="weather-sources"]', textKey: 'tour.int.weather', position: 'bottom', subTab: 'weather' },
    { selector: '[data-card-section="ha-sources"]', textKey: 'tour.int.home_assistant', position: 'bottom', subTab: 'home_assistant' },
    { selector: '[data-card-section="mqtt-sources"]', textKey: 'tour.int.mqtt', position: 'bottom', subTab: 'mqtt' },
    { selector: '[data-card-section="game-integrations"]', textKey: 'tour.int.game', position: 'bottom', subTab: 'game' }
];

const _fixedResolve = (step: TutorialStep): Element | null => {
    const el = document.querySelector(step.selector);
    if (!el) return null;
    // offsetParent is null when element or any ancestor has display:none
    if (!(el as HTMLElement).offsetParent) return null;
    return el;
};

const deviceTutorialSteps: TutorialStep[] = [
    { selector: '.mod-head', textKey: 'device.tip.identity', position: 'bottom' },
    { selector: '.mod-meta', textKey: 'device.tip.metadata', position: 'bottom' },
    { selector: '.mod-fader', textKey: 'device.tip.brightness', position: 'bottom' },
    { selector: '.mod-foot .mod-btn[onclick*="pingDevice"]', textKey: 'device.tip.ping', position: 'top' },
    { selector: '.mod-foot .mod-btn[onclick*="showSettings"]', textKey: 'device.tip.settings', position: 'top' },
    { selector: '.mod-menu-wrap', textKey: 'device.tip.menu', position: 'left' }
];

export function startTutorial(config: TutorialConfig): void {
    closeTutorial();
    // Remove focus from the trigger button so its outline doesn't persist
    if (document.activeElement) (document.activeElement as HTMLElement).blur();
    const overlay = document.getElementById(config.overlayId);
    if (!overlay) return;

    setActiveTutorial({
        steps: config.steps,
        overlay: overlay,
        mode: config.mode,
        step: 0,
        resolveTarget: config.resolveTarget,
        container: config.container,
        onClose: config.onClose || null,
        switchSubTab: config.switchSubTab || null,
        breadcrumbSelector: config.breadcrumbSelector || null,
        prepare: config.prepare || null
    });

    // Hide tooltip and ring until first step positions them (prevents flash at 0,0)
    const tooltip = overlay.querySelector('.tutorial-tooltip');
    const ring = overlay.querySelector('.tutorial-ring');
    const backdrop = overlay.querySelector('.tutorial-backdrop');
    if (tooltip) (tooltip as HTMLElement).style.visibility = 'hidden';
    if (ring) (ring as HTMLElement).style.visibility = 'hidden';
    if (backdrop) (backdrop as HTMLElement).style.clipPath = 'none';

    overlay.classList.add('active');
    document.addEventListener('keydown', handleTutorialKey);
    showTutorialStep(0);
}

export function startCalibrationTutorial(): void {
    const container = document.querySelector('#calibration-modal .modal-body');
    if (!container) return;
    startTutorial({
        steps: calibrationTutorialSteps,
        overlayId: 'calibration-tutorial-overlay',
        mode: 'absolute',
        container: container,
        resolveTarget: (step: TutorialStep): Element | null => {
            const el = document.querySelector(step.selector) as HTMLElement | null;
            if (!el) return null;
            // Skip elements hidden via display:none (e.g. overlay btn in device mode)
            if (el.style.display === 'none' || getComputedStyle(el).display === 'none') return null;
            return el;
        }
    });
}

export function startDeviceTutorial(deviceId?: string): void {
    const selector = deviceId
        ? `.card[data-device-id="${deviceId}"]`
        : '.card[data-device-id]';
    if (!document.querySelector(selector)) return;
    startTutorial({
        steps: deviceTutorialSteps,
        overlayId: 'device-tutorial-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: (step: TutorialStep): Element | null => {
            const card = document.querySelector(selector);
            if (!card) return null;
            return step.global
                ? document.querySelector(step.selector)
                : card.querySelector(step.selector);
        }
    });
}

export function startGettingStartedTutorial(): void {
    startTutorial({
        steps: gettingStartedSteps,
        overlayId: 'getting-started-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: (step: TutorialStep): Element | null => document.querySelector(step.selector) || null,
        onClose: () => localStorage.setItem(TOUR_KEY, '1')
    });
}

export function startDashboardTutorial(): void {
    const isCustomizeStep = (step: TutorialStep): boolean =>
        step.selector.includes('#dashboard-customize-panel');

    const callWindow = (fnName: string): void => {
        const fn = (window as any)[fnName];
        if (typeof fn === 'function') fn();
    };

    startTutorial({
        steps: dashboardTutorialSteps,
        overlayId: 'getting-started-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: _fixedResolve,
        prepare: (step: TutorialStep): void => {
            // Open the customize panel for steps inside it; close it
            // when the tour moves back out so dashboard sections aren't
            // covered.
            if (isCustomizeStep(step)) callWindow('openDashboardCustomize');
            else callWindow('closeDashboardCustomize');
        },
        onClose: (): void => callWindow('closeDashboardCustomize')
    });
}

function _windowSwitch(fnName: string): (key: string) => void {
    return (key: string): void => {
        const fn = (window as any)[fnName];
        if (typeof fn === 'function') fn(key);
    };
}

export function startTargetsTutorial(): void {
    startTutorial({
        steps: targetsTutorialSteps,
        overlayId: 'getting-started-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: _fixedResolve,
        switchSubTab: _windowSwitch('switchTargetSubTab'),
        breadcrumbSelector: '#targets-tree-nav .tree-dd-trigger-title'
    });
}

export function startSourcesTutorial(): void {
    startTutorial({
        steps: sourcesTourSteps,
        overlayId: 'getting-started-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: _fixedResolve,
        switchSubTab: _windowSwitch('switchStreamTab'),
        breadcrumbSelector: '#streams-tree-nav .tree-dd-trigger-title'
    });
}

export function startAutomationsTutorial(): void {
    startTutorial({
        steps: automationsTutorialSteps,
        overlayId: 'getting-started-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: _fixedResolve,
        switchSubTab: _windowSwitch('switchAutomationTab'),
        breadcrumbSelector: '#automations-tree-nav .tree-dd-trigger-title'
    });
}

export function startIntegrationsTutorial(): void {
    startTutorial({
        steps: integrationsTutorialSteps,
        overlayId: 'getting-started-overlay',
        mode: 'fixed',
        container: null,
        resolveTarget: _fixedResolve,
        switchSubTab: _windowSwitch('switchIntegrationTab'),
        breadcrumbSelector: '#integrations-tree-nav .tree-dd-trigger-title'
    });
}

export function closeTutorial(): void {
    if (!activeTutorial) return;
    const onClose = activeTutorial.onClose;
    activeTutorial.overlay.classList.remove('active');
    document.querySelectorAll('.tutorial-target').forEach(el => {
        el.classList.remove('tutorial-target');
        (el as HTMLElement).style.zIndex = '';
    });
    document.removeEventListener('keydown', handleTutorialKey);
    setActiveTutorial(null);
    if (onClose) onClose();
}

export function tutorialNext(): void {
    if (!activeTutorial) return;
    if (activeTutorial.step < activeTutorial.steps.length - 1) {
        showTutorialStep(activeTutorial.step + 1);
    } else {
        closeTutorial();
    }
}

export function tutorialPrev(): void {
    if (!activeTutorial) return;
    if (activeTutorial.step > 0) {
        showTutorialStep(activeTutorial.step - 1, -1);
    }
}

function _renderPips(overlay: HTMLElement, total: number, current: number): void {
    const container = overlay.querySelector('.tutorial-pips');
    if (!container) return;
    if (container.children.length !== total) {
        container.innerHTML = '';
        for (let i = 0; i < total; i++) {
            const pip = document.createElement('span');
            pip.className = 'tutorial-pip';
            container.appendChild(pip);
        }
    }
    Array.from(container.children).forEach((pip, i) => {
        pip.classList.toggle('done', i < current);
        pip.classList.toggle('active', i === current);
    });
}

function _drawCable(
    overlay: HTMLElement,
    rx: number, ry: number, rw: number, rh: number,
    tooltip: HTMLElement,
    actualPosition: string
): void {
    const cable = overlay.querySelector('.tutorial-cable');
    if (!cable) return;
    const line = cable.querySelector('line') as SVGLineElement | null;
    if (!line) return;

    // Tooltip rect is viewport-relative. Ring rect (rx, ry, rw, rh) is in
    // the same coord space as the SVG: viewport for fixed overlays,
    // container-relative for absolute (modal) overlays. Translate the
    // tooltip rect so both endpoints share that space.
    const tt = tooltip.getBoundingClientRect();
    const isFixed = activeTutorial?.mode === 'fixed';
    let ttLeft = tt.left;
    let ttTop = tt.top;
    if (!isFixed && activeTutorial?.container) {
        const cRect = activeTutorial.container.getBoundingClientRect();
        ttLeft -= cRect.left;
        ttTop -= cRect.top;
    }
    const ttRight = ttLeft + tt.width;
    const ttBottom = ttTop + tt.height;

    let x1: number, y1: number, x2: number, y2: number;
    switch (actualPosition) {
        case 'top':
            x1 = rx + rw / 2; y1 = ry;
            x2 = ttLeft + tt.width / 2; y2 = ttBottom;
            break;
        case 'left':
            x1 = rx; y1 = ry + rh / 2;
            x2 = ttRight; y2 = ttTop + tt.height / 2;
            break;
        case 'right':
            x1 = rx + rw; y1 = ry + rh / 2;
            x2 = ttLeft; y2 = ttTop + tt.height / 2;
            break;
        case 'bottom':
        default:
            x1 = rx + rw / 2; y1 = ry + rh;
            x2 = ttLeft + tt.width / 2; y2 = ttTop;
            break;
    }

    line.setAttribute('x1', String(x1));
    line.setAttribute('y1', String(y1));
    line.setAttribute('x2', String(x2));
    line.setAttribute('y2', String(y2));
}

function _flashCornerLock(overlay: HTMLElement): void {
    overlay.classList.remove('step-changed');
    // Force reflow so removing+re-adding the class re-triggers the animation.
    void (overlay as HTMLElement).offsetWidth;
    overlay.classList.add('step-changed');
}

function _positionSpotlight(target: Element, overlay: HTMLElement, step: TutorialStep, index: number, isFixed: boolean): void {
    const targetRect = target.getBoundingClientRect();
    const pad = 6;
    let x, y, w, h;

    if (isFixed) {
        x = targetRect.left - pad;
        y = targetRect.top - pad;
        w = targetRect.width + pad * 2;
        h = targetRect.height + pad * 2;
    } else {
        const containerRect = activeTutorial!.container!.getBoundingClientRect();
        x = targetRect.left - containerRect.left - pad;
        y = targetRect.top - containerRect.top - pad;
        w = targetRect.width + pad * 2;
        h = targetRect.height + pad * 2;
    }

    const isV2 = overlay.classList.contains('tutorial-v2');

    const backdrop = overlay.querySelector('.tutorial-backdrop') as HTMLElement;
    if (backdrop) {
        backdrop.style.clipPath = `polygon(
            0% 0%, 0% 100%,
            ${x}px 100%, ${x}px ${y}px,
            ${x + w}px ${y}px, ${x + w}px ${y + h}px,
            ${x}px ${y + h}px, ${x}px 100%,
            100% 100%, 100% 0%)`;
    }

    const ring = overlay.querySelector('.tutorial-ring') as HTMLElement;
    if (ring) {
        ring.style.left = x + 'px';
        ring.style.top = y + 'px';
        ring.style.width = w + 'px';
        ring.style.height = h + 'px';
    }

    const tooltip = overlay.querySelector('.tutorial-tooltip') as HTMLElement;
    const textEl = overlay.querySelector('.tutorial-tooltip-text');
    const counterEl = overlay.querySelector('.tutorial-step-counter');
    if (textEl) textEl.textContent = t(step.textKey);
    const total = activeTutorial!.steps.length;
    if (counterEl) {
        const pad2 = (n: number) => String(n).padStart(2, '0');
        counterEl.textContent = isV2
            ? `${pad2(index + 1)} / ${pad2(total)}`
            : `${index + 1} / ${total}`;
    }

    const breadcrumbEl = overlay.querySelector('.tutorial-tooltip-breadcrumb') as HTMLElement | null;
    if (breadcrumbEl) {
        const sel = activeTutorial!.breadcrumbSelector;
        const labelSrc = sel ? document.querySelector(sel) : null;
        const label = (labelSrc?.textContent || '').trim();
        breadcrumbEl.textContent = label;
        breadcrumbEl.classList.toggle('is-visible', label.length > 0);
    }

    const prevBtn = overlay.querySelector('.tutorial-prev-btn') as HTMLButtonElement;
    const nextBtn = overlay.querySelector('.tutorial-next-btn') as HTMLButtonElement | null;
    if (prevBtn) prevBtn.disabled = (index === 0);
    if (nextBtn) {
        const isLast = index === total - 1;
        if (isV2) {
            nextBtn.innerHTML = isLast ? 'FINISH \u2713' : 'NEXT \u25b6';
        } else {
            nextBtn.textContent = isLast ? '\u2713' : '\u2192';
        }
    }

    let actualPosition = step.position;
    if (tooltip) {
        actualPosition = positionTutorialTooltip(tooltip, x, y, w, h, step.position, isFixed);
        tooltip.style.visibility = '';
    }
    if (ring) ring.style.visibility = '';

    if (isV2) {
        _renderPips(overlay, total, index);
        if (tooltip) _drawCable(overlay, x, y, w, h, tooltip, actualPosition);
        _flashCornerLock(overlay);
    }
}

/** Wait for scroll to settle (position stops changing). */
function _waitForScrollEnd(): Promise<void> {
    return new Promise<void>(resolve => {
        let lastY = window.scrollY;
        let stableFrames = 0;
        const fallback = setTimeout(resolve, 300);
        function check() {
            const y = window.scrollY;
            if (y === lastY) stableFrames++;
            else { stableFrames = 0; lastY = y; }
            if (stableFrames >= 3) { clearTimeout(fallback); resolve(); }
            else requestAnimationFrame(check);
        }
        requestAnimationFrame(check);
    });
}

function showTutorialStep(index: number, direction: number = 1): void {
    if (!activeTutorial) return;
    activeTutorial.step = index;
    const step = activeTutorial.steps[index];
    const overlay = activeTutorial.overlay;
    const isFixed = activeTutorial.mode === 'fixed';

    document.querySelectorAll('.tutorial-target').forEach(el => {
        el.classList.remove('tutorial-target');
        (el as HTMLElement).style.zIndex = '';
    });

    // Reveal panels that live behind a sub-tab before resolving the target.
    if (step.subTab && activeTutorial.switchSubTab) {
        activeTutorial.switchSubTab(step.subTab);
    }

    // Tour-level hook: open/close auxiliary UI (side panels, popups).
    if (activeTutorial.prepare) {
        activeTutorial.prepare(step);
    }

    const target = activeTutorial.resolveTarget(step);
    if (!target) {
        // Auto-skip hidden/missing targets in the current direction
        const next = index + direction;
        if (next >= 0 && next < activeTutorial.steps.length) showTutorialStep(next, direction);
        else closeTutorial();
        return;
    }
    target.classList.add('tutorial-target');
    if (isFixed) (target as HTMLElement).style.zIndex = '10001';

    // Scroll target into view if off-screen or behind sticky header
    const preRect = target.getBoundingClientRect();
    const headerH = isFixed ? (parseInt(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0) : 0;
    const needsScroll = preRect.bottom > window.innerHeight || preRect.top < headerH;

    if (needsScroll) {
        // Hide tooltip while scrolling to prevent stale position flash
        const tt = overlay.querySelector('.tutorial-tooltip') as HTMLElement | null;
        if (tt) tt.style.visibility = 'hidden';
        target.scrollIntoView({ behavior: 'smooth', block: 'center' });
        _waitForScrollEnd().then(() => {
            if (activeTutorial && activeTutorial.step === index) {
                _positionSpotlight(target, overlay, step, index, isFixed);
            }
        });
    } else {
        _positionSpotlight(target, overlay, step, index, isFixed);
    }
}

function positionTutorialTooltip(tooltip: HTMLElement, sx: number, sy: number, sw: number, sh: number, preferred: string, isFixed: boolean): string {
    const gap = 12;
    // v2 tooltip is wider; measure the live width instead of hardcoding.
    const tooltipW = tooltip.offsetWidth || 260;
    tooltip.setAttribute('style', 'left:-9999px;top:-9999px');
    const tooltipH = tooltip.offsetHeight || 150;

    const positions = {
        top:    { x: sx + sw / 2 - tooltipW / 2, y: sy - tooltipH - gap },
        bottom: { x: sx + sw / 2 - tooltipW / 2, y: sy + sh + gap },
        left:   { x: sx - tooltipW - gap, y: sy + sh / 2 - tooltipH / 2 },
        right:  { x: sx + sw + gap, y: sy + sh / 2 - tooltipH / 2 }
    };

    let chosen = preferred;
    let pos = positions[preferred] || positions.bottom;

    const cW = isFixed ? window.innerWidth : activeTutorial!.container!.clientWidth;
    const cH = isFixed ? window.innerHeight : activeTutorial!.container!.clientHeight;

    if (pos.y + tooltipH > cH || pos.y < 0 || pos.x + tooltipW > cW || pos.x < 0) {
        const opposite = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
        const altKey = opposite[preferred];
        const alt = positions[altKey];
        if (alt && alt.y >= 0 && alt.y + tooltipH <= cH && alt.x >= 0 && alt.x + tooltipW <= cW) {
            pos = alt;
            chosen = altKey;
        }
    }

    pos.x = Math.max(8, Math.min(cW - tooltipW - 8, pos.x));
    pos.y = Math.max(8, Math.min(cH - tooltipH - 8, pos.y));

    tooltip.setAttribute('style', `left:${Math.round(pos.x)}px;top:${Math.round(pos.y)}px`);
    return chosen;
}

function handleTutorialKey(e: KeyboardEvent): void {
    if (!activeTutorial) return;
    if (e.key === 'Escape') { closeTutorial(); e.stopPropagation(); }
    else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault(); tutorialNext(); }
    else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); tutorialPrev(); }
}
