---
type PageType =
    | 'WebPage'
    | 'Article'
    | 'BlogPosting'
    | 'NewsArticle'
    | 'AboutPage'
    | 'CollectionPage'
    | 'ContactPage'
    | 'FAQPage'
    | 'ProfilePage';

interface FaqItem {
    question: string;
    answer: string;
}

interface Props {
    pageType?: PageType;
    name: string;
    description: string;
    url?: string;
    image?: string;
    headline?: string;
    datePublished?: string;
    dateModified?: string;
    authorName?: string;
    authorUrl?: string;
    authorImage?: string;
    authorId?: string;
    authorType?: 'Person' | 'Organization';
    publisherName?: string;
    publisherLogo?: string;
    publisherUrl?: string;
    publisherId?: string;
    websiteId?: string;
    breadcrumbId?: string;
    mainEntityId?: string;
    keywords?: string;
    inLanguage?: string;
    articleSection?: string;
    faqItems?: FaqItem[];
    jobTitle?: string;
    sameAs?: string[];
    additionalSchemas?: Record<string, unknown>[];
}

const {
    pageType = 'WebPage',
    name,
    description,
    url = Astro.url.href,
    image,
    headline,
    datePublished,
    dateModified,
    authorName,
    authorUrl,
    authorImage,
    authorId,
    authorType = 'Person',
    publisherName = 'Lefebvre',
    publisherLogo = 'https://assets.lefebvre.es/media/logos-2/svg/lefebvre.svg',
    publisherUrl = 'https://lefebvre.es',
    publisherId,
    websiteId,
    breadcrumbId,
    mainEntityId,
    keywords,
    inLanguage = 'es',
    articleSection,
    faqItems = [],
    jobTitle,
    sameAs,
    additionalSchemas = [],
} = Astro.props as Props;

const escapeJson = (s = "") => String(s).replace(/<[^>]*>/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/[\n\r\t]+/g, ' ');

const isArticleType = (['Article', 'BlogPosting', 'NewsArticle'] as PageType[]).includes(pageType);

// @id por-página RELATIVOS de fragmento: cualquier bloque del mismo documento (incluido el HTML
// inyectado por el render API del CMS) puede referenciarlos sin conocer la URL; Google los resuelve
// contra la página. Los @id de sitio/cross-página (websiteId, publisherId, authorId/#person) siguen
// absolutos y se reciben por props.
const webPageNodeId = '#webpage';
const primaryImageId = '#primaryimage';
const articleNodeId = '#article';

// Nodo ImageObject de la imagen principal (si hay), referenciable por @id.
const imageNode = image
    ? { '@type': 'ImageObject', '@id': primaryImageId, url: image }
    : null;

// Autor: nodo inline CON @id (autocontenido y fusionable entre páginas) si llega authorName;
// referencia pura { @id } si solo llega authorId (caso "Lefebvre" → #organization global).
const buildAuthor = (): Record<string, unknown> | null => {
    if (authorName) {
        const node: Record<string, unknown> = { '@type': authorType, name: authorName };
        if (authorId) node['@id'] = authorId;
        if (authorUrl) node.url = authorUrl;
        if (authorImage && authorType === 'Person') node.image = authorImage;
        return node;
    }
    if (authorId) return { '@id': authorId };
    return null;
};

const buildPublisher = (): Record<string, unknown> =>
    publisherId
        ? { '@id': publisherId }
        : {
            '@type': 'Organization',
            name: publisherName,
            url: publisherUrl,
            logo: { '@type': 'ImageObject', url: publisherLogo },
        };

// --- Nodo WebPage (siempre). Para tipos artículo el @type es WebPage; si no, el propio pageType.
const webPageType = isArticleType ? 'WebPage' : pageType;
const webPage: Record<string, unknown> = {
    '@type': webPageType,
    '@id': webPageNodeId,
    url,
    name,
    inLanguage,
};
if (description) webPage.description = description;
if (websiteId) webPage.isPartOf = { '@id': websiteId };
if (breadcrumbId) webPage.breadcrumb = { '@id': breadcrumbId };
if (imageNode) webPage.primaryImageOfPage = { '@id': primaryImageId };
if (!isArticleType && keywords) webPage.keywords = keywords;

const graph: Array<Record<string, unknown>> = [webPage];
if (imageNode) graph.push(imageNode);

// --- Tipos artículo: nodo independiente (#article) enlazado al WebPage ---
if (isArticleType) {
    webPage.mainEntity = { '@id': articleNodeId };
    const article: Record<string, unknown> = {
        '@type': pageType,
        '@id': articleNodeId,
        headline: headline || name,
        isPartOf: { '@id': webPageNodeId },
        mainEntityOfPage: { '@id': webPageNodeId },
        inLanguage,
    };
    if (description) article.description = description;
    if (datePublished) article.datePublished = datePublished;
    if (dateModified) article.dateModified = dateModified;
    if (articleSection) article.articleSection = articleSection;
    if (keywords) article.keywords = keywords;
    if (imageNode) article.image = { '@id': primaryImageId };
    const author = buildAuthor();
    if (author) article.author = author;
    article.publisher = buildPublisher();
    graph.push(article);
}

// --- FAQPage: las preguntas son el mainEntity del WebPage ---
if (pageType === 'FAQPage' && faqItems.length) {
    webPage.mainEntity = faqItems.map((item) => ({
        '@type': 'Question',
        name: item.question,
        acceptedAnswer: { '@type': 'Answer', text: item.answer },
    }));
}

// --- ProfilePage: el mainEntity es la entidad (Person/Organization) del autor ---
if (pageType === 'ProfilePage' && (authorName || authorId)) {
    const entityId = authorId || `${url}#person`;
    webPage.mainEntity = { '@id': entityId };
    if (authorName) {
        const entity: Record<string, unknown> = { '@type': authorType, '@id': entityId, name: authorName };
        if (authorUrl) entity.url = authorUrl;
        if (authorImage && authorType === 'Person') entity.image = authorImage;
        if (jobTitle && authorType === 'Person') entity.jobTitle = jobTitle;
        if (description) entity.description = description;
        if (sameAs && sameAs.length) entity.sameAs = sameAs;
        graph.push(entity);
    }
    // Si solo llega authorId (sin authorName), se referencia un nodo definido en otra parte
    // (p. ej. la Organization global #organization del Layout) → no se redeclara aquí.
}

// --- mainEntity explícito (p. ej. CollectionPage → ItemList): se aplica solo si el tipo no la fija
// ya (artículo/FAQ/Profile). El consumidor pasa el @id del nodo principal (relativo, p. ej. "#itemlist").
if (mainEntityId && !webPage.mainEntity) {
    webPage.mainEntity = { '@id': mainEntityId };
}

const schema: Record<string, unknown> = {
    '@context': 'https://schema.org',
    '@graph': graph,
};

const stringify = (value: unknown): string => {
    if (value === null || value === undefined) return 'null';
    if (typeof value === 'string') return `"${escapeJson(value)}"`;
    if (typeof value === 'number' || typeof value === 'boolean') return String(value);
    if (Array.isArray(value)) return `[${value.map(stringify).join(',')}]`;
    const entries = Object.entries(value as Record<string, unknown>).filter(([, v]) => v !== undefined);
    return `{${entries.map(([k, v]) => `"${k}":${stringify(v)}`).join(',')}}`;
};

const buildScript = (value: unknown) => `<script type="application/ld+json">${stringify(value)}</script>`;

const structuredData = [
    buildScript(schema),
    ...additionalSchemas.map(buildScript),
].join('\n');
---

<Fragment set:html={structuredData} />
