// Federated Dashboard — React + Tailwind + Font Awesome 6 Free (Canvas Preview JS)
// Adds custom drag preview that follows the cursor with a blue (#0EA5E9 @ 50%) shadow.
// Features: theme toggle, favorites, search (headings hidden while typing), drag & drop reordering,
// custom floating preview, status pill, 4-col grid, centered Federated logo.

import React, { useMemo, useState, useCallback, useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faStar as faStarRegular } from "@fortawesome/free-regular-svg-icons";
import {
  faSun,
  faMoon,
  faArrowUpRightFromSquare,
  faMagnifyingGlass,
  faStar as faStarSolid,
  faXmark,
} from "@fortawesome/free-solid-svg-icons";

// ------------------------------------
// Brand Tokens
// ------------------------------------
const BRAND_TOKENS = {
  primary: "#0F172A", // dark theme background
  accent: "#D67147", // favorites + focus ring + section label color
  surface: "#1E293B",
};
const LIGHT_TOKENS = {
  primary: "#4D6F83", // requested light background
  surface: "#FFFFFF", // white logo tile in light
};

// ------------------------------------
// Data
// ------------------------------------
// Local asset files for app logos (place in public/assets/)
const ASSETS = {
  panel_black: "/assets/panelblack.png",
  panel_white: "/assets/panelwhite.png",
  billing_black: "/assets/Billingblack.png",
  billing_white: "/assets/Billingwhite.png",
  jitsi: "/assets/jitsi.svg",
  element: "/assets/element.png",
  nextcloud: "/assets/nextcloud.svg",
  espocrm: "/assets/espocrm.svg",
  plane: "/assets/plane.svg",
  freescout: "/assets/freescout.png",
  roundcube: "/assets/roundcube.svg",
  bookstack: "/assets/bookstack.svg",
  baserow: "/assets/baserow.svg",
  gitea: "/assets/gitea.svg",
  powerdns: "/assets/powerdns.svg",
  wordpress: "/assets/Wordpress.png",
  vaultwarden: "/assets/Vaultwarden.svg",
  calcom_black: "/assets/calcomblack.svg",
  calcom_white: "/assets/calcomwhite.svg",
  federated_logo_light: "/assets/federated.png",
  federated_logo_dark: "/assets/federated.png",
};

const APP_CATALOG = [
  { id: "nextcloud", name: "Nextcloud", category: "Email, Files, Documents", panelManaged: true, logoUrl: ASSETS.nextcloud },
  { id: "jitsi", name: "Jitsi (Meet)", category: "Video Chat", panelManaged: true, logoUrl: ASSETS.jitsi },
  { id: "element", name: "Element", category: "Team Chat", panelManaged: true, logoUrl: ASSETS.element },
  { id: "espocrm", name: "EspoCRM", category: "Customer Relationship Manager", panelManaged: true, logoUrl: ASSETS.espocrm },
  { id: "plane", name: "Plane", category: "Project Management", panelManaged: false, logoUrl: ASSETS.plane },
  { id: "freescout", name: "FreeScout", category: "Customer Help Desk", panelManaged: false, logoUrl: ASSETS.freescout },
  { id: "vaultwarden", name: "Vaultwarden", category: "Passwords", panelManaged: false, logoUrl: ASSETS.vaultwarden },
  { id: "roundcube", name: "Roundcube", category: "Web Mail", panelManaged: true, logoUrl: ASSETS.roundcube },
  { id: "bookstack", name: "BookStack", category: "Wiki Knowledgebase", panelManaged: true, logoUrl: ASSETS.bookstack },
  { id: "baserow", name: "Baserow", category: "Visual Databases", panelManaged: true, logoUrl: ASSETS.baserow },
  { id: "gitea", name: "Gitea", category: "GIT Source Control", panelManaged: true, logoUrl: ASSETS.gitea },
  { id: "wordpress", name: "WordPress", category: "Your Website", panelManaged: true, logoUrl: ASSETS.wordpress },
  { id: "powerdns", name: "PowerDNS", category: "DNS Management", panelManaged: true, logoUrl: ASSETS.powerdns },
  { id: "calcom", name: "Cal.com", category: "Scheduling", panelManaged: true, logoUrl: ASSETS.calcom_black },
  { id: "panel", name: "Federated Panel", category: "Create & Manage Users", panelManaged: true, logoUrl: ASSETS.panel_black },
  { id: "billing", name: "Billing", category: "Manage your subscription", panelManaged: false, logoUrl: ASSETS.billing_black },
];

// ------------------------------------
// Hooks
// ------------------------------------
function useDebouncedValue(value, delay = 150) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const t = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(t);
  }, [value, delay]);
  return debounced;
}

// ------------------------------------
// UI bits
// ------------------------------------
const Pill = React.memo(function Pill({ managed }) {
  const pillBg = managed
    ? "bg-emerald-600 text-white ring-1 ring-emerald-300/30"
    : "bg-red-600 text-white ring-1 ring-red-300/30";
  const label = managed ? "In Panel" : "In App";
  return (
    <span className={`rounded-full px-2 py-0.5 text-[10px] font-semibold tracking-wide ${pillBg}`}>
      {label}
    </span>
  );
});

const AppLogo = React.memo(function AppLogo({ app, theme }) {
  const bg = theme === "light" ? "bg-white" : "bg-black/20";
  const themedSrc = app.id === "panel"
    ? (theme === "light" ? ASSETS.panel_black : ASSETS.panel_white)
    : app.id === "billing"
      ? (theme === "light" ? ASSETS.billing_black : ASSETS.billing_white)
      : app.id === "calcom"
        ? (theme === "light" ? ASSETS.calcom_black : ASSETS.calcom_white)
        : app.logoUrl;
  return (
    <div className={`flex h-20 items-center justify-center rounded-xl ${bg}`} data-testid={`logo-${app.id}`}>
      {themedSrc ? (
        <img
          src={themedSrc}
          className={`max-h-10 max-w-[60%] object-contain ${app.id === "vaultwarden" && theme === "dark" ? "invert brightness-200" : ""}`}
          loading="lazy"
          decoding="async"
          referrerPolicy="no-referrer"
          fetchPriority="low"
          width={120}
          height={40}
          crossOrigin="anonymous"
          alt={`${app.name} logo`}
        />
      ) : null}
    </div>
  );
});

// Drag-and-drop capable card
const AppCard = React.memo(function AppCard({
  app,
  favorite = false,
  theme = "light",
  onToggleFavorite,
  dnd,
}) {
  const draggable = !!(dnd && dnd.enabled);
  const isDragging = draggable && dnd && dnd.draggingId === app.id;
  return (
    <div
      className={`group relative rounded-2xl border border-white/10 bg-white/5 p-4 transform-gpu transition duration-200 ease-out cursor-default ${isDragging ? "opacity-0" : "hover:scale-[1.02] hover:shadow-[0_12px_32px_rgba(214,113,71,0.5)]"}`}
      data-brand={app.brandUrl || undefined}
      draggable={draggable}
      onDragStart={draggable ? dnd.onDragStart(app.id, dnd.list) : undefined}
      onDragOver={draggable ? dnd.onDragOver(app.id, dnd.list) : undefined}
      onDrop={draggable ? dnd.onDrop(app.id, dnd.list) : undefined}
      onDrag={draggable ? dnd.onDrag : undefined}
      onDragEnd={draggable ? dnd.onDragEnd : undefined}
      aria-grabbed={draggable ? undefined : false}
    >
      {/* Brand link (top-left) */}
      <div className="absolute left-2 top-2">
        {app.brandUrl && (
          <a
            href={app.brandUrl}
            target="_blank"
            rel="noreferrer"
            className={`inline-flex h-5 w-5 items-center justify-center rounded-full text-[10px] text-white/80 ring-1 ring-white/20 transition ${theme === "light" ? "bg-[#0EA5E9] hover:bg-[#0EA5E9]/90" : "bg-white/10 hover:bg-white/20"}`}
            title="Brand assets / logo policy"
          >
            i
          </a>
        )}
      </div>

      {/* Status pill (bottom-right) */}
      <div className="absolute right-2 bottom-2">
        <Pill managed={app.panelManaged} />
      </div>

      {/* Favorite toggle */}
      <div className="absolute right-2 top-2">
        <button
          type="button"
          onClick={() => onToggleFavorite && onToggleFavorite(app.id)}
          aria-pressed={favorite}
          aria-label={favorite ? "Remove from Favorites" : "Add to Favorites"}
          className="rounded-full p-1 transition hover:scale-110 focus:outline-none focus:ring-2 focus:ring-[var(--brand-accent)] cursor-pointer"
        >
          <FontAwesomeIcon icon={favorite ? faStarSolid : faStarRegular} className={favorite ? "text-[var(--brand-accent)]" : "text-white/70"} />
        </button>
      </div>

      {/* Title and purpose ABOVE the logo */}
      <div className="mt-6 mb-3 flex items-start justify-between">
        <div>
          <div className="text-sm font-semibold text-white">{app.category}</div>
          <h3 className="text-xs text-white/60 leading-tight">{app.name}</h3>
        </div>
        <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="mt-1 text-xs opacity-0 group-hover:opacity-100" />
      </div>

      {/* Logo area */}
      <AppLogo app={app} theme={theme} />
    </div>
  );
});

// ------------------------------------
// Page
// ------------------------------------
export default function FederatedDashboard() {
  const [theme, setTheme] = useState("light");
  const [query, setQuery] = useState("");

  // Default favorite: Panel only
  const [favorites, setFavorites] = useState(new Set(["panel"]));

  // Orders for within-section reordering
  const initialFav = new Set(["panel"]);
  const [favoritesOrder, setFavoritesOrder] = useState(APP_CATALOG.map(a => a.id).filter(id => initialFav.has(id)));
  const [othersOrder, setOthersOrder] = useState(APP_CATALOG.map(a => a.id).filter(id => !initialFav.has(id)));

  // Drag state
  const [dragging, setDragging] = useState(null); // { id, from }
  const [dragPos, setDragPos] = useState(null); // { x, y }
  const [dragMeta, setDragMeta] = useState(null); // { offsetX, offsetY, width, height, app }

  // CSS vars
  const rootStyle = useMemo(() => ({
    "--brand-primary": theme === "light" ? LIGHT_TOKENS.primary : BRAND_TOKENS.primary,
    "--brand-accent": BRAND_TOKENS.accent,
    "--brand-surface": theme === "light" ? LIGHT_TOKENS.surface : BRAND_TOKENS.surface,
  }), [theme]);

  // Index for search
  const APP_INDEX = useMemo(
    () => APP_CATALOG.map((a) => ({ ...a, _hay: `${a.name} ${a.category} ${a.id}`.toLowerCase() })),
    []
  );
  const APP_MAP = useMemo(() => {
    const m = new Map();
    APP_INDEX.forEach(a => m.set(a.id, a));
    return m;
  }, [APP_INDEX]);

  const debouncedQuery = useDebouncedValue(query, 150);
  const tokens = useMemo(
    () => debouncedQuery.toLowerCase().trim().split(/\s+/).filter(Boolean),
    [debouncedQuery]
  );
  const isSearching = tokens.length > 0;

  const matches = useCallback((a) => {
    if (tokens.length === 0) return true;
    const h = a._hay;
    for (const t of tokens) if (!h.includes(t)) return false;
    return true;
  }, [tokens]);

  // Lists in display order, then filtered
  const favAll = useMemo(() => favoritesOrder.map(id => APP_MAP.get(id)).filter(Boolean), [favoritesOrder, APP_MAP]);
  const otherAll = useMemo(() => othersOrder.map(id => APP_MAP.get(id)).filter(Boolean), [othersOrder, APP_MAP]);
  const favList = useMemo(() => favAll.filter(matches), [favAll, matches]);
  const otherList = useMemo(() => otherAll.filter(matches), [otherAll, matches]);

  // Favorite toggle + sync orders
  const toggleFavorite = useCallback((id) => {
    setFavorites(prev => {
      const next = new Set(prev);
      const wasFav = next.has(id);
      if (wasFav) next.delete(id); else next.add(id);
      setFavoritesOrder(o => (wasFav ? o.filter(x => x !== id) : o.includes(id) ? o : [...o, id]));
      setOthersOrder(o => (wasFav ? (o.includes(id) ? o : [...o, id]) : o.filter(x => x !== id)));
      return next;
    });
  }, []);

  // DnD handlers (within-section only) + floating preview tracking
  const onDragStart = useCallback((id, list) => (e) => {
    setDragging({ id, from: list });
    const rect = e.currentTarget.getBoundingClientRect();
    const offsetX = e.clientX - rect.left;
    const offsetY = e.clientY - rect.top;
    setDragMeta({ offsetX, offsetY, width: rect.width, height: rect.height, app: APP_MAP.get(id) });
    setDragPos({ x: e.clientX, y: e.clientY });
    e.dataTransfer.effectAllowed = "move";
    // hide the native ghost image so only our custom preview shows
    const img = new Image();
    img.src = "data:image/svg+xml;base64," + btoa('<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"></svg>');
    e.dataTransfer.setDragImage(img, 0, 0);
  }, [APP_MAP]);

  const onDragOver = useCallback((overId, list) => (e) => {
    if (!dragging || dragging.from !== list) return;
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";
  }, [dragging]);

  // keep preview following cursor even when not over a specific card
  useEffect(() => {
    if (!dragging) return;
    const handle = (e) => {
      setDragPos({ x: e.clientX, y: e.clientY });
    };
    window.addEventListener("dragover", handle);
    return () => window.removeEventListener("dragover", handle);
  }, [dragging]);

  const onDrag = useCallback((e) => {
    if (!dragging) return;
    setDragPos({ x: e.clientX, y: e.clientY });
  }, [dragging]);

  const onDrop = useCallback((overId, list) => (e) => {
    e.preventDefault();
    if (!dragging || dragging.from !== list) return;
    if (dragging.id === overId) { setDragging(null); setDragMeta(null); setDragPos(null); return; }
    const arr = list === "fav" ? favoritesOrder.slice() : othersOrder.slice();
    const fromIdx = arr.indexOf(dragging.id);
    const toIdx = arr.indexOf(overId);
    if (fromIdx < 0 || toIdx < 0) { setDragging(null); setDragMeta(null); setDragPos(null); return; }
    arr.splice(fromIdx, 1);
    arr.splice(toIdx, 0, dragging.id);
    if (list === "fav") setFavoritesOrder(arr); else setOthersOrder(arr);
    setDragging(null); setDragMeta(null); setDragPos(null);
  }, [dragging, favoritesOrder, othersOrder]);

  const onDropToEnd = useCallback((list) => (e) => {
    e.preventDefault();
    if (!dragging || dragging.from !== list) return;
    const arr = list === "fav" ? favoritesOrder.slice() : othersOrder.slice();
    const fromIdx = arr.indexOf(dragging.id);
    if (fromIdx < 0) { setDragging(null); setDragMeta(null); setDragPos(null); return; }
    arr.splice(fromIdx, 1);
    arr.push(dragging.id);
    if (list === "fav") setFavoritesOrder(arr); else setOthersOrder(arr);
    setDragging(null); setDragMeta(null); setDragPos(null);
  }, [dragging, favoritesOrder, othersOrder]);

  const onDragEnd = useCallback(() => { setDragging(null); setDragMeta(null); setDragPos(null); }, []);

  const clearSearch = useCallback(() => setQuery(""), []);

  return (
    <div className="min-h-screen bg-[var(--brand-primary)] text-white" style={rootStyle}>
      {/* Header */}
      <header className="sticky top-0 z-40 border-b border-white/10 bg-[var(--brand-primary)]/95 backdrop-blur">
        <div className="mx-auto grid max-w-7xl grid-cols-3 items-center px-4 py-3">
          <div className="justify-self-start"></div>
          <div className="flex flex-col items-center gap-1 leading-none text-center justify-self-center">
            <img
              src={theme === "light" ? ASSETS.federated_logo_light : ASSETS.federated_logo_dark}
              alt="Federated Computer"
              className="h-6 sm:h-7 w-auto object-contain"
            />
            <h1 className="mt-1 text-base sm:text-xl font-semibold leading-none tracking-tight">Dashboard</h1>
          </div>
          <div className="flex items-center gap-2 justify-self-end">
            <button
              onClick={() => setTheme("light")}
              aria-pressed={theme === "light"}
              className={`rounded-lg border border-white/10 bg-white/5 px-3 py-2 ${theme === "light" ? "ring-2 ring-[var(--brand-accent)]" : ""}`}
              aria-label="Light mode"
            >
              <FontAwesomeIcon icon={faSun} />
            </button>
            <button
              onClick={() => setTheme("dark")}
              aria-pressed={theme === "dark"}
              className={`rounded-lg border border-white/10 bg-white/5 px-3 py-2 ${theme === "dark" ? "ring-2 ring-[var(--brand-accent)]" : ""}`}
              aria-label="Dark mode"
            >
              <FontAwesomeIcon icon={faMoon} />
            </button>
          </div>
        </div>
      </header>

      {/* Search */}
      <section className="mx-auto max-w-7xl px-4 py-6">
        <div className="relative">
          <FontAwesomeIcon icon={faMagnifyingGlass} className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-white/50" />
          <input
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onKeyDown={(e) => { if (e.key === "Escape") clearSearch(); }}
            placeholder="Search apps…"
            className="w-full rounded-xl border border-white/10 bg-white/5 py-3 pl-10 pr-10 outline-none placeholder:text-white/50 focus:ring-2 focus:ring-[var(--brand-accent)]"
            aria-label="Search apps"
          />
          {query && (
            <button
              type="button"
              onClick={clearSearch}
              className="absolute right-2 top-1/2 -translate-y-1/2 rounded-md p-1 text-white/70 hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-[var(--brand-accent)]"
              aria-label="Clear search"
              title="Clear"
            >
              <FontAwesomeIcon icon={faXmark} />
            </button>
          )}
        </div>
      </section>

      {isSearching ? (
        <section className="mx-auto max-w-7xl px-4 py-6">
          <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
            {[...favList, ...otherList].map((app) => (
              <AppCard key={`search-${app.id}`} app={app} favorite={favorites.has(app.id)} theme={theme} onToggleFavorite={toggleFavorite} />
            ))}
            {[...favList, ...otherList].length === 0 && (
              <div className="col-span-full rounded-xl border border-white/10 bg-white/5 p-4 text-sm text-white/70">
                <span>No apps found for “{debouncedQuery}”.</span>
              </div>
            )}
          </div>
        </section>
      ) : (
        <>
          {/* Favorites */}
          <section className="mx-auto max-w-7xl px-4 py-6" onDragOver={(e) => dragging && dragging.from === "fav" && e.preventDefault()} onDrop={onDropToEnd("fav")}>
            <h2 className={`mb-3 text-sm font-semibold uppercase tracking-wider ${theme === "light" ? "text-white" : "text-[var(--brand-accent)]"}`}>Favorites</h2>
            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
              {favList.map((app) => (
                <AppCard
                  key={`fav-${app.id}`}
                  app={app}
                  favorite={true}
                  theme={theme}
                  onToggleFavorite={toggleFavorite}
                  dnd={{ enabled: true, list: "fav", onDragStart, onDragOver, onDrop, onDrag, draggingId: dragging && dragging.id, onDragEnd }}
                />
              ))}
              {favList.length === 0 && (
                <div className="col-span-full rounded-xl border border-white/10 bg-white/5 p-4 text-sm text-white/70">
                  <span>Click the star on any app to favorite it.</span>
                </div>
              )}
            </div>
          </section>

          {/* Your Apps (non-favorites) */}
          <section className="mx-auto max-w-7xl px-4 py-6" onDragOver={(e) => dragging && dragging.from === "other" && e.preventDefault()} onDrop={onDropToEnd("other") }>
            <h2 className={`mb-3 text-sm font-semibold uppercase tracking-wider ${theme === "light" ? "text-white" : "text-[var(--brand-accent)]"}`}>Your Apps</h2>
            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
              {otherList.map((app) => (
                <AppCard
                  key={app.id}
                  app={app}
                  favorite={false}
                  theme={theme}
                  onToggleFavorite={toggleFavorite}
                  dnd={{ enabled: true, list: "other", onDragStart, onDragOver, onDrop, onDrag, draggingId: dragging && dragging.id, onDragEnd }}
                />
              ))}
            </div>
          </section>
        </>
      )}

      {/* Floating drag preview */}
      {dragging && dragMeta && dragPos && (
        <div
          className="pointer-events-none fixed z-[1000]"
          style={{ left: dragPos.x - dragMeta.offsetX, top: dragPos.y - dragMeta.offsetY, width: dragMeta.width }}
        >
          <div className="rounded-2xl border border-white/10 bg-white/5 p-4 transform-gpu shadow-[0_12px_32px_rgba(14,165,233,0.5)]">
            <div className="mt-2 mb-3 flex items-start justify-between">
              <div>
                <div className="text-sm font-semibold text-white">{dragMeta.app.category}</div>
                <h3 className="text-xs text-white/60 leading-tight">{dragMeta.app.name}</h3>
              </div>
              <FontAwesomeIcon icon={faArrowUpRightFromSquare} className="mt-1 text-xs opacity-60" />
            </div>
            <AppLogo app={dragMeta.app} theme={theme} />
            <div className="absolute right-2 bottom-2">
              <Pill managed={dragMeta.app.panelManaged} />
            </div>
          </div>
        </div>
      )}

      {/* Footer */}
      <footer className="mx-auto max-w-7xl px-4 pb-12 pt-4 text-xs text-white/60">
        <div className="flex flex-wrap items-center justify-between gap-2 border-t border-white/10 pt-4">
          <div>© {new Date().getFullYear()} Federated Computer · Dashboard</div>
          <div className="inline-flex items-center gap-3">
            <span className="h-2 w-2 rounded-full bg-[var(--brand-accent)]" aria-hidden></span>
            <span>Light/Dark · Favorites toggle · Search (debounced) · Drag to reorder</span>
          </div>
        </div>
      </footer>
    </div>
  );
}

// NOTE: Dev-only sanity checks were removed to maximize Canvas compatibility.
