import * as Solid from 'solid-js';
import { escapeHtml, getAssetCrossOrigin, resolveManifestAssetLink, } from '@tanstack/router-core';
import { useRouter } from './useRouter';
/**
 * Build the list of head/link/meta/script tags to render for active matches.
 * Used internally by `HeadContent`.
 */
export const useTags = (assetCrossOrigin) => {
    const router = useRouter();
    const nonce = router.options.ssr?.nonce;
    const getTagKey = (tag) => JSON.stringify(tag);
    const activeMatches = Solid.createMemo(() => router.stores.activeMatchesSnapshot.state);
    const routeMeta = Solid.createMemo(() => activeMatches()
        .map((match) => match.meta)
        .filter(Boolean));
    const meta = Solid.createMemo(() => {
        const resultMeta = [];
        const metaByAttribute = {};
        let title;
        const routeMetasArray = routeMeta();
        for (let i = routeMetasArray.length - 1; i >= 0; i--) {
            const metas = routeMetasArray[i];
            for (let j = metas.length - 1; j >= 0; j--) {
                const m = metas[j];
                if (!m)
                    continue;
                if (m.title) {
                    if (!title) {
                        title = {
                            tag: 'title',
                            children: m.title,
                        };
                    }
                }
                else if ('script:ld+json' in m) {
                    // Handle JSON-LD structured data
                    // Content is HTML-escaped to prevent XSS when injected via innerHTML
                    try {
                        const json = JSON.stringify(m['script:ld+json']);
                        resultMeta.push({
                            tag: 'script',
                            attrs: {
                                type: 'application/ld+json',
                            },
                            children: escapeHtml(json),
                        });
                    }
                    catch {
                        // Skip invalid JSON-LD objects
                    }
                }
                else {
                    const attribute = m.name ?? m.property;
                    if (attribute) {
                        if (metaByAttribute[attribute]) {
                            continue;
                        }
                        else {
                            metaByAttribute[attribute] = true;
                        }
                    }
                    resultMeta.push({
                        tag: 'meta',
                        attrs: {
                            ...m,
                            nonce,
                        },
                    });
                }
            }
        }
        if (title) {
            resultMeta.push(title);
        }
        if (router.options.ssr?.nonce) {
            resultMeta.push({
                tag: 'meta',
                attrs: {
                    property: 'csp-nonce',
                    content: router.options.ssr.nonce,
                },
            });
        }
        resultMeta.reverse();
        return resultMeta;
    });
    const links = Solid.createMemo(() => {
        const matches = activeMatches();
        const constructed = matches
            .map((match) => match.links)
            .filter(Boolean)
            .flat(1)
            .map((link) => ({
            tag: 'link',
            attrs: {
                ...link,
                nonce,
            },
        }));
        const manifest = router.ssr?.manifest;
        const assets = matches
            .map((match) => manifest?.routes[match.routeId]?.assets ?? [])
            .filter(Boolean)
            .flat(1)
            .filter((asset) => asset.tag === 'link')
            .map((asset) => ({
            tag: 'link',
            attrs: {
                ...asset.attrs,
                crossOrigin: getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
                    asset.attrs?.crossOrigin,
                nonce,
            },
        }));
        return [...constructed, ...assets];
    });
    const preloadLinks = Solid.createMemo(() => {
        const matches = activeMatches();
        const preloadLinks = [];
        matches
            .map((match) => router.looseRoutesById[match.routeId])
            .forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads
            ?.filter(Boolean)
            .forEach((preload) => {
            const preloadLink = resolveManifestAssetLink(preload);
            preloadLinks.push({
                tag: 'link',
                attrs: {
                    rel: 'modulepreload',
                    href: preloadLink.href,
                    crossOrigin: getAssetCrossOrigin(assetCrossOrigin, 'modulepreload') ??
                        preloadLink.crossOrigin,
                    nonce,
                },
            });
        }));
        return preloadLinks;
    });
    const styles = Solid.createMemo(() => activeMatches()
        .map((match) => match.styles)
        .flat(1)
        .filter(Boolean).map(({ children, ...style }) => ({
        tag: 'style',
        attrs: {
            ...style,
            nonce,
        },
        children,
    })));
    const headScripts = Solid.createMemo(() => activeMatches()
        .map((match) => match.headScripts)
        .flat(1)
        .filter(Boolean).map(({ children, ...script }) => ({
        tag: 'script',
        attrs: {
            ...script,
            nonce,
        },
        children,
    })));
    return Solid.createMemo((prev) => {
        const next = uniqBy([
            ...meta(),
            ...preloadLinks(),
            ...links(),
            ...styles(),
            ...headScripts(),
        ], getTagKey);
        if (prev &&
            prev.length === next.length &&
            prev.every((tag, index) => getTagKey(tag) === getTagKey(next[index]))) {
            return prev;
        }
        return next;
    });
};
export function uniqBy(arr, fn) {
    const seen = new Set();
    return arr.filter((item) => {
        const key = fn(item);
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    });
}
//# sourceMappingURL=headContentUtils.jsx.map