"use client";

import { useEffect, useState } from "react";
import { useParams } from "next/navigation";
import {
  Loader2, AlertCircle, Database, KeyRound, CircleDot,
  ShieldCheck, Mail, CreditCard, Sparkles, HardDrive,
  ExternalLink, Pencil, RotateCw,
  ChevronDown, ChevronRight,
} from "lucide-react";
import { useAnatomy, type Anatomy } from "@/components/project/use-anatomy";
import { DatabaseTableTree } from "@/components/project/database-table-tree";
import { TableViewer } from "@/components/project/table-viewer";

/**
 * Infrastructure tab — supporting plumbing the product runs on.
 *
 * Six fixed sub-areas, always visible (even when empty) so a founder
 * learns the model on a brand-new project:
 *
 *   Databases · Auth · Email · Payments · Models · Storage · Secrets
 *
 * Categories explicitly NOT shown (yet):
 *   - SMS, Analytics, Search, Monitoring — removed Apr 29 2026 to keep
 *     the surface focused on what's actually wired today. Add back when
 *     each gets real UX (not just env-var detection).
 *
 * Storage is special: it's the workspace's bundled GCS-via-HMAC bucket
 * (S3-compatible) — not a third-party detection. Every workspace gets
 * one auto-provisioned, so the tile is always there with status.
 */

type ProviderCategory = Anatomy["infrastructure"]["providers"][number]["category"];

type CategoryKey =
  | "databases"
  | "auth" | "email" | "payments" | "llm"
  | "storage"
  | "secrets";

type Selection =
  | { kind: "category"; category: CategoryKey }
  | { kind: "database"; uuid: string }
  | { kind: "table";    dbUuid: string; schema: string; name: string }
  | { kind: "provider"; id: string }
  | { kind: "storage" }
  | { kind: "secrets";  resourceUuid: string }
  | null;

interface CategoryDef {
  key: CategoryKey;
  label: string;
  icon: React.ComponentType<{ size?: number; style?: React.CSSProperties }>;
  blurb: string;
  examples: string[];
  /** Vendor → external dashboard URL */
  dashboards?: Record<string, string>;
}

const INFRA_NUDGE = {
  databases: 'No database yet. Try: "Add a Postgres database to my project"',
  auth:      'No auth provider connected. Try: "Add Google OAuth to my app"',
  email:     'No email provider. Try: "Set up email sending with Resend"',
  payments:  'No payment provider. Try: "Connect Stripe to my project"',
  llm:       'No LLM connected. Try: "Add an OpenAI key to this project"',
  secrets:   'No secrets stored yet. Try: "Add my Stripe secret key"',
} as const;

const CATEGORIES: CategoryDef[] = [
  {
    key: "databases", label: "Databases", icon: Database,
    blurb: "Postgres, Redis, Mongo, ClickHouse — anything your app reads or writes from.",
    examples: ["PostgreSQL", "Redis", "MongoDB", "ClickHouse", "MySQL"],
  },
  {
    key: "auth", label: "Auth", icon: ShieldCheck,
    blurb: "Identity, sessions, SSO. Detected from env-var keys (CLERK_*, AUTH0_*, NEXTAUTH_*…).",
    examples: ["Clerk", "Auth0", "Supabase Auth", "NextAuth", "WorkOS", "SuperTokens"],
    dashboards: { Clerk: "https://dashboard.clerk.com", Auth0: "https://manage.auth0.com", "Supabase Auth": "https://supabase.com/dashboard", WorkOS: "https://dashboard.workos.com" },
  },
  {
    key: "email", label: "Email", icon: Mail,
    blurb: "Transactional + outbound email. SMS is intentionally not here yet.",
    examples: ["Resend", "Postmark", "SendGrid", "Mailgun", "AWS SES", "Loops"],
    dashboards: { Resend: "https://resend.com/emails", Postmark: "https://account.postmarkapp.com", SendGrid: "https://app.sendgrid.com", Mailgun: "https://app.mailgun.com", "AWS SES": "https://console.aws.amazon.com/ses" },
  },
  {
    key: "payments", label: "Payments", icon: CreditCard,
    blurb: "Stripe / Paddle / LemonSqueezy. Connect Stripe to enable webhook + checkout setup (coming soon).",
    examples: ["Stripe", "LemonSqueezy", "Paddle"],
    dashboards: { Stripe: "https://dashboard.stripe.com", LemonSqueezy: "https://app.lemonsqueezy.com", Paddle: "https://vendors.paddle.com" },
  },
  {
    key: "llm", label: "Models", icon: Sparkles,
    blurb: "AI models the project calls. BYOK (Bring Your Own Key) lets you use your own provider keys instead of platform defaults.",
    examples: ["OpenAI", "Anthropic", "Google AI", "Mistral", "Cohere", "Groq", "OpenRouter"],
    dashboards: { OpenAI: "https://platform.openai.com", Anthropic: "https://console.anthropic.com", "Google AI": "https://aistudio.google.com", Mistral: "https://console.mistral.ai", Groq: "https://console.groq.com", OpenRouter: "https://openrouter.ai" },
  },
  {
    key: "storage", label: "Storage", icon: HardDrive,
    blurb: "Vibn provisions an S3-compatible bucket per workspace. Every project in the workspace shares it.",
    examples: ["GCS bucket (S3-compatible)"],
  },
  {
    key: "secrets", label: "Secrets", icon: KeyRound,
    blurb: "Every env var across every app + service. Values are never read here; rotate via the AI chat.",
    examples: [],
  },
];

function categoryDef(key: CategoryKey): CategoryDef {
  return CATEGORIES.find(c => c.key === key)!;
}

export default function InfrastructureTab() {
  const params = useParams();
  const projectId = params.projectId as string;
  const { anatomy, loading, error } = useAnatomy(projectId);

  const [selection, setSelection] = useState<Selection>(null);
  /** Which database tiles are expanded inline to show their tables. */
  const [expandedDbs, setExpandedDbs] = useState<Set<string>>(new Set());

  const toggleDb = (uuid: string) => {
    setExpandedDbs(prev => {
      const next = new Set(prev);
      if (next.has(uuid)) next.delete(uuid); else next.add(uuid);
      return next;
    });
  };

  // Auto-expand the first database tile once anatomy lands so the user
  // sees tables immediately on a project that has one DB.
  useEffect(() => {
    if (anatomy && anatomy.infrastructure.databases[0] && expandedDbs.size === 0) {
      setExpandedDbs(new Set([anatomy.infrastructure.databases[0].uuid]));
    }
  }, [anatomy, expandedDbs.size]);

  const showLoading = loading && !anatomy;

  return (
    <div style={pageWrap}>
      <div style={grid}>
        {/* ── Left rail ── */}
        <section style={leftCol}>
          {showLoading && (
            <Inline><Loader2 size={13} className="animate-spin" /> Loading…</Inline>
          )}
          {error && !showLoading && (
            <Inline><AlertCircle size={13} /> {error}</Inline>
          )}

          {anatomy && CATEGORIES.map(def => (
            <CategoryRail
              key={def.key}
              def={def}
              anatomy={anatomy}
              selection={selection}
              onSelect={setSelection}
              projectId={projectId}
              expandedDbs={expandedDbs}
              onToggleDb={toggleDb}
            />
          ))}
        </section>

        {/* ── Right pane ── */}
        <aside style={rightCol}>
          <h3 style={heading}>{paneHeading(selection, anatomy)}</h3>
          <div style={panel}>
            {!anatomy && <Empty>Loading…</Empty>}
            {anatomy && !selection && <Overview anatomy={anatomy} onJump={setSelection} />}
            {anatomy && selection?.kind === "category" && (
              <CategoryDetail def={categoryDef(selection.category)} anatomy={anatomy} onJump={setSelection} />
            )}
            {anatomy && selection?.kind === "database" && (
              <DatabaseDetail uuid={selection.uuid} anatomy={anatomy} />
            )}
            {anatomy && selection?.kind === "table" && (
              <TableViewer
                projectId={projectId}
                dbUuid={selection.dbUuid}
                schema={selection.schema}
                table={selection.name}
              />
            )}
            {anatomy && selection?.kind === "provider" && (
              <ProviderDetail id={selection.id} anatomy={anatomy} />
            )}
            {anatomy && selection?.kind === "storage" && (
              <StorageDetail anatomy={anatomy} />
            )}
            {anatomy && selection?.kind === "secrets" && (
              <SecretsDetail resourceUuid={selection.resourceUuid} anatomy={anatomy} />
            )}
          </div>
        </aside>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────
// Left-rail group per category
// ──────────────────────────────────────────────────

function CategoryRail({
  def, anatomy, selection, onSelect,
  projectId, expandedDbs, onToggleDb,
}: {
  def: CategoryDef;
  anatomy: Anatomy;
  selection: Selection;
  onSelect: (s: Selection) => void;
  projectId: string;
  expandedDbs: Set<string>;
  onToggleDb: (uuid: string) => void;
}) {
  const Icon = def.icon;
  const headerActive = selection?.kind === "category" && selection.category === def.key;

  // Storage is a single-tile category backed by workspace state
  if (def.key === "storage") {
    const s = anatomy.infrastructure.bundledStorage;
    const present = s.status !== "unprovisioned";
    const active = selection?.kind === "storage";
    return (
      <div style={railGroup}>
        <CategoryHeader def={def} count={present ? 1 : 0} active={headerActive} onClick={() => onSelect({ kind: "category", category: def.key })} />
        {present && (
          <div style={railItems}>
            <button
              type="button"
              onClick={() => onSelect({ kind: "storage" })}
              style={tileButtonStyle(active)}
              aria-pressed={active}
            >
              <HardDrive size={13} style={iconStyle} />
              <div style={tileBody}>
                <div style={tileLabel}>Workspace bucket</div>
                <div style={tileHint}>{s.bucketName ?? "provisioning…"}</div>
              </div>
              <CircleDot size={9} style={{ color: storageColor(s.status), flexShrink: 0 }} />
            </button>
          </div>
        )}
      </div>
    );
  }

  // Databases render as expandable cards (like Codebases on Product) so
  // the table list lives inline; click a table to preview rows on the right.
  if (def.key === "databases") {
    const dbs = anatomy.infrastructure.databases;
    return (
      <div style={railGroup}>
        <CategoryHeader def={def} count={dbs.length} active={headerActive} onClick={() => onSelect({ kind: "category", category: def.key })} />
        {dbs.length > 0 && (
          <div style={railItems}>
            {dbs.map(db => {
              const open = expandedDbs.has(db.uuid);
              const tileSelected =
                selection?.kind === "database" && selection.uuid === db.uuid;
              const selectedTable =
                selection?.kind === "table" && selection.dbUuid === db.uuid
                  ? { schema: selection.schema, name: selection.name }
                  : undefined;

              return (
                <article key={db.uuid} style={dbCard}>
                  <button
                    type="button"
                    onClick={() => {
                      onToggleDb(db.uuid);
                      onSelect({ kind: "database", uuid: db.uuid });
                    }}
                    style={dbCardHeader}
                    aria-expanded={open}
                    aria-pressed={tileSelected}
                  >
                    <span style={chevronCell}>
                      {open
                        ? <ChevronDown size={13} style={{ color: INK.mid }} />
                        : <ChevronRight size={13} style={{ color: INK.mid }} />}
                    </span>
                    <Database size={13} style={iconStyle} />
                    <div style={tileBody}>
                      <div style={tileLabel}>{db.name}</div>
                      <div style={tileHint}>{db.type}</div>
                    </div>
                    <CircleDot size={9} style={{ color: statusColor(db.status), flexShrink: 0 }} />
                  </button>
                  {open && (
                    <div style={dbCardBody}>
                      <DatabaseTableTree
                        projectId={projectId}
                        dbUuid={db.uuid}
                        selectedTable={selectedTable}
                        onSelectTable={({ schema, name }) =>
                          onSelect({ kind: "table", dbUuid: db.uuid, schema, name })
                        }
                      />
                    </div>
                  )}
                </article>
              );
            })}
          </div>
        )}
      </div>
    );
  }

  const items = itemsForCategory(def.key, anatomy);
  const count =
    def.key === "secrets"
      ? anatomy.infrastructure.secrets.total
      : items.length;

  return (
    <div style={railGroup}>
      <CategoryHeader def={def} count={count} active={headerActive} onClick={() => onSelect({ kind: "category", category: def.key })} />
      {items.length > 0 && (
        <div style={railItems}>
          {items.map(item => renderRailItem(def.key, item, selection, onSelect))}
        </div>
      )}
    </div>
  );
}

function CategoryHeader({
  def, count, active, onClick,
}: {
  def: CategoryDef; count: number; active: boolean; onClick: () => void;
}) {
  const Icon = def.icon;
  return (
    <button
      type="button"
      onClick={onClick}
      style={{
        ...railGroupHeader,
        background: active ? "#fffdf8" : "transparent",
        border: active ? `1px solid ${INK.ink}` : "1px solid transparent",
      }}
      aria-pressed={active}
    >
      <span style={railGroupTitle}>
        <Icon size={11} style={{ color: INK.muted, marginRight: 6, verticalAlign: "-1px" }} />
        {def.label}
      </span>
      <span style={countPill}>{count}</span>
    </button>
  );
}

function itemsForCategory(key: CategoryKey, a: Anatomy): unknown[] {
  if (key === "databases") return a.infrastructure.databases;
  if (key === "secrets")   return a.infrastructure.secrets.byResource;
  if (key === "storage")   return [];
  return a.infrastructure.providers.filter(p => p.category === key);
}

function renderRailItem(
  key: CategoryKey,
  item: unknown,
  selection: Selection,
  onSelect: (s: Selection) => void
) {
  if (key === "databases") {
    const db = item as Anatomy["infrastructure"]["databases"][number];
    const active = selection?.kind === "database" && selection.uuid === db.uuid;
    return (
      <button
        key={db.uuid}
        type="button"
        onClick={() => onSelect({ kind: "database", uuid: db.uuid })}
        style={tileButtonStyle(active)}
        aria-pressed={active}
      >
        <Database size={13} style={iconStyle} />
        <div style={tileBody}>
          <div style={tileLabel}>{db.name}</div>
          <div style={tileHint}>{db.type}</div>
        </div>
        <CircleDot size={9} style={{ color: statusColor(db.status), flexShrink: 0 }} />
      </button>
    );
  }
  if (key === "secrets") {
    const r = item as Anatomy["infrastructure"]["secrets"]["byResource"][number];
    const active = selection?.kind === "secrets" && selection.resourceUuid === r.resourceUuid;
    return (
      <button
        key={r.resourceUuid}
        type="button"
        onClick={() => onSelect({ kind: "secrets", resourceUuid: r.resourceUuid })}
        style={tileButtonStyle(active)}
        aria-pressed={active}
      >
        <KeyRound size={13} style={iconStyle} />
        <div style={tileBody}>
          <div style={tileLabel}>{r.resourceName}</div>
          <div style={tileHint}>{r.resourceKind}</div>
        </div>
        <span style={countPill}>{r.count}</span>
      </button>
    );
  }
  // Provider
  const p = item as Anatomy["infrastructure"]["providers"][number];
  const active = selection?.kind === "provider" && selection.id === p.id;
  const totalKeys = p.attachments.reduce((n, a) => n + a.keys.length, 0);
  return (
    <button
      key={p.id}
      type="button"
      onClick={() => onSelect({ kind: "provider", id: p.id })}
      style={tileButtonStyle(active)}
      aria-pressed={active}
    >
      <Sparkles size={13} style={iconStyle} />
      <div style={tileBody}>
        <div style={tileLabel}>{p.vendor}</div>
        <div style={tileHint}>{totalKeys} key{totalKeys === 1 ? "" : "s"}</div>
      </div>
    </button>
  );
}

// ──────────────────────────────────────────────────
// Right-pane content
// ──────────────────────────────────────────────────

function Overview({
  anatomy, onJump,
}: { anatomy: Anatomy; onJump: (s: Selection) => void }) {
  const dbCount = anatomy.infrastructure.databases.length;
  const providerCount = anatomy.infrastructure.providers.length;
  const secretsCount = anatomy.infrastructure.secrets.total;
  const storage = anatomy.infrastructure.bundledStorage;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <div style={overviewGrid}>
        <OverviewStat
          label="Databases" value={dbCount}
          onClick={() => dbCount && onJump({ kind: "database", uuid: anatomy.infrastructure.databases[0].uuid })}
        />
        <OverviewStat
          label="Providers" value={providerCount}
          onClick={() => providerCount && onJump({ kind: "provider", id: anatomy.infrastructure.providers[0].id })}
        />
        <OverviewStat
          label="Storage"
          value={storage.status === "ready" ? 1 : 0}
          onClick={() => onJump({ kind: "storage" })}
        />
        <OverviewStat
          label="Secrets" value={secretsCount}
          onClick={() => secretsCount && onJump({ kind: "secrets", resourceUuid: anatomy.infrastructure.secrets.byResource[0].resourceUuid })}
        />
      </div>
    </div>
  );
}

function CategoryDetail({
  def, anatomy, onJump,
}: { def: CategoryDef; anatomy: Anatomy; onJump: (s: Selection) => void }) {
  const items = itemsForCategory(def.key, anatomy);
  const count =
    def.key === "secrets" ? anatomy.infrastructure.secrets.total :
    def.key === "storage" ? (anatomy.infrastructure.bundledStorage.status === "unprovisioned" ? 0 : 1) :
    items.length;

  // Per-category functional CTAs (no explainer prose).
  let actionRow: React.ReactNode = null;
  if (def.key === "payments" && !items.length) {
    actionRow = (
      <button type="button" disabled style={ctaButton}>
        Connect Stripe (coming soon)
      </button>
    );
  } else if (def.key === "storage") {
    actionRow = (
      <button type="button" onClick={() => onJump({ kind: "storage" })} style={ctaButton}>
        View workspace bucket
      </button>
    );
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      {actionRow}

      <div>
        <SectionTitle>
          Connected{" "}
          <span style={{ color: INK.mid, fontWeight: 400 }}>({count})</span>
        </SectionTitle>
        {count === 0 ? (
          <div style={emptyBox}>
            {INFRA_NUDGE[def.key as keyof typeof INFRA_NUDGE] ?? "None yet."}
          </div>
        ) : def.key === "secrets" ? (
          <div style={listBox}>
            {anatomy.infrastructure.secrets.byResource.map(r => (
              <div key={r.resourceUuid} style={listRow}>
                <span style={{ fontWeight: 600 }}>{r.resourceName}</span>
                <span style={{ color: INK.mid }}>{r.count} keys · {r.resourceKind}</span>
              </div>
            ))}
          </div>
        ) : def.key === "databases" ? (
          <div style={listBox}>
            {anatomy.infrastructure.databases.map(db => (
              <div key={db.uuid} style={listRow}>
                <span style={{ fontWeight: 600 }}>{db.name}</span>
                <span style={{ color: INK.mid }}>{db.type} · {db.status}</span>
              </div>
            ))}
          </div>
        ) : def.key === "storage" ? (
          <div style={listBox}>
            <div style={listRow}>
              <span style={{ fontWeight: 600 }}>Workspace bucket</span>
              <span style={{ color: INK.mid }}>{anatomy.infrastructure.bundledStorage.bucketName ?? "—"}</span>
            </div>
          </div>
        ) : (
          <div style={listBox}>
            {(items as Anatomy["infrastructure"]["providers"]).map(p => (
              <div key={p.id} style={listRow}>
                <span style={{ fontWeight: 600 }}>{p.vendor}</span>
                <span style={{ color: INK.mid }}>
                  {p.attachments.length} resource{p.attachments.length === 1 ? "" : "s"}
                </span>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function DatabaseDetail({ uuid, anatomy }: { uuid: string; anatomy: Anatomy }) {
  const db = anatomy.infrastructure.databases.find(d => d.uuid === uuid);
  if (!db) return <Empty>This database is no longer in the project.</Empty>;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <KvRow label="Engine"  value={db.type} />
      <KvRow label="Status"  value={db.status} dot={statusColor(db.status)} />
      <KvRow label="Visibility" value={db.isPublic ? "Public" : "In-cluster only"} />
      {db.publicPort != null && <KvRow label="Public port" value={String(db.publicPort)} />}
      {db.internalAddress && <KvRow label="Internal address" value={db.internalAddress} mono />}

      <div>
        <SectionTitle>Connection env</SectionTitle>
        <div style={codeBox}>
          <code style={code}>{db.consumerEnvKey}={"<set in Coolify env>"}</code>
        </div>
      </div>
    </div>
  );
}

function ProviderDetail({ id, anatomy }: { id: string; anatomy: Anatomy }) {
  const p = anatomy.infrastructure.providers.find(x => x.id === id);
  if (!p) return <Empty>This provider is no longer detected.</Empty>;
  const def = categoryDef(p.category);
  const dashboard = def.dashboards?.[p.vendor];
  const totalKeys = p.attachments.reduce((n, a) => n + a.keys.length, 0);

  // Stripe-specific connection callout
  const isStripe = p.vendor === "Stripe";

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <KvRow label="Vendor"   value={p.vendor} />
      <KvRow label="Category" value={def.label} />
      <KvRow label="Keys" value={`${totalKeys} key${totalKeys === 1 ? "" : "s"} across ${p.attachments.length} resource${p.attachments.length === 1 ? "" : "s"}`} />

      {dashboard && (
        <a href={dashboard} target="_blank" rel="noreferrer" style={dashboardLink}>
          Open {p.vendor} dashboard <ExternalLink size={12} />
        </a>
      )}

      <div>
        <SectionTitle>Detected here</SectionTitle>
        {p.attachments.map(att => (
          <div key={att.resourceUuid} style={attachmentBlock}>
            <div style={attachmentHeader}>
              {att.resourceName} <span style={attachmentBadge}>{att.resourceKind}</span>
            </div>
            <div style={keyList}>
              {att.keys.map(k => <code key={k} style={keyChip}>{k}</code>)}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function StorageDetail({ anatomy }: { anatomy: Anatomy }) {
  const s = anatomy.infrastructure.bundledStorage;

  if (s.status === "unprovisioned") {
    return <Empty>No bucket provisioned yet.</Empty>;
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <KvRow label="Status" value={s.status} dot={storageColor(s.status)} />
      {s.bucketName && <KvRow label="Bucket" value={s.bucketName} mono />}
      {s.region && <KvRow label="Region" value={s.region} />}
      {s.hmacAccessId && <KvRow label="HMAC access id" value={s.hmacAccessId} mono />}

      <div>
        <SectionTitle>Connection env</SectionTitle>
        <div style={codeBox}>
          <code style={code}>
{`STORAGE_ENDPOINT=https://storage.googleapis.com
STORAGE_REGION=${s.region ?? "auto"}
STORAGE_BUCKET=${s.bucketName ?? "<bucket>"}
STORAGE_ACCESS_KEY_ID=${s.hmacAccessId ?? "<access-id>"}
STORAGE_SECRET_ACCESS_KEY=<set via AI>`}
          </code>
        </div>
      </div>

      {s.errorMessage && (
        <div style={errorBox}>
          <AlertCircle size={13} style={{ color: "#c5392b", marginRight: 6, verticalAlign: "-2px" }} />
          {s.errorMessage}
        </div>
      )}
    </div>
  );
}

function SecretsDetail({
  resourceUuid, anatomy,
}: { resourceUuid: string; anatomy: Anatomy }) {
  const r = anatomy.infrastructure.secrets.byResource.find(x => x.resourceUuid === resourceUuid);
  if (!r) return <Empty>This resource is no longer in the project.</Empty>;

  // Group keys by detected provider so the user sees Stripe / Resend /
  // OpenAI bunched together with an Other catch-all for unrecognised keys.
  const providerByKey = new Map<string, { vendor: string; category: string }>();
  for (const p of anatomy.infrastructure.providers) {
    const att = p.attachments.find(a => a.resourceUuid === resourceUuid);
    if (!att) continue;
    for (const k of att.keys) {
      providerByKey.set(k, { vendor: p.vendor, category: categoryDef(p.category as CategoryKey).label });
    }
  }

  const groups = new Map<string, { label: string; keys: string[] }>();
  for (const k of r.keys) {
    const tag = providerByKey.get(k);
    const groupKey = tag ? `${tag.vendor}` : "Other";
    if (!groups.has(groupKey)) {
      groups.set(groupKey, { label: tag ? `${tag.vendor} · ${tag.category}` : "Other (project-defined)", keys: [] });
    }
    groups.get(groupKey)!.keys.push(k);
  }

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <KvRow label="Resource" value={r.resourceName} />
      <KvRow label="Kind"     value={r.resourceKind} />
      <KvRow label="Total env vars" value={String(r.count)} />

      <div>
        <SectionTitle>Keys</SectionTitle>
        <div style={listBox}>
          {[...groups.values()].map(g => (
            <div key={g.label} style={{ borderBottom: `1px solid ${INK.borderSoft}` }}>
              <div style={secretGroupHeader}>{g.label}</div>
              {g.keys.map(k => (
                <div key={k} style={secretRow}>
                  <code style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontSize: "0.78rem", color: INK.ink }}>
                    {k}
                  </code>
                  <div style={secretActions}>
                    <button type="button" disabled style={iconButton} title="Edit value (coming soon)">
                      <Pencil size={12} />
                    </button>
                    <button type="button" disabled style={iconButton} title="Rotate (coming soon)">
                      <RotateCw size={12} />
                    </button>
                  </div>
                </div>
              ))}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────
// Tiny helpers
// ──────────────────────────────────────────────────

function paneHeading(s: Selection, a: Anatomy | null): string {
  if (!a) return "Details";
  if (!s) return "Overview";
  if (s.kind === "category") return `About · ${categoryDef(s.category).label}`;
  if (s.kind === "database") return `Database · ${a.infrastructure.databases.find(x => x.uuid === s.uuid)?.name ?? ""}`;
  if (s.kind === "table")    return `Preview · ${s.schema === "public" ? s.name : `${s.schema}.${s.name}`}`;
  if (s.kind === "provider") return `Provider · ${a.infrastructure.providers.find(x => x.id === s.id)?.vendor ?? ""}`;
  if (s.kind === "storage")  return "Storage · Workspace bucket";
  if (s.kind === "secrets")  return `Secrets · ${a.infrastructure.secrets.byResource.find(x => x.resourceUuid === s.resourceUuid)?.resourceName ?? ""}`;
  return "Details";
}

function statusColor(status: string) {
  const s = (status ?? "").toLowerCase();
  if (s.includes("running") || s.includes("healthy")) return "#2e7d32";
  if (s.includes("starting") || s.includes("deploying")) return "#d4a04a";
  if (s.includes("exit") || s.includes("fail") || s.includes("unhealthy")) return "#c5392b";
  return "#a09a90";
}
function storageColor(status: string) {
  if (status === "ready") return "#2e7d32";
  if (status === "pending" || status === "partial") return "#d4a04a";
  if (status === "error") return "#c5392b";
  return "#a09a90";
}

function tileButtonStyle(active: boolean): React.CSSProperties {
  return {
    ...railItem,
    borderColor: active ? INK.ink : INK.borderSoft,
    boxShadow:   active ? `0 0 0 1px ${INK.ink}` : "none",
    background:  active ? "#fffdf8" : INK.cardBg,
  };
}

function KvRow({
  label, value, dot, mono,
}: { label: string; value: string; dot?: string; mono?: boolean }) {
  return (
    <div style={detailRow}>
      <span style={detailLabel}>{label}</span>
      <span style={detailValue}>
        {dot && <CircleDot size={9} style={{ color: dot, marginRight: 6 }} />}
        <span style={mono ? { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontSize: "0.8rem" } : {}}>
          {value}
        </span>
      </span>
    </div>
  );
}

function SectionTitle({ children }: { children: React.ReactNode }) {
  return <div style={sectionTitle}>{children}</div>;
}

function Para({ children, style }: { children: React.ReactNode; style?: React.CSSProperties }) {
  return <p style={{ ...para, ...style }}>{children}</p>;
}

function Inline({ children }: { children: React.ReactNode }) {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 8,
      padding: "12px 14px", fontSize: "0.82rem", color: INK.mid,
      background: INK.cardBg, border: `1px solid ${INK.borderSoft}`, borderRadius: 8,
    }}>
      {children}
    </div>
  );
}

function Empty({ children }: { children: React.ReactNode }) {
  return (
    <div style={{
      flex: 1, display: "flex", alignItems: "center", justifyContent: "center",
      color: INK.mid, fontSize: "0.85rem", padding: "32px 16px", textAlign: "center",
    }}>
      {children}
    </div>
  );
}

function OverviewStat({
  label, value, onClick,
}: { label: string; value: number; onClick?: () => void }) {
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={!value}
      style={{
        ...overviewStat,
        cursor: value ? "pointer" : "default",
        opacity: value ? 1 : 0.6,
      }}
    >
      <div style={overviewStatValue}>{value}</div>
      <div style={overviewStatLabel}>{label}</div>
    </button>
  );
}

// ──────────────────────────────────────────────────
// Tokens
// ──────────────────────────────────────────────────

const INK = {
  ink: "#1a1a1a",
  mid: "#5f5e5a",
  muted: "#a09a90",
  border: "#e8e4dc",
  borderSoft: "#efebe1",
  cardBg: "#fff",
  fontSans: '"Outfit", "Inter", ui-sans-serif, sans-serif',
} as const;

const pageWrap: React.CSSProperties = {
  padding: "28px 48px 48px",
  fontFamily: INK.fontSans,
  color: INK.ink,
};
const grid: React.CSSProperties = {
  display: "grid",
  gridTemplateColumns: "minmax(280px, 360px) minmax(0, 1fr)",
  gap: 28,
  maxWidth: 1400,
  margin: "0 auto",
  alignItems: "stretch",
};
const leftCol: React.CSSProperties = {
  minWidth: 0, display: "flex", flexDirection: "column", gap: 14,
};
const rightCol: React.CSSProperties = {
  minWidth: 0, display: "flex", flexDirection: "column",
};
const heading: React.CSSProperties = {
  fontSize: "0.72rem", fontWeight: 600, letterSpacing: "0.12em",
  textTransform: "uppercase", color: INK.muted, margin: "0 0 14px",
};
const railGroup: React.CSSProperties = { display: "flex", flexDirection: "column" };
const railGroupHeader: React.CSSProperties = {
  display: "flex", alignItems: "center", justifyContent: "space-between",
  padding: "6px 8px", borderRadius: 6,
  cursor: "pointer", font: "inherit", color: "inherit",
};
const railGroupTitle: React.CSSProperties = {
  fontSize: "0.68rem", fontWeight: 600, letterSpacing: "0.12em",
  textTransform: "uppercase", color: INK.muted,
};
const countPill: React.CSSProperties = {
  fontSize: "0.7rem", fontWeight: 600, color: INK.mid,
  padding: "1px 7px", borderRadius: 999, background: "#f3eee4",
};
const railItems: React.CSSProperties = { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 };
const railItem: React.CSSProperties = {
  display: "flex", alignItems: "center", gap: 10,
  width: "100%", padding: "10px 12px",
  border: `1px solid ${INK.borderSoft}`, borderRadius: 8,
  cursor: "pointer", font: "inherit", color: "inherit",
  transition: "border-color 0.12s, background 0.12s, box-shadow 0.12s",
};
const railEmptyButton: React.CSSProperties = {
  width: "100%", textAlign: "left",
  padding: "8px 12px", fontSize: "0.74rem", color: INK.muted,
  background: "transparent",
  border: `1px dashed ${INK.borderSoft}`, borderRadius: 8,
  lineHeight: 1.45, cursor: "pointer", marginTop: 4,
  fontFamily: "inherit",
};
const tileBody: React.CSSProperties = { minWidth: 0, textAlign: "left", flex: 1 };
const dbCard: React.CSSProperties = {
  background: INK.cardBg, border: `1px solid ${INK.borderSoft}`,
  borderRadius: 10, overflow: "hidden",
};
const dbCardHeader: React.CSSProperties = {
  display: "flex", alignItems: "center", gap: 8, width: "100%",
  padding: "10px 12px", background: "transparent", border: "none",
  cursor: "pointer", font: "inherit", color: "inherit",
};
const dbCardBody: React.CSSProperties = {
  padding: "8px 10px 12px", borderTop: `1px solid ${INK.borderSoft}`,
};
const chevronCell: React.CSSProperties = {
  width: 14, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
};
const tileLabel: React.CSSProperties = {
  fontSize: "0.85rem", fontWeight: 600, color: INK.ink, marginBottom: 2,
};
const tileHint: React.CSSProperties = {
  fontSize: "0.74rem", color: INK.mid, lineHeight: 1.4, textTransform: "capitalize",
};
const iconStyle: React.CSSProperties = { color: INK.mid, flexShrink: 0 };
const panel: React.CSSProperties = {
  background: INK.cardBg, border: `1px solid ${INK.border}`, borderRadius: 10,
  padding: 18, flex: 1, minHeight: 0, display: "flex", flexDirection: "column",
  overflowY: "auto",
};
const detailRow: React.CSSProperties = {
  display: "flex", alignItems: "center", justifyContent: "space-between",
  padding: "10px 4px", borderBottom: `1px solid ${INK.borderSoft}`,
};
const detailLabel: React.CSSProperties = {
  fontSize: "0.72rem", fontWeight: 600, letterSpacing: "0.06em",
  textTransform: "uppercase", color: INK.muted,
};
const detailValue: React.CSSProperties = {
  fontSize: "0.85rem", color: INK.ink, display: "inline-flex", alignItems: "center",
};
const sectionTitle: React.CSSProperties = {
  fontSize: "0.72rem", fontWeight: 600, letterSpacing: "0.08em",
  textTransform: "uppercase", color: INK.muted, marginBottom: 8,
};
const para: React.CSSProperties = {
  margin: 0, fontSize: "0.85rem", color: INK.ink, lineHeight: 1.55,
};
const overviewGrid: React.CSSProperties = {
  display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", gap: 10,
};
const overviewStat: React.CSSProperties = {
  display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 4,
  padding: "14px 16px", border: `1px solid ${INK.borderSoft}`, borderRadius: 10,
  background: INK.cardBg, color: INK.ink, font: "inherit",
};
const overviewStatValue: React.CSSProperties = {
  fontSize: "1.6rem", fontWeight: 600, lineHeight: 1,
};
const overviewStatLabel: React.CSSProperties = {
  fontSize: "0.7rem", color: INK.muted, letterSpacing: "0.08em", textTransform: "uppercase",
};
const chipRow: React.CSSProperties = { display: "flex", flexWrap: "wrap", gap: 6 };
const chip: React.CSSProperties = {
  fontSize: "0.74rem", color: INK.mid,
  padding: "3px 9px", borderRadius: 999,
  background: "#fafaf6", border: `1px solid ${INK.borderSoft}`,
};
const listBox: React.CSSProperties = {
  display: "flex", flexDirection: "column",
  border: `1px solid ${INK.borderSoft}`, borderRadius: 8, overflow: "hidden",
};
const listRow: React.CSSProperties = {
  display: "flex", alignItems: "center", justifyContent: "space-between",
  padding: "10px 12px", fontSize: "0.82rem",
  borderBottom: `1px solid ${INK.borderSoft}`,
};
const emptyBox: React.CSSProperties = {
  padding: "12px 14px", fontSize: "0.82rem", color: INK.mid,
  background: "#fafaf6", border: `1px dashed ${INK.borderSoft}`, borderRadius: 8,
  lineHeight: 1.5,
};
const errorBox: React.CSSProperties = {
  padding: "12px 14px", fontSize: "0.82rem", color: "#7a1f15",
  background: "#fbe9e7", border: `1px solid #f4c2bc`, borderRadius: 8,
  lineHeight: 1.5,
};
const codeBox: React.CSSProperties = {
  padding: "10px 12px",
  background: "#fafaf6",
  border: `1px solid ${INK.borderSoft}`, borderRadius: 8,
  overflowX: "auto",
  whiteSpace: "pre",
};
const code: React.CSSProperties = {
  fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
  fontSize: "0.78rem", color: INK.ink, whiteSpace: "pre",
};
const inlineCode: React.CSSProperties = {
  fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
  fontSize: "0.78rem", padding: "1px 5px",
  background: "#fafaf6", border: `1px solid ${INK.borderSoft}`, borderRadius: 4,
};
const dashboardLink: React.CSSProperties = {
  display: "inline-flex", alignItems: "center", gap: 6,
  padding: "8px 12px", borderRadius: 8,
  background: INK.ink, color: "#fff", fontSize: "0.82rem", fontWeight: 600,
  textDecoration: "none", alignSelf: "flex-start",
};
const ctaButton: React.CSSProperties = {
  alignSelf: "flex-start",
  padding: "8px 14px", borderRadius: 8,
  background: INK.ink, color: "#fff", fontSize: "0.82rem", fontWeight: 600,
  border: "none", cursor: "pointer", font: "inherit",
};
const attachmentBlock: React.CSSProperties = {
  padding: "10px 12px", marginTop: 8,
  background: "#fafaf6", borderRadius: 8,
};
const attachmentHeader: React.CSSProperties = {
  fontSize: "0.82rem", fontWeight: 600, color: INK.ink, marginBottom: 6,
  display: "flex", alignItems: "center", gap: 8,
};
const attachmentBadge: React.CSSProperties = {
  fontSize: "0.62rem", fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase",
  color: INK.mid, background: "#ece6da", padding: "1px 6px", borderRadius: 4,
};
const keyList: React.CSSProperties = {
  display: "flex", flexWrap: "wrap", gap: 4,
};
const keyChip: React.CSSProperties = {
  fontSize: "0.7rem", color: INK.mid, padding: "2px 6px",
  background: INK.cardBg, border: `1px solid ${INK.borderSoft}`, borderRadius: 4,
  fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
};
const secretGroupHeader: React.CSSProperties = {
  padding: "8px 12px", fontSize: "0.7rem", color: INK.muted,
  letterSpacing: "0.08em", textTransform: "uppercase",
  background: "#fafaf6",
};
const secretRow: React.CSSProperties = {
  display: "flex", alignItems: "center", justifyContent: "space-between",
  padding: "8px 12px",
  borderTop: `1px solid ${INK.borderSoft}`,
};
const secretActions: React.CSSProperties = {
  display: "flex", gap: 4,
};
const iconButton: React.CSSProperties = {
  display: "inline-flex", alignItems: "center", justifyContent: "center",
  width: 24, height: 24, padding: 0,
  background: "transparent", border: `1px solid ${INK.borderSoft}`,
  borderRadius: 4, cursor: "not-allowed", color: INK.muted,
  font: "inherit",
};
