/*
 * Editorial markdown components.
 *
 * All components use CSS variables from globals.css (no prefix).
 * Conflicting names with shadcn: --brand-primary, --brand-secondary,
 * --link-accent, --page-border.
 */

import { useCallback, useEffect, useRef, useState } from "react";
import Prism from "prismjs";
import "prismjs/components/prism-jsx";
import "prismjs/components/prism-tsx";
import "prismjs/components/prism-bash";

/* =========================================================================
   TOC sidebar (fixed left)
   ========================================================================= */

function useActiveTocId() {
  const [activeId, setActiveId] = useState("");

  useEffect(() => {
    const headings = document.querySelectorAll<HTMLElement>("h1[id]");
    if (headings.length === 0) {
      return;
    }

    const observer = new IntersectionObserver(
      (entries) => {
        const visible: string[] = [];
        entries.forEach((entry) => {
          if (entry.isIntersecting && entry.target.id) {
            visible.push(entry.target.id);
          }
        });

        if (visible.length > 0) {
          const sorted = visible.sort((a, b) => {
            const elA = document.getElementById(a);
            const elB = document.getElementById(b);
            if (!elA || !elB) {
              return 0;
            }
            return elA.getBoundingClientRect().top - elB.getBoundingClientRect().top;
          });
          setActiveId(sorted[sorted.length - 1]);
        }
      },
      {
        rootMargin: "-80px 0px -75% 0px",
        threshold: 0,
      },
    );

    headings.forEach((heading) => {
      observer.observe(heading);
    });

    return () => {
      observer.disconnect();
    };
  }, []);

  return activeId;
}

export function TableOfContents({
  items,
  logo,
}: {
  items: Array<{ label: string; href: string }>;
  logo?: string;
}) {
  const activeId = useActiveTocId();

  return (
    <aside
      className="fixed top-[80px] hidden lg:block"
      style={{ left: "max(1rem, calc((100vw - 550px) / 2 - 200px))", width: "122px" }}
    >
      <nav>
        <a
          href="/"
          className="no-underline transition-colors block"
          style={{
            fontSize: "14px",
            fontWeight: 700,
            lineHeight: "20px",
            letterSpacing: "-0.09px",
            padding: "4px 0",
            color: "var(--text-primary)",
            fontFamily: "var(--font-primary)",
            marginBottom: "8px",
          }}
          onMouseEnter={(e) => {
            e.currentTarget.style.color = "var(--text-hover)";
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.color = "var(--text-primary)";
          }}
        >
          {logo ?? "index"}
        </a>
        {items.map((item) => {
          const isActive = `#${activeId}` === item.href;
          const defaultColor = isActive ? "var(--text-primary)" : "var(--text-secondary)";
          return (
            <a
              key={item.href}
              href={item.href}
              className="block no-underline"
              style={{
                fontSize: "13px",
                fontWeight: 475,
                lineHeight: "15.6px",
                letterSpacing: "-0.04px",
                padding: "5px 0",
                color: defaultColor,
                fontFamily: "var(--font-primary)",
                transition: "color 0.15s ease",
              }}
              onMouseEnter={(e) => {
                if (!isActive) {
                  e.currentTarget.style.color = "var(--text-hover)";
                }
              }}
              onMouseLeave={(e) => {
                e.currentTarget.style.color = defaultColor;
              }}
            >
              {item.label}
            </a>
          );
        })}
      </nav>
    </aside>
  );
}

/* =========================================================================
   Back button (fixed top-right)
   ========================================================================= */

export function BackButton() {
  return (
    <a
      href="/"
      className="fixed top-5 right-5 z-[100000] flex items-center justify-center w-10 h-10 rounded-full no-underline"
      style={{
        background: "var(--btn-bg)",
        color: "var(--text-secondary)",
        boxShadow: "var(--btn-shadow)",
        transition: "color 0.15s, transform 0.15s",
      }}
      onMouseEnter={(e) => {
        e.currentTarget.style.color = "var(--text-hover)";
        e.currentTarget.style.transform = "scale(1.05)";
      }}
      onMouseLeave={(e) => {
        e.currentTarget.style.color = "var(--text-secondary)";
        e.currentTarget.style.transform = "scale(1)";
      }}
      onMouseDown={(e) => {
        e.currentTarget.style.transform = "scale(0.95)";
      }}
      onMouseUp={(e) => {
        e.currentTarget.style.transform = "scale(1.05)";
      }}
    >
      <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
        <path
          d="M12.25 7H1.75M1.75 7L6.125 2.625M1.75 7L6.125 11.375"
          stroke="currentColor"
          strokeWidth="1.5"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </svg>
    </a>
  );
}

/* =========================================================================
   Typography
   ========================================================================= */

export function SectionHeading({ id, children }: { id: string; children: React.ReactNode }) {
  return (
    <h1
      id={id}
      className="scroll-mt-[5.25rem]"
      style={{
        fontFamily: "var(--font-primary)",
        fontSize: "14px",
        fontWeight: 560,
        lineHeight: "20px",
        letterSpacing: "-0.09px",
        color: "var(--text-primary)",
        margin: 0,
        padding: 0,
        display: "flex",
        alignItems: "center",
        gap: "12px",
        paddingTop: "24px",
        paddingBottom: "24px",
      }}
    >
      <span style={{ whiteSpace: "nowrap" }}>{children}</span>
      <span style={{ flex: 1, height: "1px", background: "var(--divider)" }} />
    </h1>
  );
}

export function P({ children, className = "" }: { children: React.ReactNode; className?: string }) {
  return (
    <p
      className={`editorial-prose ${className}`}
      style={{
        fontFamily: "var(--font-primary)",
        fontSize: "14px",
        fontWeight: 475,
        lineHeight: "22px",
        letterSpacing: "-0.09px",
        color: "var(--text-primary)",
        opacity: 0.82,
        margin: 0,
      }}
    >
      {children}
    </p>
  );
}

export function Caption({ children }: { children: React.ReactNode }) {
  return (
    <p
      style={{
        fontFamily: "var(--font-primary)",
        fontSize: "12px",
        fontWeight: 475,
        textAlign: "center",
        lineHeight: "20px",
        letterSpacing: "-0.09px",
        color: "var(--text-secondary)",
        margin: 0,
      }}
    >
      {children}
    </p>
  );
}

export function A({ href, children }: { href: string; children: React.ReactNode }) {
  return (
    <a
      href={href}
      target="_blank"
      rel="noopener noreferrer"
      style={{
        color: "var(--link-accent, #0969da)",
        fontWeight: 600,
        textDecoration: "none",
      }}
      onMouseEnter={(e) => {
        e.currentTarget.style.textDecoration = "underline";
      }}
      onMouseLeave={(e) => {
        e.currentTarget.style.textDecoration = "none";
      }}
    >
      {children}
    </a>
  );
}

export function Code({ children }: { children: React.ReactNode }) {
  return (
    <code className="inline-code">
      {children}
    </code>
  );
}

/* =========================================================================
   Layout
   ========================================================================= */

export function Divider() {
  return (
    <div style={{ padding: "24px 0", display: "flex", alignItems: "center" }}>
      <div style={{ height: "1px", background: "var(--divider)", flex: 1 }} />
    </div>
  );
}

export function Section({ id, title, children }: { id: string; title: string; children: React.ReactNode }) {
  return (
    <>
      <SectionHeading id={id}>{title}</SectionHeading>
      {children}
    </>
  );
}

export function OL({ children }: { children: React.ReactNode }) {
  return (
    <ol
      className="m-0 pl-5"
      style={{
        fontFamily: "var(--font-primary)",
        fontSize: "14px",
        fontWeight: 475,
        lineHeight: "20px",
        letterSpacing: "-0.09px",
        color: "var(--text-primary)",
        listStyleType: "decimal",
      }}
    >
      {children}
    </ol>
  );
}

export function List({ children }: { children: React.ReactNode }) {
  return (
    <ul
      className="m-0 pl-5"
      style={{
        fontFamily: "var(--font-primary)",
        fontSize: "14px",
        fontWeight: 475,
        lineHeight: "20px",
        letterSpacing: "-0.09px",
        color: "var(--text-primary)",
        listStyleType: "disc",
      }}
    >
      {children}
    </ul>
  );
}

export function Li({ children }: { children: React.ReactNode }) {
  return <li style={{ padding: "0 0 8px 12px" }}>{children}</li>;
}

/* =========================================================================
   Code block with Prism syntax highlighting and line numbers
   ========================================================================= */

export function CodeBlock({ children, lang = "jsx", lineHeight = "1.85" }: { children: string; lang?: string; lineHeight?: string }) {
  const codeRef = useRef<HTMLElement>(null);
  const lines = children.split("\n");

  useEffect(() => {
    if (codeRef.current) {
      Prism.highlightElement(codeRef.current);
    }
  }, [children]);

  return (
    <figure className="m-0 bleed">
      <div className="relative">
        <pre
          className="overflow-x-auto"
          style={{
            // background: "var(--code-bg)",
            borderRadius: "8px",
            margin: 0,
            padding: 0,
          }}
        >
          <div
            className="flex"
            style={{
              padding: "12px 8px 8px",
              fontFamily: "var(--font-code)",
              fontSize: "12px",
              fontWeight: 400,
              lineHeight,
              letterSpacing: "normal",
              color: "var(--text-primary)",
              tabSize: 2,
            }}
          >
            <span
              className="select-none shrink-0"
              aria-hidden="true"
              style={{
                color: "var(--code-line-nr)",
                textAlign: "right",
                paddingRight: "20px",
                width: "36px",
                userSelect: "none",
              }}
            >
              {lines.map((_, i) => {
                return (
                  <span key={i} className="block">
                    {i + 1}
                  </span>
                );
              })}
            </span>
            <code
              ref={codeRef}
              className={`language-${lang}`}
              style={{ whiteSpace: "pre", background: "none", padding: 0 }}
            >
              {children}
            </code>
          </div>
        </pre>
      </div>
    </figure>
  );
}

/* =========================================================================
   Pixelated placeholder image
   Uses a tiny pre-generated image with CSS image-rendering: pixelated
   (nearest-neighbor / point sampling in GPU terms) for a crisp mosaic
   effect. The real image fades in on top once loaded — no flash because
   the placeholder stays underneath and the real image starts at opacity 0.
   ========================================================================= */

export function PixelatedImage({
  src,
  placeholder,
  alt,
  width,
  height,
  className = "",
  style,
}: {
  src: string;
  /**
   * URL of the tiny pixelated placeholder image. Use a static import so Vite
   * inlines it as a base64 data URI (all placeholders are < 4KB, well under
   * Vite's default assetsInlineLimit of 4096 bytes). This makes the
   * placeholder available synchronously on first render with zero HTTP
   * requests. Do NOT use dynamic imports or public/ paths — dynamic imports
   * add a microtask delay, and public/ files bypass Vite's asset pipeline.
   *
   * @example
   * ```tsx
   * import placeholderScreenshot from "../assets/placeholders/placeholder-screenshot@2x.png";
   * <PixelatedImage placeholder={placeholderScreenshot} src="/screenshot@2x.png" ... />
   * ```
   */
  placeholder: string;
  alt: string;
  width: number;
  height: number;
  className?: string;
  style?: React.CSSProperties;
}) {
  const [loaded, setLoaded] = useState(false);

  // Handles both the normal onLoad event and the case where the image is
  // already cached (img.complete is true before React mounts the handler).
  const imgRef = useCallback((img: HTMLImageElement | null) => {
    if (img?.complete && img.naturalWidth > 0) {
      setLoaded(true);
    }
  }, []);

  return (
    <div
      className={className}
      style={{
        position: "relative",
        width: "100%",
        maxWidth: `${width}px`,
        aspectRatio: `${width} / ${height}`,
        overflow: "hidden",
        ...style,
      }}
    >
      {/* Placeholder: tiny image rendered with nearest-neighbor sampling */}
      <img
        src={placeholder}
        alt=""
        aria-hidden
        width={width}
        height={height}
        style={{
          position: "absolute",
          inset: 0,
          width: "100%",
          height: "100%",
          objectFit: "cover",
          imageRendering: "pixelated",
          zIndex: 0,
        }}
      />
      {/* Real image: starts invisible, fades in over the placeholder */}
      <img
        ref={imgRef}
        src={src}
        alt={alt}
        width={width}
        height={height}
        onLoad={() => {
          setLoaded(true);
        }}
        style={{
          position: "relative",
          width: "100%",
          height: "100%",
          objectFit: "cover",
          opacity: loaded ? 1 : 0,
          transition: "opacity 0.4s ease",
          zIndex: 1,
        }}
      />
    </div>
  );
}

/* =========================================================================
   Lazy video with pixelated poster placeholder
   Same visual pattern as PixelatedImage but for <video> elements.
   Poster layers (pixelated → real) show through the transparent video element.
   Video uses native loading="lazy" + preload="none" so zero bytes are
   downloaded until the element is near the viewport and the user clicks play.
   No custom IntersectionObserver needed — all native HTML attributes.
   ========================================================================= */

export function LazyVideo({
  src,
  poster,
  placeholderPoster,
  width,
  height,
  type = "video/mp4",
  className = "",
  style,
}: {
  src: string;
  poster: string;
  /**
   * URL of the tiny pixelated poster placeholder. Use a static import so Vite
   * inlines it as a base64 data URI (all placeholders are < 4KB, well under
   * Vite's default assetsInlineLimit of 4096 bytes). This makes the
   * placeholder available synchronously on first render with zero HTTP
   * requests. Do NOT use dynamic imports or public/ paths — dynamic imports
   * add a microtask delay, and public/ files bypass Vite's asset pipeline.
   *
   * @example
   * ```tsx
   * import placeholderPoster from "../assets/placeholders/placeholder-demo-poster.png";
   * <LazyVideo placeholderPoster={placeholderPoster} poster="/demo-poster.png" ... />
   * ```
   */
  placeholderPoster: string;
  width: number;
  height: number;
  type?: string;
  className?: string;
  style?: React.CSSProperties;
}) {
  const [posterLoaded, setPosterLoaded] = useState(false);

  // Handles cached poster images (same pattern as PixelatedImage)
  const posterRef = useCallback((img: HTMLImageElement | null) => {
    if (img?.complete && img.naturalWidth > 0) {
      setPosterLoaded(true);
    }
  }, []);

  return (
    <div
      className={className}
      style={{
        position: "relative",
        width: "100%",
        maxWidth: `${width}px`,
        aspectRatio: `${width} / ${height}`,
        overflow: "hidden",
        ...style,
      }}
    >
      {/* Pixelated poster placeholder: loads instantly (~500 bytes) */}
      <img
        src={placeholderPoster}
        alt=""
        aria-hidden
        width={width}
        height={height}
        style={{
          position: "absolute",
          inset: 0,
          width: "100%",
          height: "100%",
          objectFit: "cover",
          imageRendering: "pixelated",
          zIndex: 0,
        }}
      />
      {/* Real poster: fades in over the pixelated placeholder */}
      <img
        ref={posterRef}
        src={poster}
        alt=""
        aria-hidden
        width={width}
        height={height}
        onLoad={() => {
          setPosterLoaded(true);
        }}
        style={{
          position: "absolute",
          inset: 0,
          width: "100%",
          height: "100%",
          objectFit: "cover",
          opacity: posterLoaded ? 1 : 0,
          transition: "opacity 0.4s ease",
          zIndex: 1,
        }}
      />
      {/* Video: transparent until playing, native lazy + no preload.
          Controls float on top of poster layers. No poster attr needed
          because the img layers handle the visual placeholder.
          loading="lazy" is a newer HTML attr not yet in React's TS types. */}
      <video
        controls
        preload="none"
        {...{ loading: "lazy" } as React.VideoHTMLAttributes<HTMLVideoElement>}
        width={width}
        height={height}
        style={{
          position: "relative",
          width: "100%",
          height: "100%",
          objectFit: "cover",
          zIndex: 2,
          background: "transparent",
        }}
      >
        <source src={src} type={type} />
      </video>
    </div>
  );
}

/* =========================================================================
   Chart placeholder (dark box with animated line)
   ========================================================================= */

export function ChartPlaceholder({ height = 200, label }: { height?: number; label?: string }) {
  return (
    <div className="bleed">
      <div
        className="w-full overflow-hidden relative"
        style={{
          height: `${height}px`,
          background: "rgb(17, 17, 17)",
        }}
      >
        <svg
          viewBox="0 0 550 200"
          className="absolute inset-0 w-full h-full"
          preserveAspectRatio="none"
        >
          <defs>
            <linearGradient id="chartFill" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor="#3b82f6" stopOpacity="0.3" />
              <stop offset="100%" stopColor="#3b82f6" stopOpacity="0" />
            </linearGradient>
          </defs>
          <path
            d="M0,140 C30,135 60,120 90,125 C120,130 150,100 180,95 C210,90 240,110 270,105 C300,100 330,80 360,85 C390,90 420,70 450,65 C480,60 510,75 550,60"
            fill="none"
            stroke="#3b82f6"
            strokeWidth="2"
          />
          <path
            d="M0,140 C30,135 60,120 90,125 C120,130 150,100 180,95 C210,90 240,110 270,105 C300,100 330,80 360,85 C390,90 420,70 450,65 C480,60 510,75 550,60 L550,200 L0,200 Z"
            fill="url(#chartFill)"
          />
          <circle cx="550" cy="60" r="4" fill="#3b82f6">
            <animate attributeName="r" values="4;6;4" dur="2s" repeatCount="indefinite" />
            <animate attributeName="opacity" values="1;0.6;1" dur="2s" repeatCount="indefinite" />
          </circle>
        </svg>
        {label && (
          <div
            className="absolute top-3 right-3 px-2 py-1 rounded text-xs"
            style={{
              background: "rgba(59, 130, 246, 0.15)",
              color: "#3b82f6",
              fontFamily: "var(--font-code)",
              fontWeight: 475,
              fontSize: "11px",
            }}
          >
            {label}
          </div>
        )}
      </div>
    </div>
  );
}

/* =========================================================================
   Comparison table
   ========================================================================= */

export function ComparisonTable({
  title,
  headers,
  rows,
}: {
  title?: string;
  headers: [string, string, string];
  rows: Array<[string, string, string]>;
}) {
  return (
    <div className="w-full max-w-full overflow-x-auto" style={{ padding: "8px 0" }}>
      {title && (
        <div
          style={{
            fontFamily: "var(--font-primary)",
            fontSize: "11px",
            fontWeight: 400,
            color: "var(--text-muted)",
            textTransform: "uppercase",
            letterSpacing: "0.02em",
            padding: "0 0 6px",
          }}
        >
          {title}
        </div>
      )}
      <table
        className="w-full"
        style={{
          borderSpacing: 0,
          borderCollapse: "collapse",
        }}
      >
        <thead>
          <tr>
            {headers.map((header) => {
              return (
                <th
                  key={header}
                  className="text-left"
                  style={{
                    padding: "4.8px 12px 4.8px 0",
                    fontSize: "11px",
                    fontWeight: 400,
                    fontFamily: "var(--font-primary)",
                    color: "var(--text-muted)",
                    borderBottom: "1px solid var(--page-border)",
                  }}
                >
                  {header}
                </th>
              );
            })}
          </tr>
        </thead>
        <tbody>
          {rows.map(([feature, them, us]) => {
            return (
              <tr key={feature}>
                <td
                  style={{
                    padding: "4.8px 12px 4.8px 0",
                    fontSize: "11px",
                    fontWeight: 500,
                    fontFamily: "var(--font-code)",
                    color: "var(--text-primary)",
                    borderBottom: "1px solid var(--page-border)",
                    whiteSpace: "nowrap",
                  }}
                >
                  {feature}
                </td>
                <td
                  style={{
                    padding: "4.8px 12px 4.8px 0",
                    fontSize: "11px",
                    fontWeight: 500,
                    fontFamily: "var(--font-code)",
                    color: "var(--text-primary)",
                    borderBottom: "1px solid var(--page-border)",
                    whiteSpace: "nowrap",
                  }}
                >
                  {them}
                </td>
                <td
                  style={{
                    padding: "4.8px 12px 4.8px 0",
                    fontSize: "11px",
                    fontWeight: 500,
                    fontFamily: "var(--font-code)",
                    color: "var(--text-primary)",
                    borderBottom: "1px solid var(--page-border)",
                  }}
                >
                  {us}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/* =========================================================================
   Page shell — wraps content with layout, TOC, back button, animations
   ========================================================================= */

export function EditorialPage({
  toc,
  logo,
  children,
}: {
  toc: Array<{ label: string; href: string }>;
  logo?: string;
  children: React.ReactNode;
}) {
  return (
    <div
      className="editorial-page relative min-h-screen overflow-x-hidden"
      style={{
        background: "var(--bg)",
        color: "var(--text-primary)",
        fontFamily: "var(--font-primary)",
        WebkitFontSmoothing: "antialiased",
        textRendering: "optimizeLegibility",
      }}
    >
      <TableOfContents items={toc} logo={logo} />

      <div
        className="mx-auto"
        style={{ width: "550px", maxWidth: "calc(100% - 2rem)", padding: "0 1rem 6rem" }}
      >
        <div style={{ height: "80px" }} />

        <article className="editorial-article flex flex-col gap-[32px]">
          {children}
        </article>
      </div>
    </div>
  );
}
