import { Link, Style } from "@solidjs/meta";
import { createAsync } from "@solidjs/router";
import { Component, For, Show, createMemo, useContext } from "solid-js";
import { Font, FontFace } from "../../../../../types";
import { getSystem } from "../../../../data/system";
import { StateContext } from "../../../../lib/context/page";
import { urlJoin } from "#utils/url/urlJoin";

export const defaultFallbacks = [
  `ui-sans-serif`,
  `system-ui`,
  `-apple-system`,
  `BlinkMacSystemFont`,
  `'Segoe UI'`,
  `Roboto`,
  `'Helvetica Neue'`,
  `Arial`,
  `'Noto Sans'`,
  `sans-serif`,
  `'Apple Color Emoji'`,
  `'Segoe UI Emoji'`,
  `'Segoe UI Symbol'`,
  `'Noto Color Emoji'`,
];

const CDN_URL = "https://api.fonts.coollabs.io";

export const Fonts: Component<{ defaultFont?: boolean }> = function (props) {
  const system = createAsync(() => getSystem(), { deferStream: true });
  const [state] = useContext(StateContext)!;
  const fontData = createMemo(function () {
    const mySystem = system();
    if (!mySystem) {
      return;
    }

    const data = {
      familyCss: "",
      preloads: [] as { mime: string; url: string }[],
      cdnFonts: [] as string[],
    };

    const fallBackUrl = mySystem.fs.urls.local ?? "/uploads";

    const fontFamily = function (font: Font) {
      const familyName = font.family || font.publicId;

      if (font.cdn && font.family) {
        data.cdnFonts.push(font.family);
      }

      const face = function (face: FontFace) {
        let preloadRegistered = false;
        const src = face.files
          .reduce((acc, v) => {
            const url = urlJoin(
              mySystem.fs.urls[font.driver || "local"] ?? fallBackUrl,
              "fonts",
              font.publicId,
              v.name,
            );

            if (!preloadRegistered && (font.preload || face.preload)) {
              data.preloads.push({
                url,
                mime: v.mime,
              });
              preloadRegistered = true;
            }

            acc.push(`url("${url}") format("${v.format}")`);
            return acc;
          }, [] as string[])
          .join(",");

        return `
@font-face {
  font-family: '${familyName}';
  src: ${src};
  ${face.weight != null ? `font-weight: ${face.weight};` : ""}
  font-style: ${face.style || "normal"};
  font-display: ${font.preload || face.preload ? "block" : "swap"};
}`;
      };

      return font.faces.map(face).join("\n") + "\n";
    };

    data["familyCss"] = state.fonts.map(fontFamily).join("\n");

    return data;
  });
  const fontCss = createMemo(function () {
    if (!state.fonts.length) {
      return;
    }

    return `

    ${fontData()?.familyCss}
    ${
      props.defaultFont !== false && state.defaultFont
        ? `html { font-family: var(--font-${state.defaultFont})}`
        : ""
    }

    :root {
      ${state.fonts
        .map((f, i) => {
          const familyName = f.family || f.publicId;
          const fallbacks = f.fallbacks[0] ? f.fallbacks : defaultFallbacks;
          let family = `'${familyName}'`;
          if (fallbacks.length) {
            family += "," + fallbacks.join(",");
          }

          return `--font-${f.publicId}: ${family};`;
        })
        .join("\n")}
    }
    
`;
  });

  return (
    <Show when={system()}>
      <Show when={fontData()?.cdnFonts.length}>
        <Link rel="preconnect" href={CDN_URL} crossorigin="anonymous" />
        <For each={fontData()?.cdnFonts}>
          {(family) => {
            return (
              <Link
                href={
                  CDN_URL +
                  "/css2?" +
                  new URLSearchParams({
                    display: "swap",
                    family,
                  }).toString() +
                  ":ital,wght@0,100..900;1,100..900"
                }
                rel="stylesheet"
              />
            );
          }}
        </For>
      </Show>
      <For each={fontData()?.preloads}>
        {(f) => {
          return (
            <Link
              rel="preload"
              href={f.url}
              as="font"
              type={f.mime}
              crossorigin="anonymous"
            />
          );
        }}
      </For>
      <Style class={!state.admin ? "nitro-page-style" : undefined}>
        {fontCss()}
      </Style>
    </Show>
  );
};
