---
import Footer from '../components/Footer.astro';
import Header from '../components/Header.astro';
import Analytics from '../components/Analytics.astro';
import {
  THEME_BG,
  absoluteUrl,
  normalizeTrailingSlash,
  optimizeOgImage,
  site,
} from '../lib/site';
import defaultOg from '../assets/og-default.jpg';
import themeInit from '../scripts/theme-init.js?raw';
import '../styles/global.css';

interface ArticleMeta {
  publishedTime: string;
  modifiedTime?: string;
  tags?: readonly string[];
}

interface Props {
  title?: string;
  description?: string;
  canonicalPath?: string;
  ogImage?: string;
  ogImageAlt?: string;
  ogImageWidth?: number;
  ogImageHeight?: number;
  ogType?: 'website' | 'article' | 'profile';
  article?: ArticleMeta;
  jsonLd?: Record<string, unknown> | Array<Record<string, unknown>>;
  noindex?: boolean;
  preloadMono?: boolean;
}

const {
  title = site.title,
  description = site.description,
  canonicalPath: rawCanonicalPath = Astro.url.pathname,
  ogImage,
  ogImageAlt = "Andras Schmelczer's personal site",
  ogImageWidth,
  ogImageHeight,
  ogType = 'website',
  article,
  jsonLd,
  noindex = false,
  preloadMono = false,
} = Astro.props;

const isRoot = title === site.title;
const pageTitle = isRoot ? site.title : `${title} · ${site.name}`;
const ogTitle = isRoot ? site.title : title;
const canonical = absoluteUrl(normalizeTrailingSlash(rawCanonicalPath));

let resolvedOgImage = ogImage;
let resolvedOgWidth = ogImageWidth ?? 1200;
let resolvedOgHeight = ogImageHeight ?? 630;

if (!resolvedOgImage) {
  const generated = await optimizeOgImage(defaultOg);
  resolvedOgImage = generated.src;
  resolvedOgWidth = 1200;
  resolvedOgHeight = 630;
}

const ogImageUrl = resolvedOgImage.startsWith('http')
  ? resolvedOgImage
  : absoluteUrl(resolvedOgImage);
const ogImageExt = ogImageUrl
  .match(/\.(png|webp|gif|svg|jpe?g)(?:\?|$)/i)?.[1]
  ?.toLowerCase();
const ogImageType =
  ogImageExt === 'png'
    ? 'image/png'
    : ogImageExt === 'webp'
      ? 'image/webp'
      : ogImageExt === 'gif'
        ? 'image/gif'
        : ogImageExt === 'svg'
          ? 'image/svg+xml'
          : 'image/jpeg';
// theme-init.js is inlined raw (it must run before paint), so it can't import
// the theme colors; they're substituted into its placeholder here instead.
const themeInitScript = themeInit.replace('__THEME_BG__', JSON.stringify(THEME_BG));
const jsonLdEntries = jsonLd ? (Array.isArray(jsonLd) ? jsonLd : [jsonLd]) : [];
const jsonLdStrings = jsonLdEntries.map((entry) =>
  JSON.stringify(entry).replace(/</g, '\\u003c')
);
---

<!doctype html>
<html lang="en" class="no-js">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width,initial-scale=1,viewport-fit=cover"
    />
    <title>{pageTitle}</title>
    <meta name="description" content={description} />
    <meta name="author" content={site.name} />
    <meta name="color-scheme" content="light dark" />
    {noindex && <meta name="robots" content="noindex,follow" />}
    {!noindex && <link rel="canonical" href={canonical} />}
    <meta
      name="theme-color"
      content={THEME_BG.light}
      media="(prefers-color-scheme: light)"
    />
    <meta
      name="theme-color"
      content={THEME_BG.dark}
      media="(prefers-color-scheme: dark)"
    />
    <script is:inline data-theme-script set:html={themeInitScript} />
    <link
      rel="preload"
      href="/fonts/source-sans-3-latin-variable.woff2"
      as="font"
      type="font/woff2"
      crossorigin
    />
    {
      preloadMono && (
        <link
          rel="preload"
          href="/fonts/ibm-plex-mono-latin-400.woff2"
          as="font"
          type="font/woff2"
          crossorigin
        />
      )
    }
    <link
      rel="alternate"
      type="application/rss+xml"
      title={`${site.name} RSS`}
      href="/rss.xml"
    />
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
    <link rel="icon" href="/favicon.ico" sizes="any" />
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
    <link rel="manifest" href="/site.webmanifest" />
    <meta property="og:site_name" content={site.name} />
    <meta property="og:title" content={ogTitle} />
    <meta property="og:description" content={description} />
    <meta property="og:url" content={canonical} />
    <meta property="og:image" content={ogImageUrl} />
    <meta property="og:image:type" content={ogImageType} />
    <meta property="og:image:alt" content={ogImageAlt} />
    <meta property="og:image:width" content={String(resolvedOgWidth)} />
    <meta property="og:image:height" content={String(resolvedOgHeight)} />
    <meta property="og:type" content={ogType} />
    <meta property="og:locale" content="en_US" />
    {
      article && (
        <>
          <meta property="article:published_time" content={article.publishedTime} />
          {article.modifiedTime && (
            <meta property="article:modified_time" content={article.modifiedTime} />
          )}
          <meta property="article:author" content={absoluteUrl('/about/')} />
          {article.tags?.map((tag) => (
            <meta property="article:tag" content={tag} />
          ))}
        </>
      )
    }
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content={ogTitle} />
    <meta name="twitter:description" content={description} />
    <meta name="twitter:image" content={ogImageUrl} />
    <meta name="twitter:image:alt" content={ogImageAlt} />
    {
      jsonLdStrings.map((jsonLdString) => (
        <script is:inline type="application/ld+json" set:html={jsonLdString} />
      ))
    }
    <Analytics />
  </head>
  <body>
    <Header />
    <main id="content">
      <slot />
    </main>
    <Footer />
  </body>
</html>
