/**
 * Before/after comparison slider (ProjectDetail `before-after` block type, see
 * templates/components/_project-detail-block-before-after.html.twig and decision (c) in
 * spec-driven-development/project-detail-redesign.md). Public-facing module (not
 * `assets/admin/`, mirrors how `assets/project-detail.ts` already owns the page's other
 * public-facing GSAP behavior) — no drag-slider/comparison-slider pattern existed anywhere
 * under `assets/` prior to this, so this is new, standalone infrastructure.
 *
 * Pointer Events (`pointerdown`/`pointermove`/`pointerup`, with `setPointerCapture`) drive a
 * CSS `clip-path: inset(0 X% 0 0)` on the "after" image layer. No new npm dependency.
 * Works identically in edit mode and public view — a display interaction, not an editing
 * control, so it mounts unconditionally wherever `[data-before-after-handle]` is present.
 *
 * Keyboard accessible: arrow-key nudge on the divider handle (`role="slider"` in the Twig
 * partial), `aria-valuenow` kept in sync.
 */

export interface ContainerRect {
    left: number;
    width: number;
}

const KEYBOARD_STEP = 2;
const DEFAULT_PERCENT = 50;

/**
 * Pure function: percentage (0-100, clamped) of the container's width that lies to the left
 * of `clientX` — i.e. how much of the "after" layer should be visible from the left edge.
 * Extracted standalone (no DOM/pointer dependency) so the clamp/position math is unit
 * testable without mounting real pointer events — see
 * tests/frontend/components/before-after-slider.test.ts.
 */
export function computeClipPercent(clientX: number, containerRect: ContainerRect): number {
    if (containerRect.width <= 0) {
        return DEFAULT_PERCENT;
    }

    const raw = ((clientX - containerRect.left) / containerRect.width) * 100;
    return Math.min(100, Math.max(0, raw));
}

class BeforeAfterSlider {
    private handle: HTMLElement;
    private container: HTMLElement;
    private afterLayer: HTMLElement;
    private percent = DEFAULT_PERCENT;

    constructor(handle: HTMLElement, container: HTMLElement, afterLayer: HTMLElement) {
        this.handle = handle;
        this.container = container;
        this.afterLayer = afterLayer;

        this.applyPercent(this.percent);
        this.bindPointerEvents();
        this.bindKeyboard();
    }

    private bindPointerEvents(): void {
        this.handle.addEventListener('pointerdown', (event: PointerEvent) => {
            event.preventDefault();
            this.handle.setPointerCapture(event.pointerId);
            this.updateFromClientX(event.clientX);

            const onMove = (moveEvent: PointerEvent): void => {
                this.updateFromClientX(moveEvent.clientX);
            };
            const onUp = (upEvent: PointerEvent): void => {
                this.handle.releasePointerCapture(upEvent.pointerId);
                this.handle.removeEventListener('pointermove', onMove);
                this.handle.removeEventListener('pointerup', onUp);
                this.handle.removeEventListener('pointercancel', onUp);
            };

            this.handle.addEventListener('pointermove', onMove);
            this.handle.addEventListener('pointerup', onUp);
            this.handle.addEventListener('pointercancel', onUp);
        });
    }

    private bindKeyboard(): void {
        this.handle.addEventListener('keydown', (event: KeyboardEvent) => {
            if (event.key === 'ArrowLeft') {
                event.preventDefault();
                this.applyPercent(this.percent - KEYBOARD_STEP);
            } else if (event.key === 'ArrowRight') {
                event.preventDefault();
                this.applyPercent(this.percent + KEYBOARD_STEP);
            } else if (event.key === 'Home') {
                event.preventDefault();
                this.applyPercent(0);
            } else if (event.key === 'End') {
                event.preventDefault();
                this.applyPercent(100);
            }
        });
    }

    private updateFromClientX(clientX: number): void {
        const rect = this.container.getBoundingClientRect();
        this.applyPercent(computeClipPercent(clientX, rect));
    }

    private applyPercent(percent: number): void {
        this.percent = Math.min(100, Math.max(0, percent));
        this.afterLayer.style.clipPath = `inset(0 ${100 - this.percent}% 0 0)`;
        this.handle.style.left = `${this.percent}%`;
        this.handle.setAttribute('aria-valuenow', String(Math.round(this.percent)));
    }
}

/**
 * Mounts one `BeforeAfterSlider` per `[data-before-after-handle]` found under `root`
 * (defaults to `document`). Skips a handle silently (logs a warning) if its expected
 * container/after-layer structure isn't present, rather than throwing and blocking the rest
 * of the page's mounting.
 */
export function initBeforeAfterSliders(root: ParentNode = document): void {
    root.querySelectorAll<HTMLElement>('[data-before-after-handle]').forEach(handle => {
        const container = handle.closest<HTMLElement>('[data-before-after-container="true"]');
        const afterLayer = container?.querySelector<HTMLElement>('[data-before-after-after-layer="true"]');

        if (!container || !afterLayer) {
            console.warn('[BeforeAfterSlider] Missing container or after-layer for a [data-before-after-handle] element');
            return;
        }

        new BeforeAfterSlider(handle, container, afterLayer);
    });
}
