<!--
    Component: FontApplicator
    Loads fonts from fontshare with link tag
    - Loads font only if it's not already applied
    - Uses IntersectionObserver to detect when font is visible
    - Adds smooth transition when font appears
-->
<script lang="ts">
import { cn } from '$shared/shadcn/utils/shadcn-utils';
import type { Snippet } from 'svelte';
import { prefersReducedMotion } from 'svelte/motion';
import { appliedFontsManager } from '../../model';

interface Props {
    /**
     * Font name to set
     */
    name: string;
    /**
     * Font id to load
     */
    id: string;
    /** */
    url: string;
    /**
     * Font weight
     */
    weight?: number;
    /**
     * Variable font flag
     */
    isVariable?: boolean;
    /**
     * Additional classes
     */
    className?: string;
    /**
     * Children
     */
    children?: Snippet;
}

let { name, id, url, weight = 400, isVariable = false, className, children }: Props = $props();
let element: Element;

// Track if the user has actually scrolled this into view
let hasEnteredViewport = $state(false);
const status = $derived(appliedFontsManager.getFontStatus(id, weight, isVariable));

$effect(() => {
    if (status === 'loaded' || status === 'error') {
        hasEnteredViewport = true;
        return;
    }

    const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
            hasEnteredViewport = true;

            // Touch ensures it's in the queue.
            // It's safe to call this even if VirtualList called it
            // (Manager dedupes based on key)
            appliedFontsManager.touch([{
                id,
                weight,
                name,
                url,
                isVariable,
            }]);

            observer.unobserve(element);
        }
    });

    if (element) observer.observe(element);
    return () => observer.disconnect();
});

// The "Show" condition: Element is in view AND (Font is ready OR it errored out)
const shouldReveal = $derived(hasEnteredViewport && (status === 'loaded'));

const transitionClasses = $derived(
    prefersReducedMotion.current
        ? 'transition-none' // Disable CSS transitions if motion is reduced
        : 'transition-all duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]',
);
</script>

<div
    bind:this={element}
    style:font-family={shouldReveal ? `'${name}'` : 'system-ui, -apple-system, sans-serif'}
    class={cn(
        transitionClasses,
        // If reduced motion is on, we skip the transform/blur entirely
        !shouldReveal && !prefersReducedMotion.current
            && 'opacity-50 scale-[0.95] blur-sm',
        !shouldReveal && prefersReducedMotion.current && 'opacity-0', // Still hide until font is ready, but no movement
        shouldReveal && 'opacity-100 scale-100 blur-0',
        className,
    )}
>
    {@render children?.()}
</div>
