---
// External dependencies
import { marked } from 'marked';

import { render } from 'astro:content';
import type { CollectionEntry } from 'astro:content';

import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
import Footer from '@layouts/Footer.astro';

import components from '@components/MDX/components';
import NodeGraph from '@components/MDX/NodeGraph/NodeGraph.astro';
import SchemaViewer from '@components/MDX/SchemaViewer/SchemaViewerRoot.astro';
import Admonition from '@components/MDX/Admonition';
import VersionList from '@components/Lists/VersionList.astro';
import CopyAsMarkdown from '@components/CopyAsMarkdown';
import FavoriteButton from '@components/FavoriteButton';
import Badge from '@components/Badge.astro';
import { shouldRenderSideBarSection } from '@stores/sidebar-store/builders/shared';

import {
  QueueListIcon,
  RectangleGroupIcon,
  ServerIcon,
  BoltIcon,
  ChatBubbleLeftIcon,
  MagnifyingGlassIcon,
  MapIcon,
  ClockIcon,
} from '@heroicons/react/24/outline';
import { ArrowsRightLeftIcon } from '@heroicons/react/20/solid';
import {
  Bot,
  Box,
  Boxes,
  SquarePenIcon,
  DatabaseIcon,
  DatabaseZapIcon,
  ShieldCheckIcon,
  AlignLeft,
  Package,
  BookText,
  CalendarDays,
} from 'lucide-react';

import { getSpecificationsForService } from '@utils/collections/services';
import { resourceToCollectionMap, collectionToResourceMap, getDeprecatedDetails } from '@utils/collections/util';
import {
  createAdrStatusBadge,
  formatAdrDate,
  getAdrRelationships,
  getAdrs,
  isAdrCollection,
  isDeprecatedAdr,
  isSupersededAdr,
} from '@utils/collections/adrs';
import { getSchemasFromResource } from '@utils/collections/schemas';
import { getIcon } from '@utils/badges';
import { buildUrl, buildEditUrlForResource } from '@utils/url-builder';
import { isIconPath, resolveIconUrl } from '@utils/icon';
import {
  isEventCatalogChatEnabled,
  isMarkdownDownloadEnabled,
  isVisualiserEnabled,
  isRSSEnabled,
  isExportPDFEnabled,
} from '@utils/feature';
import { getMDXComponentsByName, parseMdxBooleanProp } from '@utils/markdown';

import type { CollectionTypes } from '@types';

import config from '@config';

import { Page } from './_index.data';

export const prerender = Page.prerender;
export const getStaticPaths = Page.getStaticPaths;

// Get data
const props = await Page.getData(Astro);

const { Content, headings } = await render(props);
const isAdrPage = isAdrCollection(props.collection);
const allAdrs = isAdrPage ? await getAdrs({ getAllVersions: true }) : [];
const adrRelationships = isAdrPage ? getAdrRelationships(props as any, allAdrs) : null;
const supersededByAdrs = adrRelationships?.supersededBy || [];

const pageTitle = `${props.collection} | ${props.data.name}`.replace(/^\w/, (c) => c.toUpperCase());
const contentBadges = props.data.badges || [];
const editUrl =
  props.data.editUrl || (config.editUrl && props?.filePath ? buildEditUrlForResource(config.editUrl, props?.filePath) : '');

const headerIconSrc = isIconPath(props.data.styles?.icon) ? resolveIconUrl(props.data.styles!.icon!) : null;

const getContentBadges = () =>
  contentBadges.map((badge: any) => ({
    ...badge,
    icon: badge.icon ? getIcon(badge.icon) : null,
  }));

const createResourceBadge = (content: string, icon: any): any => ({ content, icon, textColor: 'gray' });

const getAgentProviderIconURL = (provider: string) => {
  const providerIconName = provider
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/[^a-z0-9-]/g, '');

  return buildUrl(`/icons/agent/${providerIconName}.svg`, true);
};

const getAgentProviderIconURLs = (provider: string) => {
  const providerIconName = provider
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '-')
    .replace(/[^a-z0-9-]/g, '');

  const hasThemedIcon = ['openai', 'anthropic'].includes(providerIconName);

  return hasThemedIcon
    ? {
        iconURLLight: buildUrl(`/icons/agent/${providerIconName}-light.svg`, true),
        iconURLDark: buildUrl(`/icons/agent/${providerIconName}-dark.svg`, true),
      }
    : {
        iconURL: getAgentProviderIconURL(provider),
      };
};

const getBadge = () => {
  if (props.collection === 'agents') {
    const badges = [createResourceBadge('Agent', Bot)];
    if (props.data?.model?.provider) {
      badges.push({
        content: props.data.model.provider,
        ...getAgentProviderIconURLs(props.data.model.provider),
        textColor: 'gray',
      });
    }
    const modelBadge = props.data?.model?.name;
    if (modelBadge) {
      badges.push(createResourceBadge(modelBadge, Bot));
    }
    return badges;
  }

  if (isAdrPage) {
    return [
      createResourceBadge('Decision Record', BookText),
      createResourceBadge(formatAdrDate(props.data.date), CalendarDays),
      createAdrStatusBadge(props.data.status),
    ];
  }

  if (props.collection === 'services') {
    return [createResourceBadge('Service', ServerIcon)];
  }
  if (props.collection === 'events') {
    return [createResourceBadge('Event', BoltIcon)];
  }
  if (props.collection === 'commands') {
    return [createResourceBadge('Command', ChatBubbleLeftIcon)];
  }
  if (props.collection === 'queries') {
    return [createResourceBadge('Query', MagnifyingGlassIcon)];
  }
  if (props.collection === 'domains') {
    return [createResourceBadge('Domain', RectangleGroupIcon)];
  }

  if (props.collection === 'flows') {
    return [createResourceBadge('Flow', QueueListIcon)];
  }

  if (props.collection === 'channels') {
    return [createResourceBadge('Channel', ArrowsRightLeftIcon)];
  }

  if (props.collection === 'containers') {
    const badges = [];
    const content = props.data.container_type?.charAt(0).toUpperCase() + props.data.container_type?.slice(1) || 'Database';

    badges.push(createResourceBadge(content, DatabaseIcon));

    if (props.data?.technology) {
      badges.push(createResourceBadge(`${props.data.technology}`, DatabaseZapIcon));
    }

    if (props.data?.residency) {
      badges.push(createResourceBadge(`Residency: ${props.data.residency}`, MapIcon));
    }

    if (props.data?.retention) {
      badges.push(createResourceBadge(`Retention: ${props.data.retention}`, ClockIcon));
    }

    if (props.data?.access_mode) {
      badges.push(createResourceBadge(`Access Mode: ${props.data.access_mode}`, ShieldCheckIcon));
    }

    return badges;
  }

  if (props.collection === 'entities') {
    const entityBadges = [createResourceBadge('Entity', Box)];
    if (props.data.aggregateRoot) {
      entityBadges.push(createResourceBadge('(Aggregate Root)', Boxes));
    }
    return entityBadges;
  }

  if (props.collection === 'data-products') {
    return [createResourceBadge('Data Product', Package)];
  }

  return [createResourceBadge('', QueueListIcon)];
};

const getSpecificationBadges = () => {
  const badges = [];

  const specifications = getSpecificationsForService(props);

  const asyncapiSpecs = specifications.filter((spec) => spec.type === 'asyncapi');
  const openapiSpecs = specifications.filter((spec) => spec.type === 'openapi');
  const graphQLSpecs = specifications.filter((spec) => spec.type === 'graphql');

  if (openapiSpecs.length > 0) {
    for (const spec of openapiSpecs) {
      badges.push({
        content: spec.name || 'OpenAPI Spec',
        iconURL: buildUrl('/icons/openapi.svg', true),
        class: 'text-[rgb(var(--ec-page-text))] hover:underline',
        id: 'open-api-badge',
        url: buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}/spec/${spec.filenameWithoutExtension}`),
      });
    }
  }

  if (asyncapiSpecs.length > 0) {
    for (const spec of asyncapiSpecs) {
      badges.push({
        content: spec.name || 'AsyncAPI Spec',
        iconURL: buildUrl('/icons/asyncapi.svg', true),
        class: 'text-[rgb(var(--ec-page-text))] hover:underline',
        id: 'asyncapi-badge',
        url: buildUrl(
          `/docs/${props.collection}/${props.data.id}/${props.data.version}/asyncapi/${spec.filenameWithoutExtension}`
        ),
      });
    }
  }

  if (graphQLSpecs.length > 0) {
    for (const spec of graphQLSpecs) {
      badges.push({
        content: spec.name || 'GraphQL Spec',
        iconURL: buildUrl('/icons/graphql.svg', true),
        class: 'text-[rgb(var(--ec-page-text))] hover:underline',
        id: 'graphql-badge',
        url: buildUrl(
          `/docs/${props.collection}/${props.data.id}/${props.data.version}/graphql/${spec.filenameWithoutExtension}`
        ),
      });
    }
  }

  return badges;
};

const badges = [...getBadge(), ...getContentBadges(), ...getSpecificationBadges()];

// Index only the latest version
const pagefindAttributes =
  props.data.version === props.data.latestVersion
    ? {
        'data-pagefind-body': '',
        'data-pagefind-meta': `title:${pageTitle}`,
      }
    : {};

const {
  hasDeprecated,
  message: deprecatedMessage,
  isMarkedAsDeprecated,
  deprecatedDate: formattedDate,
} = getDeprecatedDetails(props as unknown as CollectionEntry<CollectionTypes>);

let friendlyCollectionName = props.collection.slice(0, props.collection.length - 1);
friendlyCollectionName = friendlyCollectionName === 'querie' ? 'query' : friendlyCollectionName;
friendlyCollectionName = friendlyCollectionName === 'entitie' ? 'entity' : friendlyCollectionName;
friendlyCollectionName = friendlyCollectionName === 'adr' ? 'architecture decision' : friendlyCollectionName;

const schemasForResource = getSchemasFromResource(props);

const messageCollections = ['events', 'commands', 'queries'];
const printUrl =
  isExportPDFEnabled() && messageCollections.includes(props.collection)
    ? buildUrl(`/docs/print/${props.collection}/${props.data.id}/${props.data.version}`)
    : undefined;

const generatePromptForResource = (props: any) => {
  return `
    Please tell me more about the ${props.collection.slice(0, props.collection.length - 1)} -  ${props.data.name} v${props.data.version} in this catalog.
  `;
};

// Handle node graphs in the markdown
const currentPageNodeGraph = {
  id: props.data.id,
  version: props.data.version,
  type: collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap],
};

let nodeGraphs =
  getMDXComponentsByName(props.body || '', 'NodeGraph')?.map((nodeGraph: any) => {
    const graph = nodeGraph.id === undefined ? { ...currentPageNodeGraph, ...nodeGraph } : nodeGraph;

    return {
      ...graph,
      search: parseMdxBooleanProp(nodeGraph.search, true),
      legend: parseMdxBooleanProp(nodeGraph.legend, true),
    };
  }) || [];
const flowEmbeds = getMDXComponentsByName(props.body || '', 'Flow') || [];

// Get props for the node graph (when no id is passed, we assume its the current page)
const hasCurrentFlowEmbed =
  props.collection === 'flows' &&
  flowEmbeds.some((flowEmbed: any) => {
    const flowId = flowEmbed.id;
    const flowVersion = flowEmbed.version || 'latest';

    return flowId === props.data.id && (flowVersion === 'latest' || flowVersion === props.data.version);
  });
const hasCurrentPageNodeGraph = nodeGraphs.some((nodeGraph: any) => {
  return (
    nodeGraph.id === currentPageNodeGraph.id &&
    nodeGraph.version === currentPageNodeGraph.version &&
    nodeGraph.type === currentPageNodeGraph.type
  );
});

const shouldRenderVersionList =
  shouldRenderSideBarSection(props, 'versions') && props.data.versions && props.data.versions.length > 1;
const httpOperation =
  ['commands', 'queries'].includes(props.collection) && props.data.operation?.path ? props.data.operation : undefined;
const httpMethodColors: Record<string, { background: string; color: string }> = {
  GET: { background: 'rgba(34,197,94,0.15)', color: '#22c55e' },
  POST: { background: 'rgba(59,130,246,0.15)', color: '#3b82f6' },
  PUT: { background: 'rgba(245,158,11,0.15)', color: '#f59e0b' },
  PATCH: { background: 'rgba(245,158,11,0.15)', color: '#f59e0b' },
  DELETE: { background: 'rgba(239,68,68,0.15)', color: '#ef4444' },
  OPTIONS: { background: 'rgba(139,92,246,0.15)', color: '#8b5cf6' },
  HEAD: { background: 'rgba(107,114,128,0.15)', color: '#6b7280' },
};
const httpMethodStyle = httpOperation?.method
  ? httpMethodColors[httpOperation.method.toUpperCase()] || { background: '#6b7280', color: '#ffffff' }
  : undefined;

// This will render the graph for this page. Flow pages can already render their graph explicitly via <Flow />.
if (!isAdrPage && !hasCurrentFlowEmbed && !hasCurrentPageNodeGraph) {
  nodeGraphs.push({
    ...currentPageNodeGraph,
    search: true,
    legend: true,
  });
}
---

<VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
  <main class="flex docs-layout min-h-full bg-[rgb(var(--ec-page-bg))]" {...pagefindAttributes}>
    <div class="flex docs-layout w-full">
      <div class="w-full lg:mr-2 pr-24 py-8 bg-[rgb(var(--ec-page-bg))]">
        <div class="border-b border-[rgb(var(--ec-page-border))] md:pb-6 pt-4">
          <div>
            <div class="flex justify-between items-end">
              <div class="flex flex-col gap-4">
                <span class="text-xs md:text-sm font-semibold text-[rgb(var(--ec-accent))] capitalize">
                  {friendlyCollectionName}
                </span>
                <div class="flex items-center gap-3">
                  {
                    headerIconSrc && (
                      <img
                        src={headerIconSrc}
                        alt={props.data.name}
                        class="w-8 h-8 md:w-10 md:h-10 object-contain rounded-md shrink-0"
                      />
                    )
                  }
                  <div class="flex items-center gap-2">
                    <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">
                      {props.data.name}
                      {props.data.latestVersion !== props.data.version && <span>(v{props.data.version})</span>}
                    </h2>
                    <FavoriteButton
                      client:load
                      nodeKey={`${collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap]}:${props.data.id}:${props.data.version}`}
                      title={props.data.name}
                      badge={collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap]
                        .charAt(0)
                        .toUpperCase() +
                        collectionToResourceMap[props.collection as keyof typeof collectionToResourceMap].slice(1)}
                      href={buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}`)}
                      size="md"
                    />
                  </div>
                </div>
              </div>
              <div class="hidden lg:block mb-2">
                <CopyAsMarkdown
                  client:only="react"
                  schemas={schemasForResource}
                  chatQuery={generatePromptForResource(props)}
                  chatEnabled={isEventCatalogChatEnabled()}
                  markdownDownloadEnabled={isMarkdownDownloadEnabled()}
                  rssFeedEnabled={isRSSEnabled()}
                  editUrl={editUrl}
                  preferChatAsDefault={isEventCatalogChatEnabled()}
                  printUrl={printUrl}
                />
              </div>
            </div>

            <h2 class="text-base pt-4 text-[rgb(var(--ec-page-text-muted))] font-light">{props.data.summary}</h2>
            {
              httpOperation && (
                <div class="pt-4">
                  <div
                    class="inline-flex max-w-full items-start gap-2 rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-content-hover))] px-3 py-2 text-sm text-[rgb(var(--ec-page-text))]"
                    aria-label="HTTP path"
                  >
                    {httpOperation.method && (
                      <span
                        class="mt-0.5 shrink-0 rounded-md px-2 py-0.5 font-mono text-xs font-semibold"
                        style={`background: ${httpMethodStyle?.background}; color: ${httpMethodStyle?.color};`}
                      >
                        {httpOperation.method}
                      </span>
                    )}
                    <code class="break-all font-mono text-[rgb(var(--ec-page-text))]">{httpOperation.path}</code>
                  </div>
                </div>
              )
            }
            {
              badges && (
                <div class="flex flex-wrap gap-3 pt-6 pb-2">
                  {badges.map((badge: any) => {
                    return <Badge badge={badge} />;
                  })}
                </div>
              )
            }

            {
              isSupersededAdr(props) && (
                <Admonition type="warning" title="This architecture decision has been superseded">
                  <div>
                    {supersededByAdrs.length > 0 ? (
                      <p>
                        You are looking at a previous architecture decision. It has been superseded by{' '}
                        {supersededByAdrs.map((adr: any, index: number) => (
                          <>
                            <a class="underline" href={buildUrl(`/docs/adrs/${adr.data.id}/${adr.data.version}`)}>
                              {adr.data.name}
                            </a>
                            {index < supersededByAdrs.length - 1 ? ', ' : '.'}
                          </>
                        ))}
                      </p>
                    ) : (
                      <p>This architecture decision has been marked as superseded.</p>
                    )}
                  </div>
                </Admonition>
              )
            }

            {
              isDeprecatedAdr(props) && (
                <Admonition type="warning" title="This architecture decision has been deprecated">
                  <p>This architecture decision has been marked as deprecated.</p>
                </Admonition>
              )
            }

            {
              isMarkedAsDeprecated && hasDeprecated && (
                <Admonition
                  className="w-full"
                  type="alert"
                  title={formattedDate ? `This ${friendlyCollectionName} was deprecated on ${formattedDate}` : `Deprecated`}
                >
                  <div>
                    {!deprecatedMessage && (
                      <p>
                        The {friendlyCollectionName} has been marked as deprecated
                        {formattedDate && ` on ${formattedDate}`}.
                      </p>
                    )}
                    {deprecatedMessage && <div class="prose prose-sm max-w-none" set:html={marked.parse(deprecatedMessage)} />}
                  </div>
                </Admonition>
              )
            }
            {
              isMarkedAsDeprecated && !hasDeprecated && (
                <Admonition type="warning" title={`This ${friendlyCollectionName} will be deprecated on ${formattedDate}`}>
                  <div>
                    {!deprecatedMessage && (
                      <p>
                        The {friendlyCollectionName} will be deprecated on {formattedDate}.
                      </p>
                    )}
                    {deprecatedMessage && <div class="prose prose-sm max-w-none" set:html={marked.parse(deprecatedMessage)} />}
                  </div>
                </Admonition>
              )
            }

            {
              props.data.draft && (
                <Admonition type="warning" title={props.data?.draft?.title || `Draft ${friendlyCollectionName}`}>
                  <div>
                    {!props.data.draft.message && (
                      <p>This resource has been marked as a draft and may still be under development.</p>
                    )}
                    {props.data.draft.message && (
                      <div class="prose prose-sm max-w-none" set:html={marked.parse(props.data.draft.message)} />
                    )}
                  </div>
                </Admonition>
              )
            }
          </div>
        </div>

        <div data-pagefind-ignore>
          {
            props.data.version !== props.data.latestVersion && (
              <Admonition type="warning" title="New version found">
                <p>
                  You are looking at a previous version of the {props.collection.slice(0, props.collection.length - 1)}{' '}
                  <strong>{props.data.name}</strong>.{' '}
                  <a
                    class="underline block pt-2"
                    href={buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.latestVersion}`)}
                  >
                    The latest version of this {props.collection.slice(0, props.collection.length - 1)} is
                    <span>v{props.data.latestVersion}</span> &rarr;
                  </a>
                </p>
              </Admonition>
            )
          }
        </div>

        <div class="prose prose-md py-4 w-full text-[15px]">
          <Content components={components(props)} />
        </div>
        <div data-pagefind-ignore>
          <!-- @ts-ignore -->
          <!-- <SchemaViewer id={props.data.id} catalog={props.catalog} filePath={props.filePath} /> -->
          <SchemaViewer id={props.data.id} filePath={props.filePath} />

          {
            nodeGraphs.length > 0 &&
              nodeGraphs.map((nodeGraph: any) => {
                const collection = resourceToCollectionMap[nodeGraph.type as keyof typeof resourceToCollectionMap];
                return (
                  <NodeGraph
                    id={nodeGraph.id}
                    version={nodeGraph.version}
                    collection={collection}
                    title={nodeGraph.title}
                    mode={nodeGraph.mode || 'simple'}
                    linksToVisualiser={true}
                    showSearch={nodeGraph.search ?? true}
                    showLegend={nodeGraph.legend ?? true}
                    href={
                      isVisualiserEnabled()
                        ? {
                            label: 'Open in Visualiser',
                            url: buildUrl(`/visualiser/${collection}/${nodeGraph.id}/${nodeGraph.version}`),
                          }
                        : undefined
                    }
                  />
                );
              })
          }
        </div>
        <Footer />
        <!-- Add edit this page button  with icon and text-->
        <div class="flex justify-end">
          <!-- Built with EventCatalog -->
          {
            editUrl && (
              <div class="flex justify-end">
                <a
                  href={editUrl}
                  class="text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] border border-[rgb(var(--ec-page-border))] rounded-md p-2"
                >
                  <SquarePenIcon className="w-4 h-4 inline-block mr-1" />
                  Edit this page
                </a>
              </div>
            )
          }
        </div>
      </div>
      <aside
        id="eventcatalog-docs-sidebar"
        class="hidden xl:block sticky top-[4rem] self-start w-[280px] max-h-[calc(100vh-4rem)] overflow-y-auto py-2 flex-shrink-0 pr-10 bg-[rgb(var(--ec-page-bg))]"
      >
        <div class="mt-8 space-y-8">
          {
            headings.length > 0 && (
              <div>
                <h3 class="text-xs text-[rgb(var(--ec-page-text))] font-semibold group-data-[hover]:text-[rgb(var(--ec-page-text))]/80 capitalize flex items-center gap-2 mb-4">
                  <AlignLeft className="w-4 h-4" />
                  On this page
                </h3>
                <div class="space-y-8">
                  <nav class="text-xs border-l border-[rgb(var(--ec-page-border))]">
                    {headings.map((heading) => {
                      const level = heading.depth > 2 ? heading.depth : 1;
                      if (heading.depth > 3) {
                        return null;
                      }
                      return (
                        <a
                          href={`#${heading.slug}`}
                          class={`block py-1.5 pr-2.5 leading-5 text-[rgb(var(--ec-page-text-muted))] hover:border-[rgb(var(--ec-page-border))] hover:text-[rgb(var(--ec-page-text))] border-l-2 border-transparent -ml-px transition-all duration-200`}
                          style={`padding-left: ${level * 0.75}rem`}
                        >
                          {heading.text}
                        </a>
                      );
                    })}
                  </nav>
                </div>
              </div>
            )
          }
          {
            shouldRenderVersionList && (
              <VersionList
                title={`Versions (${props.data.versions?.length})`}
                versions={props.data.versions}
                collectionItem={props}
              />
            )
          }
        </div>
      </aside>
    </div>

    <style is:global>
      .docs-layout .prose {
        max-width: none;
        overflow: auto;
      }

      .mermaid svg {
        margin: 1em auto 2em;
      }

      /* Fix for architecture diagrams */
      .mermaid[data-content*='architecture'] svg {
        max-width: 350px !important;
        margin: 0;
        /* width: 100px !important; */
      }

      /* TOC active state styles using accent variables */
      .toc-active-text {
        color: rgb(var(--ec-accent));
      }
      .toc-active-border {
        border-color: rgb(var(--ec-accent));
      }
    </style>

    <script define:vars={{ config }}>
      window.eventcatalog = {};
      window.eventcatalog.mermaid = config.mermaid;
    </script>

    <script>
      import { destroyZoomInstances, renderMermaidWithZoom } from '@utils/mermaid-zoom';

      // Listen for Astro transitions
      document.addEventListener('astro:page-load', () => {
        destroyZoomInstances();
        const graphs = document.getElementsByClassName('mermaid');
        if (graphs.length > 0) {
          renderMermaidWithZoom(graphs, window.eventcatalog?.mermaid);
        }
      });

      // Initial render
      const graphs = document.getElementsByClassName('mermaid');
      if (graphs.length > 0) {
        renderMermaidWithZoom(graphs, window.eventcatalog?.mermaid);
      }

      // Make available globally for accordion component
      window.renderDiagrams = (graphs: HTMLCollectionOf<Element>) => {
        renderMermaidWithZoom(graphs, window.eventcatalog?.mermaid);
      };
    </script>

    <script>
      import { renderPlantUMLWithZoom } from '@utils/mermaid-zoom';

      function initPlantUML() {
        const blocks = document.getElementsByClassName('plantuml');
        if (blocks.length > 0) {
          renderPlantUMLWithZoom(blocks);
        }
      }

      // Run on initial load and page transitions
      initPlantUML();
      document.addEventListener('astro:page-load', initPlantUML);

      // Make available globally for accordion component
      window.renderPlantUML = (blocks: HTMLCollectionOf<Element>) => {
        renderPlantUMLWithZoom(blocks);
      };
    </script>
    <script>
      // @ts-nocheck
      function setupObserver() {
        try {
          const observerOptions = {
            rootMargin: '0px 0px -40% 0px',
            threshold: 0.1,
          };

          // Flag to temporarily disable observer after click
          let observerPaused = false;

          // Function to highlight a TOC item
          function highlightTocItem(id) {
            // Remove active class from all links
            document.querySelectorAll('.active-toc-item').forEach((link) => {
              link.classList.remove('active-toc-item', 'toc-active-text', 'font-medium', 'toc-active-border');
              link.classList.add('border-transparent');
            });

            // Add active class to current link
            const tocLink = document.querySelector(`nav a[href="#${id}"]`);
            if (tocLink) {
              tocLink.classList.add('active-toc-item', 'toc-active-text', 'font-medium', 'toc-active-border');
              tocLink.classList.remove('border-transparent');

              // Scroll the highlighted item into view with a small delay to ensure DOM updates first
              setTimeout(() => {
                tocLink.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
              }, 10);
            }
          }

          // Set up the intersection observer for scrolling
          const observer = new IntersectionObserver((entries) => {
            // If observer is paused, don't process entries
            if (observerPaused) return;

            entries.forEach((entry) => {
              try {
                const id = entry.target.getAttribute('id');
                if (entry.isIntersecting && id) {
                  highlightTocItem(id);
                }
              } catch (entryError) {
                console.error('Error processing intersection entry:', entryError);
              }
            });
          }, observerOptions);

          // Find all headings in the content area within the .prose container to observe
          const prose = document.querySelector('.prose');
          if (!prose) {
            console.warn('No .prose container found for TOC highlighting');
            return;
          }

          // First try to find headings with IDs
          const headings = prose.querySelectorAll('h1[id], h2[id], h3[id]');

          if (headings.length > 0) {
            headings.forEach((heading) => {
              observer.observe(heading);
            });
          } else {
            // Fallback: If no headings with IDs found, attach IDs to them
            const allHeadings = prose.querySelectorAll('h1, h2, h3');

            allHeadings.forEach((heading) => {
              // Only add ID if it doesn't exist
              if (!heading.id) {
                const text = heading.textContent || '';
                const slug = text
                  .toLowerCase()
                  .replace(/[^\w\s-]/g, '')
                  .replace(/\s+/g, '-');
                heading.id = slug;
              }
              observer.observe(heading);
            });
          }

          // Add click event listeners to all TOC links
          const tocLinks = document.querySelectorAll('nav a[href^="#"]');
          tocLinks.forEach((link) => {
            link.addEventListener('click', (e) => {
              // Get the ID from the href attribute
              const hrefAttr = link.getAttribute('href');
              if (!hrefAttr) return;

              const id = hrefAttr.substring(1);

              // Highlight the clicked item
              highlightTocItem(id);

              // Temporarily pause the observer to prevent immediate highlighting changes
              observerPaused = true;

              // Resume the observer after a delay (1.5 seconds)
              setTimeout(() => {
                observerPaused = false;
              }, 500);
            });
          });
        } catch (error) {
          console.error('Error setting up TOC highlighting:', error);
        }
      }

      setupObserver();

      document.addEventListener('astro:page-load', setupObserver);
    </script>
  </main>
</VerticalSideBarLayout>
