"use client";

// UI-Primitiven des Tools. Ein einziger Ort für Buttons, Dialoge, Bestätigungen, Toasts
// und Ladezustände -- damit Verhalten (ESC, Fokus-Falle, Animation) und Optik überall
// identisch sind, statt in jeder Komponente von Hand nachgebaut zu werden.
//
// Ersetzt insbesondere window.confirm()/alert(): Browser-Systemdialoge folgen keinem der
// drei Farbschemata und wirken wie ein Fremdkörper.

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from "react";
import { AlertTriangle, CheckCircle2, X } from "lucide-react";

// --- ESC-Verwaltung ----------------------------------------------------------------------
// Mehrere Schichten können gleichzeitig offen sein (Inspector + Dialog). ESC soll nur die
// OBERSTE schliessen. Jede Schicht registriert sich beim Öffnen; ESC trifft die letzte.
const escStack: (() => void)[] = [];
let escListenerAttached = false;

function ensureEscListener() {
  if (escListenerAttached || typeof window === "undefined") return;
  escListenerAttached = true;
  window.addEventListener("keydown", (e) => {
    if (e.key !== "Escape" || escStack.length === 0) return;
    e.stopPropagation();
    escStack[escStack.length - 1]();
  });
}

function useEscClose(onClose: () => void) {
  const closeRef = useRef(onClose);
  useEffect(() => {
    closeRef.current = onClose;
  }, [onClose]);
  useEffect(() => {
    ensureEscListener();
    const handler = () => closeRef.current();
    escStack.push(handler);
    return () => {
      const i = escStack.indexOf(handler);
      if (i >= 0) escStack.splice(i, 1);
    };
  }, []);
}

// Einfache Fokus-Falle: beim Öffnen den ersten fokussierbaren Inhalt fokussieren,
// Tab bleibt innerhalb der Schicht, beim Schliessen kehrt der Fokus zurück.
function useFocusTrap(ref: React.RefObject<HTMLElement | null>) {
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const previous = document.activeElement as HTMLElement | null;
    const focusables = () =>
      [...el.querySelectorAll<HTMLElement>(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      )].filter((x) => !x.hasAttribute("disabled"));
    (focusables()[0] ?? el).focus({ preventScroll: true });
    function onKey(e: KeyboardEvent) {
      if (e.key !== "Tab") return;
      const f = focusables();
      if (f.length === 0) return;
      const first = f[0];
      const last = f[f.length - 1];
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    }
    el.addEventListener("keydown", onKey);
    return () => {
      el.removeEventListener("keydown", onKey);
      previous?.focus?.({ preventScroll: true });
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
}

// --- Button ------------------------------------------------------------------------------

type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";

const BUTTON_CLASSES: Record<ButtonVariant, string> = {
  primary:
    "bg-accent text-accent-fg shadow-sm hover:bg-accent-hover active:scale-[0.98]",
  secondary:
    "border border-border text-muted hover:bg-surface-2 hover:text-fg active:scale-[0.98]",
  danger:
    "border border-border text-muted hover:border-danger hover:bg-danger-soft hover:text-danger active:scale-[0.98]",
  ghost: "text-muted hover:bg-surface-2 hover:text-fg",
};

export function Button({
  variant = "primary",
  size = "md",
  className = "",
  children,
  ...rest
}: React.ButtonHTMLAttributes<HTMLButtonElement> & {
  variant?: ButtonVariant;
  size?: "sm" | "md";
}) {
  const sizeClass = size === "sm" ? "px-2.5 py-1 text-xs" : "px-4 py-2 text-sm";
  return (
    <button
      type="button"
      {...rest}
      className={`inline-flex items-center justify-center gap-1.5 rounded-lg font-medium transition-all duration-150 disabled:pointer-events-none disabled:opacity-50 ${BUTTON_CLASSES[variant]} ${sizeClass} ${className}`}
    >
      {children}
    </button>
  );
}

// --- Modal -------------------------------------------------------------------------------

export function Modal({
  title,
  subtitle,
  onClose,
  children,
  wide,
}: {
  title: string;
  subtitle?: string;
  onClose: () => void;
  children: React.ReactNode;
  wide?: boolean;
}) {
  const panelRef = useRef<HTMLDivElement | null>(null);
  useEscClose(onClose);
  useFocusTrap(panelRef);
  return (
    <div
      className="ui-fade fixed inset-0 z-40 flex items-start justify-center overflow-y-auto bg-black/40 px-4 py-8"
      onClick={onClose}
    >
      <div
        ref={panelRef}
        role="dialog"
        aria-modal="true"
        aria-label={title}
        onClick={(e) => e.stopPropagation()}
        className={`ui-pop flex w-full ${wide ? "max-w-2xl" : "max-w-md"} flex-col gap-3 rounded-2xl border border-border bg-surface p-6 shadow-xl`}
      >
        <div className="flex items-start justify-between gap-3">
          <div className="min-w-0">
            <h2 className="text-base font-semibold text-fg">{title}</h2>
            {subtitle && <p className="mt-0.5 text-xs text-muted">{subtitle}</p>}
          </div>
          <button
            type="button"
            onClick={onClose}
            aria-label="Schliessen"
            className="rounded-md p-1 text-faint transition-colors hover:bg-surface-2 hover:text-fg"
          >
            <X className="h-4 w-4" />
          </button>
        </div>
        {children}
      </div>
    </div>
  );
}

// --- Inspector ---------------------------------------------------------------------------
// Rechtes Seitenpanel für Einzel-Bearbeitungen (Zellen, Übergänge, Phase, Profil).
// Bewusst OHNE Backdrop: Die Matrix bleibt sichtbar und klickbar -- ein Klick auf eine
// andere Zelle wechselt einfach den Panel-Inhalt. Das hält den Kontext, den Modals nehmen.
// Auf schmalen Screens deckt das Panel die volle Breite ab.

export function InspectorShell({
  title,
  subtitle,
  onClose,
  children,
}: {
  title: string;
  subtitle?: string;
  onClose: () => void;
  children: React.ReactNode;
}) {
  const panelRef = useRef<HTMLDivElement | null>(null);
  useEscClose(onClose);
  return (
    <aside
      ref={panelRef}
      role="complementary"
      aria-label={title}
      className="ui-slide-in fixed inset-y-0 right-0 z-30 flex w-full flex-col border-l border-border bg-surface shadow-2xl sm:w-[26rem]"
    >
      <div className="flex items-start justify-between gap-3 border-b border-border px-5 py-4">
        <div className="min-w-0">
          <h2 className="truncate text-base font-semibold text-fg">{title}</h2>
          {subtitle && <p className="mt-0.5 text-xs text-muted">{subtitle}</p>}
        </div>
        <button
          type="button"
          onClick={onClose}
          aria-label="Panel schliessen"
          className="rounded-md p-1 text-faint transition-colors hover:bg-surface-2 hover:text-fg"
        >
          <X className="h-4 w-4" />
        </button>
      </div>
      <div className="flex-1 overflow-y-auto px-5 py-4">{children}</div>
    </aside>
  );
}

// --- Bestätigungs-Dialog (ersetzt window.confirm) ---------------------------------------

interface ConfirmOptions {
  title: string;
  message: string;
  confirmLabel?: string;
  danger?: boolean;
}

const ConfirmContext = createContext<((opts: ConfirmOptions) => Promise<boolean>) | null>(null);

export function useConfirm() {
  const fn = useContext(ConfirmContext);
  if (!fn) throw new Error("useConfirm ausserhalb des ConfirmProviders");
  return fn;
}

export function ConfirmProvider({ children }: { children: React.ReactNode }) {
  const [pending, setPending] = useState<{
    opts: ConfirmOptions;
    resolve: (ok: boolean) => void;
  } | null>(null);

  const confirm = useCallback(
    (opts: ConfirmOptions) =>
      new Promise<boolean>((resolve) => setPending({ opts, resolve })),
    []
  );

  function settle(ok: boolean) {
    pending?.resolve(ok);
    setPending(null);
  }

  return (
    <ConfirmContext.Provider value={confirm}>
      {children}
      {pending && (
        <Modal title={pending.opts.title} onClose={() => settle(false)}>
          <p className="text-sm text-muted">{pending.opts.message}</p>
          <div className="flex gap-2 pt-1">
            <Button
              variant={pending.opts.danger ? "danger" : "primary"}
              autoFocus
              onClick={() => settle(true)}
              className={pending.opts.danger ? "border-danger bg-danger-soft text-danger" : ""}
            >
              {pending.opts.confirmLabel ?? "Bestätigen"}
            </Button>
            <Button variant="secondary" onClick={() => settle(false)}>
              Abbrechen
            </Button>
          </div>
        </Modal>
      )}
    </ConfirmContext.Provider>
  );
}

// --- Toasts (ersetzt alert und stumme Erfolge) -------------------------------------------

interface ToastItem {
  id: number;
  kind: "success" | "error";
  text: string;
}

const ToastContext = createContext<((kind: ToastItem["kind"], text: string) => void) | null>(null);

export function useToast() {
  const fn = useContext(ToastContext);
  if (!fn) throw new Error("useToast ausserhalb des ToastProviders");
  return fn;
}

export function ToastProvider({ children }: { children: React.ReactNode }) {
  const [toasts, setToasts] = useState<ToastItem[]>([]);
  const nextId = useRef(1);

  const push = useCallback((kind: ToastItem["kind"], text: string) => {
    const id = nextId.current++;
    setToasts((prev) => [...prev, { id, kind, text }]);
    // Fehler bleiben länger stehen als Erfolge.
    setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), kind === "error" ? 6000 : 3200);
  }, []);

  return (
    <ToastContext.Provider value={push}>
      {children}
      <div className="pointer-events-none fixed bottom-4 right-4 z-50 flex w-80 flex-col gap-2">
        {toasts.map((t) => (
          <div
            key={t.id}
            className={`ui-pop pointer-events-auto flex items-start gap-2 rounded-xl border px-3.5 py-2.5 text-sm shadow-lg ${
              t.kind === "error"
                ? "border-danger bg-danger-soft text-danger"
                : "border-border bg-surface text-fg"
            }`}
          >
            {t.kind === "error" ? (
              <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0" />
            ) : (
              <CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-success" />
            )}
            <span className="min-w-0 flex-1">{t.text}</span>
            <button
              type="button"
              aria-label="Meldung schliessen"
              onClick={() => setToasts((prev) => prev.filter((x) => x.id !== t.id))}
              className="rounded p-0.5 text-faint hover:text-fg"
            >
              <X className="h-3.5 w-3.5" />
            </button>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
}

// --- Ladezustände und leere Zustände ---------------------------------------------------

export function Skeleton({ className = "" }: { className?: string }) {
  return <div className={`animate-pulse rounded-lg bg-surface-2 ${className}`} />;
}

// Platzhalter während die Planansicht lädt -- deutet die Struktur an, statt nur
// "Lädt…" zu schreiben (wahrgenommene Geschwindigkeit).
export function PlanSkeleton() {
  return (
    <div className="flex flex-col gap-5" aria-busy="true" aria-label="Lädt">
      <Skeleton className="h-8 w-64" />
      <Skeleton className="h-24 w-full" />
      <Skeleton className="h-12 w-full" />
      <Skeleton className="h-72 w-full" />
    </div>
  );
}

export function EmptyState({
  icon,
  title,
  text,
  children,
}: {
  icon: React.ReactNode;
  title: string;
  text: string;
  children?: React.ReactNode;
}) {
  return (
    <div className="flex flex-col items-center gap-3 rounded-xl border border-dashed border-border bg-surface p-10 text-center">
      <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-accent-soft text-accent-soft-fg">
        {icon}
      </div>
      <div>
        <h3 className="text-sm font-semibold text-fg">{title}</h3>
        <p className="mx-auto mt-1 max-w-md text-sm text-muted">{text}</p>
      </div>
      {children && <div className="mt-1 flex flex-wrap justify-center gap-2">{children}</div>}
    </div>
  );
}
