"use client";

import { ReactNode, useEffect, useState } from "react";
import { InfoIcon } from "./icons";

export function PageHeader({ title, description }: { title: string; description?: ReactNode }) {
  return (
    <div className="mb-6">
      <h1 className="flex items-center gap-2 text-2xl font-semibold text-slate-900">
        {title}
        {description && <InfoTip label={`About ${title}`} width="w-72">{description}</InfoTip>}
      </h1>
    </div>
  );
}

export function Notice({
  kind = "info",
  children,
}: {
  kind?: "info" | "success" | "error";
  children: ReactNode;
}) {
  const styles = {
    info: "bg-slate-100 text-slate-800",
    success: "bg-green-50 text-ok",
    error: "bg-red-50 text-danger",
  }[kind];
  return (
    // A div (not a p) so callers can pass block content like <ul> without invalid
    // nesting / hydration errors; role keeps it announced to assistive tech.
    <div role={kind === "error" ? "alert" : "status"} className={`rounded-md px-3 py-2 text-sm ${styles}`}>
      {children}
    </div>
  );
}

export function Field({
  label,
  htmlFor,
  hint,
  error,
  info,
  children,
}: {
  label: string;
  htmlFor: string;
  hint?: string;
  error?: string;
  info?: ReactNode;
  children: ReactNode;
}) {
  return (
    <div>
      <div className="flex items-center gap-1.5">
        <label htmlFor={htmlFor} className="label">
          {label}
        </label>
        {info && <InfoTip label={`About ${label}`}>{info}</InfoTip>}
      </div>
      {children}
      {hint && !error && <p className="mt-1 text-xs text-slate-500">{hint}</p>}
      {error && (
        <p id={`${htmlFor}-error`} className="field-error" role="alert">
          {error}
        </p>
      )}
    </div>
  );
}

export function Toggle({
  id,
  checked,
  onChange,
  label,
  info,
}: {
  id: string;
  checked: boolean;
  onChange: (v: boolean) => void;
  label: string;
  info?: ReactNode;
}) {
  return (
    <label htmlFor={id} className="flex cursor-pointer items-center justify-between gap-4">
      <span className="flex items-center gap-1.5 text-sm font-medium text-slate-800">
        {label}
        {info && <InfoTip label={`About ${label}`}>{info}</InfoTip>}
      </span>
      <button
        id={id}
        type="button"
        role="switch"
        aria-checked={checked}
        onClick={() => onChange(!checked)}
        className={`relative inline-flex h-6 w-11 items-center rounded-full transition ${
          checked ? "bg-brand" : "bg-slate-300"
        }`}
      >
        <span
          className={`inline-block h-5 w-5 transform rounded-full bg-white transition ${
            checked ? "translate-x-5" : "translate-x-1"
          }`}
        />
      </button>
    </label>
  );
}

export function Spinner({ label = "Loading…" }: { label?: string }) {
  return (
    <p role="status" className="py-8 text-center text-sm text-slate-500">
      {label}
    </p>
  );
}

export function Badge({ kind, children }: { kind: "ok" | "warn" | "danger" | "neutral"; children: ReactNode }) {
  const styles = {
    ok: "bg-green-100 text-ok",
    warn: "bg-amber-100 text-warn",
    danger: "bg-red-100 text-danger",
    neutral: "bg-slate-100 text-slate-700",
  }[kind];
  return <span className={`badge ${styles}`}>{children}</span>;
}

/**
 * Small "ⓘ" affordance that reveals an explanation on hover or keyboard focus.
 * Pure CSS visibility (group-hover / group-focus-within) so it works without JS
 * state and stays accessible (role="tooltip", focusable trigger).
 */
export function InfoTip({
  children,
  label = "More information",
  side = "top",
  width = "w-64",
}: {
  children: ReactNode;
  label?: string;
  side?: "top" | "bottom";
  width?: string;
}) {
  return (
    <span className="group relative inline-flex align-middle">
      <button
        type="button"
        aria-label={label}
        className="inline-flex h-4 w-4 items-center justify-center rounded-full text-slate-400 hover:text-brand focus-visible:text-brand"
      >
        <InfoIcon className="h-4 w-4" />
      </button>
      <span
        role="tooltip"
        className={`pointer-events-none absolute left-1/2 z-50 ${width} -translate-x-1/2 rounded-md bg-slate-900 px-3 py-2 text-xs font-normal leading-snug text-white opacity-0 shadow-lg transition-opacity duration-100 group-hover:opacity-100 group-focus-within:opacity-100 ${
          side === "top" ? "bottom-full mb-2" : "top-full mt-2"
        }`}
      >
        {children}
      </span>
    </span>
  );
}

/** Deterministic, offline avatar: initials on a colour derived from the email. */
export function Avatar({ name, email, size = 32 }: { name?: string | null; email: string; size?: number }) {
  const initials = avatarInitials(name, email);
  const hue = avatarHue(email);
  return (
    <span
      aria-hidden
      style={{ width: size, height: size, backgroundColor: `hsl(${hue} 58% 42%)`, fontSize: Math.round(size * 0.4) }}
      className="inline-flex shrink-0 select-none items-center justify-center rounded-full font-semibold text-white"
    >
      {initials}
    </span>
  );
}

function avatarInitials(name: string | null | undefined, email: string): string {
  const src = name && !name.includes("@") ? name : (email.split("@")[0] ?? email);
  const words = src.split(/[\s._-]+/).filter(Boolean);
  const letters = (words.length >= 2 ? words[0][0] + words[1][0] : (words[0] ?? "?").slice(0, 2));
  return letters.toUpperCase();
}

function avatarHue(email: string): number {
  let h = 0;
  for (let i = 0; i < email.length; i++) h = (h * 31 + email.charCodeAt(i)) % 360;
  return h;
}

export type TabDef = { id: string; label: string; render: () => ReactNode };

/**
 * Accessible tab strip. Renders only the active panel (lazy).
 *
 * - variant "primary" (default): underline style, used for top-level page tabs;
 *   syncs the active tab to the URL hash so it is refresh-/deep-link-stable.
 * - variant "pill": compact pill style for SUBTABS nested inside a primary tab;
 *   hash sync is off by default so the inner and outer tabs don't fight the URL.
 */
export function Tabs({
  tabs,
  initial,
  variant = "primary",
  syncHash,
}: {
  tabs: TabDef[];
  initial?: string;
  variant?: "primary" | "pill";
  syncHash?: boolean;
}) {
  const doSync = syncHash ?? variant === "primary";
  const idKey = tabs.map((t) => t.id).join("|");
  const [active, setActive] = useState(() =>
    initial && tabs.some((t) => t.id === initial) ? initial : tabs[0].id,
  );

  useEffect(() => {
    if (!doSync) return;
    const ids = idKey.split("|");
    const fromHash = () => {
      const h = window.location.hash.replace(/^#/, "");
      if (h && ids.includes(h)) setActive(h);
    };
    fromHash();
    window.addEventListener("hashchange", fromHash);
    return () => window.removeEventListener("hashchange", fromHash);
  }, [idKey, doSync]);

  function select(id: string) {
    setActive(id);
    if (doSync && typeof window !== "undefined") window.history.replaceState(null, "", `#${id}`);
  }

  const current = tabs.find((t) => t.id === active) ?? tabs[0];
  const listClass =
    variant === "pill"
      ? "mb-5 flex flex-wrap gap-1 border-b border-slate-200"
      : "mb-6 flex flex-wrap gap-1 border-b border-slate-200";

  return (
    <div>
      <div role="tablist" aria-label="Sections" className={listClass}>
        {tabs.map((t) => {
          const on = t.id === current.id;
          const btnClass =
            variant === "pill"
              ? `-mb-px border-b-2 px-3 py-1.5 text-xs font-medium transition-colors ${
                  on ? "border-brand text-brand" : "border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-800"
                }`
              : `-mb-px border-b-2 px-4 py-2 text-sm font-medium transition-colors ${
                  on ? "border-brand text-brand" : "border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-800"
                }`;
          return (
            <button
              key={t.id}
              role="tab"
              id={`tab-${t.id}`}
              type="button"
              aria-selected={on}
              aria-controls={`panel-${t.id}`}
              onClick={() => select(t.id)}
              className={btnClass}
            >
              {t.label}
            </button>
          );
        })}
      </div>
      <div role="tabpanel" id={`panel-${current.id}`} aria-labelledby={`tab-${current.id}`}>
        {current.render()}
      </div>
    </div>
  );
}
