"use client";

import { useState, useEffect, useCallback, useRef } from "react";
import type {
  NpmSearchResult,
  InstalledPackage,
  InstalledResource,
  ResourceType,
} from "@/lib/package-types";

// ── Helpers ──────────────────────────────────────────────

function shortenPath(p: string): string {
  return p.replace(/^\/(?:Users|home)\/[^/]+/, "~");
}

function formatDownloads(n: number): string {
  if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, "")}M`;
  if (n >= 1_000) return `${(n / 1_000).toFixed(1).replace(/\.0$/, "")}K`;
  return String(n);
}

const RESOURCE_LABELS: Record<ResourceType, { label: string; bg: string; color: string; plural: string }> = {
  extension: { label: "ext", bg: "rgba(99,102,241,0.12)", color: "rgba(99,102,241,0.8)", plural: "extensions" },
  skill: { label: "skill", bg: "rgba(34,197,94,0.12)", color: "rgba(34,197,94,0.8)", plural: "skills" },
  prompt: { label: "prompt", bg: "rgba(234,179,8,0.12)", color: "rgba(234,179,8,0.8)", plural: "prompts" },
  theme: { label: "theme", bg: "rgba(236,72,153,0.12)", color: "rgba(236,72,153,0.8)", plural: "themes" },
};

function scopeLabel(scope: string): string {
  return scope === "project" ? "project" : "global";
}

// ── Toggle switch ───────────────────────────────────────

function Toggle({
  enabled,
  loading,
  onToggle,
  small,
}: {
  enabled: boolean;
  loading: boolean;
  onToggle: () => void;
  small?: boolean;
}) {
  const w = small ? 32 : 40;
  const h = small ? 18 : 22;
  const ballSize = small ? 13 : 16;
  return (
    <button
      onClick={onToggle}
      disabled={loading}
      title={enabled ? "Enabled — click to disable" : "Disabled — click to enable"}
      style={{
        flexShrink: 0,
        width: w,
        height: h,
        borderRadius: h / 2,
        border: "none",
        padding: 0,
        cursor: loading ? "wait" : "pointer",
        background: enabled ? "var(--accent)" : "var(--border)",
        position: "relative",
        transition: "background 0.18s",
        outline: "none",
      }}
    >
      <span
        style={{
          position: "absolute",
          top: (h - ballSize) / 2,
          left: enabled ? w - ballSize - (h - ballSize) / 2 : (h - ballSize) / 2,
          width: ballSize,
          height: ballSize,
          borderRadius: "50%",
          background: "var(--bg)",
          boxShadow: "0 1px 4px rgba(0,0,0,0.22)",
          transition: "left 0.18s cubic-bezier(.4,0,.2,1)",
        }}
      />
    </button>
  );
}

// ── Resource badge in list ──────────────────────────────

function ResourceBadge({ type, count }: { type: ResourceType; count: number }) {
  const meta = RESOURCE_LABELS[type];
  return (
    <span
      style={{
        fontSize: 10,
        padding: "1px 5px",
        borderRadius: 3,
        background: meta.bg,
        color: meta.color,
        fontFamily: "var(--font-mono)",
        whiteSpace: "nowrap",
      }}
    >
      {count > 0 ? `${count}${meta.label}` : meta.label}
    </span>
  );
}

// ── Add Package Panel ───────────────────────────────────

function AddPackagePanel({
  cwd,
  onInstalled,
}: {
  cwd: string;
  onInstalled: () => void;
}) {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<NpmSearchResult[]>([]);
  const [searching, setSearching] = useState(false);
  const [searchError, setSearchError] = useState<string | null>(null);
  const [installing, setInstalling] = useState<string | null>(null);
  const [installError, setInstallError] = useState<string | null>(null);
  const [installedPkgs, setInstalledPkgs] = useState<Set<string>>(new Set());
  const [scope, setScope] = useState<"global" | "project">("global");
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  const search = useCallback(async (q: string) => {
    if (!q.trim()) return;
    setSearching(true);
    setSearchError(null);
    setResults([]);
    try {
      const res = await fetch("/api/packages/search", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ query: q.trim(), size: 30 }),
      });
      const d = (await res.json()) as {
        results?: NpmSearchResult[];
        error?: string;
      };
      if (d.error) {
        setSearchError(d.error);
        return;
      }
      setResults(d.results ?? []);
      if ((d.results ?? []).length === 0) setSearchError("No packages found");
    } catch (e) {
      setSearchError(String(e));
    } finally {
      setSearching(false);
    }
  }, []);

  const install = useCallback(
    async (pkgName: string) => {
      setInstalling(pkgName);
      setInstallError(null);
      try {
        const res = await fetch("/api/packages/install", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ package: pkgName, scope, cwd }),
        });
        const d = (await res.json()) as { success?: boolean; error?: string };
        if (!res.ok || d.error) {
          setInstallError(d.error ?? `HTTP ${res.status}`);
          return;
        }
        setInstalledPkgs((prev) => new Set(prev).add(pkgName));
        onInstalled();
      } catch (e) {
        setInstallError(String(e));
      } finally {
        setInstalling(null);
      }
    },
    [onInstalled, scope, cwd],
  );

  const installPath =
    scope === "global"
      ? "~/.pi/agent/npm/"
      : `${shortenPath(cwd)}/.pi/npm/`;

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
      {/* Header area */}
      <div style={{ display: "flex", flexDirection: "column", gap: 12, marginBottom: 20 }}>
        <div style={{ fontSize: 14, fontWeight: 600, color: "var(--text)" }}>
          Add Package
        </div>

        {/* Search row */}
        <div style={{ display: "flex", gap: 8 }}>
          <input
            ref={inputRef}
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === "Enter") search(query);
            }}
            placeholder="e.g. web-search, subagents, memory..."
            style={{
              flex: 1,
              padding: "7px 10px",
              fontSize: 13,
              background: "var(--bg-panel)",
              border: "1px solid var(--border)",
              borderRadius: 6,
              color: "var(--text)",
              outline: "none",
            }}
          />
          <button
            onClick={() => search(query)}
            disabled={searching || !query.trim()}
            style={{
              padding: "7px 16px",
              fontSize: 13,
              borderRadius: 6,
              border: "none",
              background: "var(--accent)",
              color: "#fff",
              cursor: searching || !query.trim() ? "not-allowed" : "pointer",
              opacity: searching || !query.trim() ? 0.5 : 1,
              flexShrink: 0,
            }}
          >
            {searching ? "Searching…" : "Search"}
          </button>
        </div>

        {/* Scope selector + install path */}
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div
            style={{
              display: "flex",
              borderRadius: 5,
              border: "1px solid var(--border)",
              overflow: "hidden",
              fontSize: 12,
              flexShrink: 0,
            }}
          >
            {(["global", "project"] as const).map((s) => (
              <button
                key={s}
                onClick={() => setScope(s)}
                style={{
                  padding: "3px 10px",
                  border: "none",
                  cursor: "pointer",
                  background: scope === s ? "var(--bg-selected)" : "none",
                  color: scope === s ? "var(--text)" : "var(--text-dim)",
                  fontWeight: scope === s ? 600 : 400,
                  borderRight: s === "global" ? "1px solid var(--border)" : "none",
                }}
              >
                {s}
              </button>
            ))}
          </div>
          <span
            style={{
              fontSize: 12,
              color: "var(--text-dim)",
              fontFamily: "var(--font-mono)",
              overflow: "hidden",
              textOverflow: "ellipsis",
              whiteSpace: "nowrap",
            }}
          >
            → {installPath}
          </span>
        </div>

        {/* Errors */}
        {searchError && (
          <div style={{ fontSize: 12, color: "#f87171" }}>{searchError}</div>
        )}
        {installError && (
          <div style={{ fontSize: 12, color: "#f87171", wordBreak: "break-word" }}>
            {installError}
          </div>
        )}
      </div>

      {/* Results list */}
      {results.length > 0 ? (
        <div style={{ flex: 1, overflowY: "auto" }}>
          {results.map((r) => {
            const isInstalled = installedPkgs.has(r.name);
            const isInstalling = installing === r.name;
            return (
              <div
                key={r.name}
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: 14,
                  padding: "12px 0",
                  borderBottom: "1px solid var(--border)",
                }}
              >
                <div style={{ flex: 1, minWidth: 0 }}>
                  {/* Package name + version */}
                  <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 3 }}>
                    <span
                      style={{
                        fontSize: 13,
                        fontWeight: 600,
                        color: "var(--text)",
                        fontFamily: "var(--font-mono)",
                      }}
                    >
                      {r.name}
                    </span>
                    <span style={{ fontSize: 11, color: "var(--text-dim)" }}>
                      v{r.version}
                    </span>
                  </div>
                  {/* Description */}
                  <div
                    style={{
                      fontSize: 12,
                      color: "var(--text-muted)",
                      lineHeight: 1.4,
                      marginBottom: 4,
                      overflow: "hidden",
                      textOverflow: "ellipsis",
                      whiteSpace: "nowrap",
                    }}
                  >
                    {r.description}
                  </div>
                  {/* Badges row */}
                  <div style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }}>
                    {r.resourceTypes.map((t) => (
                      <ResourceBadge key={t} type={t} count={0} />
                    ))}
                    {r.downloads > 0 && (
                      <span style={{ fontSize: 11, color: "var(--text-dim)" }}>
                        {formatDownloads(r.downloads)} /mo
                      </span>
                    )}
                    {r.links.repository && (
                      <a
                        href={r.links.repository}
                        target="_blank"
                        rel="noreferrer"
                        style={{ fontSize: 11, color: "var(--accent)", textDecoration: "none" }}
                        onClick={(e) => e.stopPropagation()}
                      >
                        repo ↗
                      </a>
                    )}
                  </div>
                </div>
                <button
                  onClick={() => !isInstalled && !isInstalling && install(r.name)}
                  disabled={isInstalled || isInstalling || installing !== null}
                  style={{
                    flexShrink: 0,
                    padding: "5px 14px",
                    fontSize: 12,
                    fontWeight: 500,
                    borderRadius: 5,
                    border: "1px solid var(--border)",
                    cursor: isInstalled || isInstalling || installing !== null ? "not-allowed" : "pointer",
                    background: isInstalled ? "rgba(34,197,94,0.1)" : "none",
                    color: isInstalled ? "#16a34a" : isInstalling ? "var(--accent)" : "var(--text-muted)",
                    transition: "color 0.12s",
                  }}
                >
                  {isInstalled ? "✓ Installed" : isInstalling ? "Installing…" : "Install"}
                </button>
              </div>
            );
          })}
        </div>
      ) : !searchError && !searching ? (
        <div style={{ fontSize: 13, color: "var(--text-dim)", lineHeight: 1.8 }}>
          Search npm for pi packages to discover and install extensions, skills, prompts, and themes for your agent.
        </div>
      ) : null}
    </div>
  );
}

// ── Package Detail View ─────────────────────────────────

function PackageDetail({
  pkg,
  cwd,
  onToggleResource,
  onRemove,
  onUpdate,
  toggling,
  removing,
  updating,
}: {
  pkg: InstalledPackage;
  cwd: string;
  onToggleResource: (resource: InstalledResource) => void;
  onRemove: (pkg: InstalledPackage) => void;
  onUpdate: (pkg: InstalledPackage) => void;
  toggling: Set<string>;
  removing: boolean;
  updating: boolean;
}) {
  const label = scopeLabel(pkg.scope);
  const hasResources = pkg.resources.length > 0;

  function displayPath(p: string): string {
    if (pkg.scope === "project" && p.startsWith(cwd)) {
      const rel = p.slice(cwd.length).replace(/^[/\\]/, "");
      return `./${rel}`;
    }
    return shortenPath(p);
  }

  // Group resources by type
  const grouped = pkg.resources.reduce(
    (acc, r) => {
      const key = r.type;
      if (!acc[key]) acc[key] = [];
      acc[key].push(r);
      return acc;
    },
    {} as Record<string, InstalledResource[]>,
  );

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 20 }}>
      {/* Header: name + version + scope */}
      <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
        <span style={{ fontSize: 16, fontWeight: 700, color: "var(--text)", fontFamily: "var(--font-mono)" }}>
          {pkg.name}
        </span>
        {pkg.version && (
          <span style={{ fontSize: 13, color: "var(--text-dim)" }}>v{pkg.version}</span>
        )}
        <span
          style={{
            fontSize: 10,
            padding: "1px 5px",
            borderRadius: 3,
            flexShrink: 0,
            background: pkg.scope === "project"
              ? "rgba(99,102,241,0.12)"
              : "rgba(120,120,120,0.12)",
            color: pkg.scope === "project" ? "rgba(99,102,241,0.8)" : "var(--text-dim)",
          }}
        >
          {label}
        </span>
        {pkg.installPath && (
          <span
            style={{
              fontFamily: "var(--font-mono)",
              fontSize: 11,
              color: "var(--text-dim)",
              flex: 1,
              overflow: "hidden",
              textOverflow: "ellipsis",
              whiteSpace: "nowrap",
            }}
          >
            {displayPath(pkg.installPath)}
          </span>
        )}
      </div>

      {/* Description */}
      {pkg.description && (
        <div style={{ fontSize: 13, color: "var(--text-muted)", lineHeight: 1.5 }}>
          {pkg.description}
        </div>
      )}

      {/* Action buttons */}
      <div style={{ display: "flex", gap: 8 }}>
        <button
          onClick={() => onRemove(pkg)}
          disabled={removing}
          style={{
            padding: "6px 14px",
            fontSize: 12,
            fontWeight: 500,
            borderRadius: 6,
            border: "1px solid rgba(239,68,68,0.3)",
            background: "none",
            color: removing ? "var(--text-dim)" : "#ef4444",
            cursor: removing ? "not-allowed" : "pointer",
            opacity: removing ? 0.5 : 1,
          }}
        >
          {removing ? "Removing…" : "Remove"}
        </button>
        <button
          onClick={() => onUpdate(pkg)}
          disabled={updating}
          style={{
            padding: "6px 14px",
            fontSize: 12,
            fontWeight: 500,
            borderRadius: 6,
            border: "1px solid var(--border)",
            background: "none",
            color: updating ? "var(--text-dim)" : "var(--text-muted)",
            cursor: updating ? "not-allowed" : "pointer",
            opacity: updating ? 0.5 : 1,
          }}
        >
          {updating ? "Updating…" : "Update"}
        </button>
      </div>

      {/* Links */}
      {(pkg.source.startsWith("npm:") || pkg.repository || pkg.homepage) && (
        <div style={{ display: "flex", gap: 16, fontSize: 12, flexWrap: "wrap", alignItems: "center" }}>
          {pkg.repository && (
            <a
              href={pkg.repository}
              target="_blank"
              rel="noreferrer"
              style={{ color: "var(--accent)", textDecoration: "none" }}
            >
              repository ↗
            </a>
          )}
          {pkg.source.startsWith("npm:") && (
            <a
              href={`https://www.npmjs.com/package/${pkg.name}`}
              target="_blank"
              rel="noreferrer"
              style={{ color: "var(--accent)", textDecoration: "none" }}
            >
              npm ↗
            </a>
          )}
          {pkg.homepage && pkg.homepage !== pkg.repository && (
            <a
              href={pkg.homepage}
              target="_blank"
              rel="noreferrer"
              style={{ color: "var(--accent)", textDecoration: "none" }}
            >
              homepage ↗
            </a>
          )}
          <span style={{ color: "var(--text-dim)", marginLeft: "auto" }}>
            Install: <code style={{ fontFamily: "var(--font-mono)", fontSize: 11 }}>pi install {pkg.source}</code>
          </span>
        </div>
      )}

      {/* Resources */}
      {hasResources ? (
        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          <span style={{ fontSize: 12, color: "var(--text-muted)", fontWeight: 600 }}>
            Resources
          </span>
          {(["extension", "skill", "prompt", "theme"] as ResourceType[]).map((type) => {
            const resources = grouped[type];
            if (!resources || resources.length === 0) return null;
            const meta = RESOURCE_LABELS[type];
            return (
              <div key={type} style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--text-dim)" }}>
                  <span style={{
                    display: "inline-block",
                    width: 8,
                    height: 8,
                    borderRadius: "50%",
                    background: meta.color,
                  }} />
                  <span style={{ fontWeight: 500, textTransform: "capitalize" }}>{meta.plural}</span>
                  <span>({resources.length})</span>
                </div>
                {resources.map((r) => (
                  <div
                    key={r.path}
                    style={{
                      display: "flex",
                      alignItems: "center",
                      gap: 8,
                      padding: "4px 0 4px 16px",
                      fontSize: 12,
                    }}
                  >
                    <Toggle
                      enabled={r.enabled}
                      loading={toggling.has(resourceKey(pkg.source, r.path))}
                      onToggle={() => onToggleResource(r)}
                      small
                    />
                    <span
                      style={{
                        fontFamily: "var(--font-mono)",
                        fontSize: 11,
                        color: r.enabled ? "var(--text)" : "var(--text-dim)",
                        flex: 1,
                        overflow: "hidden",
                        textOverflow: "ellipsis",
                        whiteSpace: "nowrap",
                      }}
                    >
                      {r.path}
                    </span>
                  </div>
                ))}
              </div>
            );
          })}
        </div>
      ) : (
        <div style={{ fontSize: 12, color: "var(--text-dim)", fontStyle: "italic" }}>
          No resource metadata found. This package may not declare a <code style={{ fontFamily: "var(--font-mono)", fontSize: 11 }}>pi</code> manifest.
        </div>
      )}

      {/* Source info */}
      <div style={{ fontSize: 12, color: "var(--text-dim)", paddingTop: 8, borderTop: "1px solid var(--border)" }}>
        Source: <code style={{ fontFamily: "var(--font-mono)" }}>{pkg.source}</code>
      </div>
    </div>
  );
}

function resourceKey(source: string, path: string): string {
  return `${source}::${path}`;
}

// ── Main PackagesConfig ─────────────────────────────────

export function PackagesConfig({
  cwd,
  onClose,
}: {
  cwd: string;
  onClose: () => void;
}) {
  const [packages, setPackages] = useState<InstalledPackage[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [toggling, setToggling] = useState<Set<string>>(new Set());
  const [addMode, setAddMode] = useState(false);
  const [removing, setRemoving] = useState(false);
  const [updating, setUpdating] = useState(false);

  const loadPackages = useCallback(() => {
    setLoading(true);
    setError(null);
    fetch(`/api/packages?cwd=${encodeURIComponent(cwd)}`)
      .then((r) => r.json())
      .then((d: { packages?: InstalledPackage[]; error?: string }) => {
        if (d.error) {
          setError(d.error);
          return;
        }
        const list = d.packages ?? [];
        setPackages(list);
        if (list.length > 0 && !selectedId) {
          setSelectedId(list[0].source);
          setAddMode(false);
        }
      })
      .catch((e) => setError(String(e)))
      .finally(() => setLoading(false));
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [cwd]);

  useEffect(() => {
    loadPackages();
  }, [cwd, loadPackages]);

  const toggleResource = useCallback(async (resource: InstalledResource) => {
    const selected = packages.find((p) => p.source === selectedId);
    if (!selected) return;

    const key = resourceKey(selected.source, resource.path);
    setToggling((s) => new Set(s).add(key));
    try {
      const res = await fetch("/api/packages", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          source: selected.source,
          resourcePath: resource.path,
          resourceType: resource.type,
          enabled: !resource.enabled,
          scope: selected.scope,
          cwd,
        }),
      });
      const d = (await res.json()) as { success?: boolean; error?: string };
      if (!res.ok || d.error) return;
      // Update local state
      setPackages((prev) =>
        prev.map((p) => {
          if (p.source !== selected.source) return p;
          return {
            ...p,
            resources: p.resources.map((r) =>
              r.path === resource.path && r.type === resource.type
                ? { ...r, enabled: !r.enabled }
                : r,
            ),
          };
        }),
      );
    } catch {
      // ignore
    } finally {
      setToggling((s) => {
        const n = new Set(s);
        n.delete(key);
        return n;
      });
    }
  }, [packages, selectedId, cwd]);

  const handleRemove = useCallback(async (pkg: InstalledPackage) => {
    if (!confirm(`Remove ${pkg.source}?`)) return;
    setRemoving(true);
    try {
      const res = await fetch("/api/packages/remove", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          package: pkg.source,
          scope: pkg.scope,
          cwd,
        }),
      });
      const d = (await res.json()) as { success?: boolean; error?: string };
      if (!res.ok || d.error) return;
      loadPackages();
      if (selectedId === pkg.source) setSelectedId(null);
    } catch {
      // ignore
    } finally {
      setRemoving(false);
    }
  }, [cwd, selectedId, loadPackages]);

  const handleUpdate = useCallback(async (pkg: InstalledPackage) => {
    setUpdating(true);
    try {
      const res = await fetch("/api/packages/update", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          package: pkg.source,
          scope: pkg.scope,
          cwd,
        }),
      });
      const d = (await res.json()) as { success?: boolean; error?: string };
      if (!res.ok || d.error) return;
      loadPackages();
    } catch {
      // ignore
    } finally {
      setUpdating(false);
    }
  }, [cwd, loadPackages]);

  const selectedPkg = packages.find((p) => p.source === selectedId) ?? null;

  // Group packages by scope for the left panel
  const groups: { label: string; packages: InstalledPackage[] }[] = [];
  for (const grpLabel of ["project", "global"]) {
    const grpPkgs = packages.filter((p) => scopeLabel(p.scope) === grpLabel);
    if (grpPkgs.length > 0) groups.push({ label: grpLabel, packages: grpPkgs });
  }

  return (
    <div
      style={{
        position: "fixed",
        inset: 0,
        zIndex: 1000,
        background: "rgba(0,0,0,0.35)",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
      }}
      onClick={(e) => {
        if (e.target === e.currentTarget) onClose();
      }}
    >
      <div
        style={{
          width: 860,
          height: "78vh",
          background: "var(--bg)",
          border: "1px solid var(--border)",
          borderRadius: 10,
          display: "flex",
          flexDirection: "column",
          boxShadow: "0 8px 32px rgba(0,0,0,0.18)",
          overflow: "hidden",
        }}
      >
        {/* ── Header ── */}
        <div
          style={{
            display: "flex",
            alignItems: "center",
            justifyContent: "space-between",
            padding: "12px 18px",
            borderBottom: "1px solid var(--border)",
            flexShrink: 0,
          }}
        >
          <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
            <span style={{ fontSize: 15, fontWeight: 700, color: "var(--text)" }}>
              Packages
            </span>
            <code
              style={{
                fontSize: 11,
                color: "var(--text-muted)",
                fontFamily: "var(--font-mono)",
                maxWidth: 320,
                overflow: "hidden",
                textOverflow: "ellipsis",
                whiteSpace: "nowrap",
              }}
            >
              {shortenPath(cwd)}
            </code>
          </div>
          <button
            onClick={onClose}
            style={{
              background: "none",
              border: "none",
              color: "var(--text-muted)",
              cursor: "pointer",
              fontSize: 20,
              lineHeight: 1,
              padding: "2px 6px",
            }}
          >
            ×
          </button>
        </div>

        {/* ── Body ── */}
        <div style={{ flex: 1, display: "flex", overflow: "hidden" }}>
          {/* Left: package list */}
          <div
            style={{
              width: 210,
              borderRight: "1px solid var(--border)",
              display: "flex",
              flexDirection: "column",
              flexShrink: 0,
              background: "var(--bg-panel)",
            }}
          >
            <div style={{ flex: 1, overflowY: "auto", padding: "8px 6px" }}>
              {loading ? (
                <div style={{ padding: "10px 8px", fontSize: 12, color: "var(--text-muted)" }}>
                  Loading…
                </div>
              ) : error ? (
                <div style={{ padding: "10px 8px", fontSize: 11, color: "#f87171" }}>
                  {error}
                </div>
              ) : packages.length === 0 ? (
                <div style={{ padding: "10px 8px", fontSize: 11, color: "var(--text-dim)" }}>
                  No packages installed
                </div>
              ) : (
                groups.map(({ label: grpLabel, packages: grpPkgs }) => (
                  <div key={grpLabel} style={{ marginBottom: 6 }}>
                    <div
                      style={{
                        padding: "4px 8px 3px",
                        fontSize: 10,
                        fontWeight: 600,
                        color: "var(--text-dim)",
                        textTransform: "uppercase",
                        letterSpacing: "0.06em",
                      }}
                    >
                      {grpLabel}
                    </div>
                    {grpPkgs.map((pkg) => {
                      const isSelected = !addMode && selectedId === pkg.source;
                      const resourceCounts = pkg.resources.reduce(
                        (acc, r) => {
                          if (r.enabled) acc.enabled++;
                          acc.total++;
                          return acc;
                        },
                        { enabled: 0, total: 0 },
                      );
                      // Count resource types
                      const types = new Set(pkg.resources.map((r) => r.type));

                      return (
                        <div
                          key={pkg.source}
                          onClick={() => {
                            setSelectedId(pkg.source);
                            setAddMode(false);
                          }}
                          style={{
                            display: "flex",
                            flexDirection: "column",
                            gap: 3,
                            padding: "8px",
                            borderRadius: 5,
                            cursor: "pointer",
                            background: isSelected ? "var(--bg-selected)" : "none",
                          }}
                          onMouseEnter={(e) => {
                            if (!isSelected) e.currentTarget.style.background = "var(--bg-hover)";
                          }}
                          onMouseLeave={(e) => {
                            if (!isSelected) e.currentTarget.style.background = "none";
                          }}
                        >
                          <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
                            <span
                              style={{
                                flexShrink: 0,
                                width: 7,
                                height: 7,
                                borderRadius: "50%",
                                background: resourceCounts.enabled > 0 ? "var(--accent)" : "var(--border)",
                                boxShadow: resourceCounts.enabled > 0 ? "0 0 4px var(--accent)" : "none",
                              }}
                            />
                            <span
                              style={{
                                fontSize: 12,
                                fontWeight: isSelected ? 600 : 400,
                                color: "var(--text)",
                                fontFamily: "var(--font-mono)",
                                flex: 1,
                                overflow: "hidden",
                                textOverflow: "ellipsis",
                                whiteSpace: "nowrap",
                              }}
                            >
                              {pkg.name}
                            </span>
                          </div>
                          <div style={{ display: "flex", gap: 3, paddingLeft: 12, flexWrap: "wrap" }}>
                            {[...types].map((t) => (
                              <span
                                key={t}
                                style={{
                                  fontSize: 9,
                                  padding: "0 3px",
                                  borderRadius: 2,
                                  background: RESOURCE_LABELS[t].bg,
                                  color: RESOURCE_LABELS[t].color,
                                }}
                              >
                                {RESOURCE_LABELS[t].label}
                              </span>
                            ))}
                            {resourceCounts.total > 0 && (
                              <span style={{ fontSize: 9, color: "var(--text-dim)" }}>
                                {resourceCounts.enabled}/{resourceCounts.total}
                              </span>
                            )}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                ))
              )}
            </div>

            {/* Add package button */}
            <div style={{ padding: "8px 6px", borderTop: "1px solid var(--border)", flexShrink: 0 }}>
              <div
                onClick={() => setAddMode(true)}
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: 6,
                  padding: "7px 8px",
                  borderRadius: 5,
                  cursor: "pointer",
                  background: addMode ? "var(--bg-selected)" : "none",
                  color: addMode ? "var(--accent)" : "var(--text-dim)",
                  fontSize: 12,
                }}
                onMouseEnter={(e) => {
                  if (!addMode) e.currentTarget.style.background = "var(--bg-hover)";
                }}
                onMouseLeave={(e) => {
                  if (!addMode) e.currentTarget.style.background = "none";
                }}
              >
                <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <line x1="12" y1="5" x2="12" y2="19" />
                  <line x1="5" y1="12" x2="19" y2="12" />
                </svg>
                Add package
              </div>
            </div>
          </div>

          {/* Right: detail or add panel */}
          <div style={{ flex: 1, overflowY: "auto", padding: 20 }}>
            {addMode ? (
              <AddPackagePanel cwd={cwd} onInstalled={loadPackages} />
            ) : loading ? null : selectedPkg ? (
              <PackageDetail
                key={selectedPkg.source}
                pkg={selectedPkg}
                cwd={cwd}
                onToggleResource={toggleResource}
                onRemove={handleRemove}
                onUpdate={handleUpdate}
                toggling={toggling}
                removing={removing}
                updating={updating}
              />
            ) : (
              <div
                style={{
                  height: "100%",
                  display: "flex",
                  alignItems: "center",
                  justifyContent: "center",
                  color: "var(--text-dim)",
                  fontSize: 13,
                }}
              >
                {packages.length === 0
                  ? "No packages installed. Click \"Add package\" to get started."
                  : "Select a package"}
              </div>
            )}
          </div>
        </div>

        {/* ── Footer ── */}
        <div
          style={{
            display: "flex",
            alignItems: "center",
            justifyContent: "flex-end",
            padding: "10px 18px",
            borderTop: "1px solid var(--border)",
            flexShrink: 0,
          }}
        >
          <button
            onClick={onClose}
            style={{
              padding: "6px 14px",
              background: "none",
              border: "1px solid var(--border)",
              borderRadius: 6,
              color: "var(--text-muted)",
              cursor: "pointer",
              fontSize: 13,
            }}
          >
            Close
          </button>
        </div>
      </div>
    </div>
  );
}
