import type { CSSProperties } from "react";
import { mediaUrl } from "@/lib/media/urls";
import type { Section, SectionBackground, SectionBorder } from "@/lib/blocks/layout";
import type { RenderContext } from "./types";

const col = (k: string) => `var(--color-${k})`;
const tint = (k: string, pct: number) => `color-mix(in srgb, ${col(k)} ${pct}%, transparent)`;

/** Relative luminance of a #rrggbb colour (0 dark … 1 light). */
function luminance(hex: string | undefined): number | null {
  if (!hex || !/^#[0-9a-f]{6}$/i.test(hex)) return null;
  const c = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255).map((v) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4));
  const [r = 0, g = 0, b = 0] = c;
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

/** True when text on this background should be light. Unknown (e.g. an image without overlay) keeps the page's normal text colour. */
export function isDarkBackground(bg: SectionBackground, palette: Record<string, string> | undefined): boolean {
  if (bg.type === "image" || bg.type === "video") return bg.overlay >= 40;
  const base = luminance(palette?.[bg.colour]);
  if (bg.type === "gradient") {
    const other = luminance(palette?.[bg.colour2]);
    return base !== null && other !== null && (base + other) / 2 < 0.4;
  }
  return base !== null && base < 0.4;
}

function patternStyle(bg: SectionBackground): CSSProperties {
  const c = tint(bg.colour2, 35);
  const base = { backgroundColor: col(bg.colour) };
  switch (bg.pattern) {
    case "grid": return { ...base, backgroundImage: `linear-gradient(${c} 1px, transparent 1px), linear-gradient(90deg, ${c} 1px, transparent 1px)`, backgroundSize: "28px 28px" };
    case "stripes": return { ...base, backgroundImage: `repeating-linear-gradient(90deg, ${c} 0 2px, transparent 2px 16px)` };
    case "diagonal": return { ...base, backgroundImage: `repeating-linear-gradient(45deg, ${c} 0 2px, transparent 2px 14px)` };
    case "checks": return { ...base, backgroundImage: `conic-gradient(${c} 25%, transparent 0 50%, ${c} 0 75%, transparent 0)`, backgroundSize: "32px 32px" };
    default: return { ...base, backgroundImage: `radial-gradient(${c} 2px, transparent 2px)`, backgroundSize: "22px 22px" };
  }
}

/** Decorative layer behind a section's content. */
export function SectionBackgroundLayer({ bg, ctx }: { bg: SectionBackground; ctx: RenderContext }) {
  let inner: React.ReactNode = null;
  if (bg.type === "colour") inner = <div className="absolute inset-0" style={{ backgroundColor: col(bg.colour) }} />;
  else if (bg.type === "gradient") inner = <div className="absolute inset-0" style={{ backgroundImage: `linear-gradient(${bg.direction}, ${col(bg.colour)}, ${col(bg.colour2)})` }} />;
  else if (bg.type === "pattern") inner = <div className="absolute inset-0" style={patternStyle(bg)} />;
  else if (bg.type === "image") {
    const m = bg.imageId ? ctx.media.get(bg.imageId) : undefined;
    // Missing or private images fall back to the colour so the section never turns see-through by accident.
    inner = <div className="absolute inset-0 bg-cover bg-center" style={m ? { backgroundImage: `url("${mediaUrl(m.id)}")`, backgroundAttachment: bg.fixed ? "fixed" : undefined } : { backgroundColor: col(bg.colour) }} />;
  } else if (bg.type === "video") {
    inner = bg.videoUrl ? (
      <video className="absolute inset-0 h-full w-full object-cover" src={bg.videoUrl} autoPlay muted loop playsInline preload="metadata" aria-hidden tabIndex={-1} />
    ) : (
      <div className="absolute inset-0" style={{ backgroundColor: col(bg.colour) }} />
    );
  }
  const overlay = (bg.type === "image" || bg.type === "video") && bg.overlay > 0 ? <div className="absolute inset-0 bg-black" style={{ opacity: bg.overlay / 100 }} /> : null;
  return (
    <div className="pointer-events-none absolute inset-0 overflow-hidden" aria-hidden data-section-bg={bg.type}>
      {inner}
      {overlay}
    </div>
  );
}

export function borderStyle(b: SectionBorder): CSSProperties {
  const line = `${b.width}px ${b.style} ${col(b.colour)}`;
  const base: CSSProperties = { borderRadius: b.radius ? `${b.radius}px` : undefined };
  if (b.sides === "top") return { ...base, borderTop: line };
  if (b.sides === "bottom") return { ...base, borderBottom: line };
  if (b.sides === "topBottom") return { ...base, borderTop: line, borderBottom: line };
  return { ...base, border: line };
}

export const needsClip = (s: Section) => Boolean(s.background) || Boolean(s.border?.radius);
