import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';

// Register GSAP plugin with error boundary
try {
    gsap.registerPlugin(ScrollTrigger);
} catch (error) {
    console.error('Failed to register GSAP ScrollTrigger plugin:', error);
    throw new Error('GSAP ScrollTrigger plugin registration failed. Altitude sidebar will not work.', { cause: error });
}

interface AltitudeSidebarOptions {
    valueEl: HTMLElement;
    /** All altitude-tagged sections ([data-section][data-altitude]) — drives the numeric readout. */
    readoutSections: HTMLElement[];
    /** Liquid marker that slides/morphs to the currently active level's star. */
    blobEl: HTMLElement;
    /** Star markers in the track, in top-to-bottom order. */
    levelEls: HTMLElement[];
    /** Mountain-band sections, paired 1:1 with levelEls by index — drives which level is "active". */
    levelSections: HTMLElement[];
}

/**
 * AltitudeSidebar - updates the fixed altitude readout as the user scrolls
 * through the home page's altitude-tagged sections, and slides a single
 * liquid blob marker to whichever track star matches the currently active
 * mountain band (only one lit at a time).
 */
export class AltitudeSidebar {
    private options: AltitudeSidebarOptions;
    private triggers: ScrollTrigger[] = [];
    private blobTimeline: ReturnType<typeof gsap.timeline> | null = null;
    private activeIndex = -1;

    constructor(options: AltitudeSidebarOptions) {
        this.options = options;
    }

    public init(): void {
        gsap.set(this.options.blobEl, { xPercent: -50, yPercent: -50 });

        this.options.readoutSections.forEach((section) => {
            const altitude = section.dataset.altitude;
            if (!altitude) {
                return;
            }

            const trigger = ScrollTrigger.create({
                trigger: section,
                start: 'top center',
                end: 'bottom center',
                onEnter: () => this.setAltitude(altitude),
                onEnterBack: () => this.setAltitude(altitude),
            });
            this.triggers.push(trigger);
        });

        this.options.levelSections.forEach((section, index) => {
            const trigger = ScrollTrigger.create({
                trigger: section,
                start: 'top center',
                end: 'bottom center',
                onEnter: () => this.setActiveLevel(index),
                onEnterBack: () => this.setActiveLevel(index),
            });
            this.triggers.push(trigger);
        });
    }

    private setActiveLevel(index: number): void {
        if (index === this.activeIndex) {
            return;
        }

        const levelEl = this.options.levelEls[index];
        if (!levelEl) {
            return;
        }

        const isFirstActivation = this.activeIndex === -1;
        this.activeIndex = index;
        const targetTop = levelEl.offsetTop + levelEl.offsetHeight / 2;

        this.blobTimeline?.kill();

        if (isFirstActivation) {
            gsap.set(this.options.blobEl, { top: targetTop, scale: 0.4, opacity: 0 });
            this.blobTimeline = gsap.timeline().to(this.options.blobEl, {
                opacity: 1,
                scale: 1,
                duration: 0.4,
                ease: 'back.out(1.8)',
            });
            return;
        }

        // Squash/stretch while sliding, then spring back to a resting
        // capsule shape — reads as a liquid blob morphing between the two
        // points rather than a marker teleporting.
        this.blobTimeline = gsap
            .timeline()
            .to(this.options.blobEl, { scaleX: 0.7, scaleY: 1.4, duration: 0.15, ease: 'power1.out' })
            .to(this.options.blobEl, { top: targetTop, duration: 0.35, ease: 'power2.inOut' }, '<')
            .to(this.options.blobEl, { scaleX: 1, scaleY: 1, duration: 0.3, ease: 'elastic.out(1, 0.55)' });
    }

    private setAltitude(value: string): void {
        const textNode = this.options.valueEl.childNodes[0];
        if (textNode) {
            textNode.textContent = value;
        }
    }

    public destroy(): void {
        this.triggers.forEach((trigger) => trigger.kill());
        this.triggers = [];
        this.blobTimeline?.kill();
    }
}
