"use client";

/**
 * Schema Generator — AI-powered JSON-LD structured data generator.
 * Generates, validates, edits, and manages schema markup per page.
 */

import { useState, useEffect, useCallback } from "react";
import { useBrand } from "@/context/brand-context";
import { useBackgroundTasks } from "@/lib/stores/task-store";
import SchemaCodeEditor from "@/components/schema-code-editor";
import FeatureGate from "@/components/plan/feature-gate";
import {
  Code2, Sparkles, Loader2, Save, Trash2, Copy, Globe, Check,
  AlertCircle, CheckCircle2, Zap, ToggleLeft, ToggleRight, FileCode,
  ShieldCheck, Download,
} from "lucide-react";

interface SchemaRecord {
  id: string; pageUrl: string; schemaType: string; jsonLd: unknown;
  status: string; autoInject: boolean; updatedAt: string;
}

interface GenResult {
  recommendedTypes: string[];
  schemas: Array<{ type: string; jsonLd: Record<string, unknown> }>;
  reasoning: string;
}

/** Shape returned by /api/schema/validate — mirrors
 *  ValidationResult in src/lib/services/schema-generator.ts. Kept in
 *  sync manually; the route returns { valid, errors, warnings }. */
interface ValidationResult {
  valid: boolean;
  errors: string[];
  warnings: string[];
}

// Schema Generator is a Professional+ feature. FeatureGate wrapper
// catches direct navigations that bypass the sidebar's locked-click handler.
export default function SchemaGeneratorPageGated() {
  return (
    <FeatureGate feature="schema_generator">
      <SchemaGeneratorPage />
    </FeatureGate>
  );
}

function SchemaGeneratorPage() {
  const { brand } = useBrand();
  const { addTask, completeTask, failTask } = useBackgroundTasks();

  const [pageUrl, setPageUrl] = useState("");
  const [generating, setGenerating] = useState(false);
  const [genResult, setGenResult] = useState<GenResult | null>(null);
  const [schemas, setSchemas] = useState<SchemaRecord[]>([]);
  const [loading, setLoading] = useState(true);
  const [editingId, setEditingId] = useState<string | null>(null);
  const [editCode, setEditCode] = useState("");
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState<string | null>(null);
  // Per-saved-schema validation results, keyed by SchemaMarkup.id.
  // Populated on initial list-load so users see Valid / Invalid
  // badges without clicking; re-computed on "Validate" button click
  // for manually-edited schemas. Values are the direct response
  // from /api/schema/validate.
  const [validations, setValidations] = useState<Record<string, ValidationResult>>({});
  // Tracks which saved schemas are currently being re-validated —
  // used to show a spinner on the Validate button without blocking
  // other actions on the same row.
  const [validating, setValidating] = useState<Set<string>>(new Set());
  // Bulk-generate session. `bulkRunning` gates the button; bulkResult
  // is the final response from /api/schema/bulk-generate (succeeded /
  // failed / skipped / partial counts + per-URL rows). Held in state
  // rather than toast because users will want to review which pages
  // hit which errors, not a 2-second confirmation.
  const [bulkRunning, setBulkRunning] = useState(false);
  const [bulkResult, setBulkResult] = useState<{
    succeeded: number; failed: number; skipped: number; total: number;
    partial: boolean; message: string;
    results: Array<{ pageUrl: string; savedSchemaIds: string[]; error?: string }>;
  } | null>(null);

  function show(msg: string) { setToast(msg); setTimeout(() => setToast(null), 2000); }

  /** POST a single JSON-LD blob to /api/schema/validate and stash
   *  the result under the given schema id. Used on both the initial
   *  list load (auto-validate) and the per-row "Validate" button. */
  const runValidation = useCallback(async (id: string, jsonLd: unknown) => {
    setValidating((prev) => new Set(prev).add(id));
    try {
      const res = await fetch("/api/schema/validate", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ jsonLd }),
      });
      if (res.ok) {
        const data = await res.json() as ValidationResult;
        setValidations((prev) => ({ ...prev, [id]: data }));
      }
    } catch {
      // Silent — validation is a nice-to-have; a network blip shouldn't
      // wedge the UI. User can hit Validate again.
    } finally {
      setValidating((prev) => {
        const next = new Set(prev);
        next.delete(id);
        return next;
      });
    }
  }, []);

  /** Copy helper — writes to the clipboard and fires the toast.
   *  Falls back to execCommand on the rare browser that doesn't
   *  expose navigator.clipboard (old Firefox, insecure contexts). */
  async function copyToClipboard(text: string, successMsg: string) {
    try {
      if (typeof navigator !== "undefined" && navigator.clipboard) {
        await navigator.clipboard.writeText(text);
      } else {
        const ta = document.createElement("textarea");
        ta.value = text;
        document.body.appendChild(ta);
        ta.select();
        document.execCommand("copy");
        document.body.removeChild(ta);
      }
      show(successMsg);
    } catch {
      show("Copy failed");
    }
  }

  /** Export every saved schema as a single download. Two outputs so
   *  users can pick the one that matches their workflow:
   *    - HTML: a ready-to-paste document with each schema wrapped in
   *            <script type="application/ld+json"> and grouped under
   *            a page-URL comment header. Useful for "I want to send
   *            these to my dev team" hand-off.
   *    - JSON: a structured archive of every row (id, pageUrl,
   *            schemaType, jsonLd, status, autoInject, updatedAt).
   *            Useful for versioning, diffing, or bulk-reimport on
   *            another environment.
   *
   *  Zero-dependency: uses Blob + URL.createObjectURL + a synthetic
   *  anchor click. A ZIP-per-schema option would require bringing in
   *  jszip and isn't warranted for the typical <= 20 schemas most
   *  brands have.
   */
  function downloadAll(format: "html" | "json") {
    if (schemas.length === 0) { show("No schemas to download"); return; }
    const datestamp = new Date().toISOString().slice(0, 10);
    const hostSlug = (brand.domain || brand.name || "schemas").replace(/[^a-zA-Z0-9.-]+/g, "-").toLowerCase();
    let content: string;
    let mimeType: string;
    let ext: string;

    if (format === "json") {
      content = JSON.stringify(
        schemas.map((s) => ({
          id: s.id,
          pageUrl: s.pageUrl,
          schemaType: s.schemaType,
          status: s.status,
          autoInject: s.autoInject,
          updatedAt: s.updatedAt,
          jsonLd: s.jsonLd,
        })),
        null,
        2,
      );
      mimeType = "application/json";
      ext = "json";
    } else {
      // Group by pageUrl so a page with multiple schemas (e.g. FAQ
      // page with FAQPage + BreadcrumbList + Organization) renders
      // as one block the user can copy wholesale.
      const byUrl = new Map<string, SchemaRecord[]>();
      for (const s of schemas) {
        const existing = byUrl.get(s.pageUrl) ?? [];
        existing.push(s);
        byUrl.set(s.pageUrl, existing);
      }
      const blocks = Array.from(byUrl.entries()).map(([url, rows]) => {
        const scripts = rows.map((r) => {
          const json = JSON.stringify(r.jsonLd, null, 2);
          return `<script type="application/ld+json">\n${json}\n</script>`;
        }).join("\n\n");
        return `<!-- ═══════════════════════════════════════════════════════════════\n     Page: ${url}\n     Schemas: ${rows.map((r) => r.schemaType).join(", ")}\n     ═══════════════════════════════════════════════════════════════ -->\n${scripts}`;
      });
      content = `<!DOCTYPE html>\n<!--\n  meSEO schema export for ${brand.domain || brand.name}\n  Generated: ${new Date().toISOString()}\n  Schemas: ${schemas.length} across ${byUrl.size} page${byUrl.size === 1 ? "" : "s"}\n\n  Paste each <script> block into the <head> of the matching page.\n-->\n<html>\n<head>\n<title>Schema Markup Export — ${brand.domain || brand.name}</title>\n</head>\n<body>\n${blocks.join("\n\n")}\n</body>\n</html>\n`;
      mimeType = "text/html";
      ext = "html";
    }

    const blob = new Blob([content], { type: mimeType });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${hostSlug}-schemas-${datestamp}.${ext}`;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    // Revoke on the next tick — the download will have started by
    // then and holding the URL longer leaks a reference.
    setTimeout(() => URL.revokeObjectURL(url), 1000);
    show(`Downloaded ${schemas.length} schema${schemas.length === 1 ? "" : "s"} as ${ext.toUpperCase()}`);
  }

  const loadSchemas = useCallback(async () => {
    if (!brand?.id) return;
    setLoading(true);
    try {
      const res = await fetch(`/api/schema?brandId=${brand.id}`);
      if (res.ok) {
        const d = await res.json();
        const list: SchemaRecord[] = d.schemas || [];
        setSchemas(list);
        // Fire-and-forget initial validation for every row so users
        // land on the page already seeing green / red badges — no
        // extra click required to know whether a saved schema is
        // well-formed. Parallel requests: /api/schema/validate is
        // pure, no DB, so N concurrent calls don't pressure the pool.
        list.forEach((s) => { runValidation(s.id, s.jsonLd); });
      }
    } finally { setLoading(false); }
  }, [brand.id, runValidation]);

  useEffect(() => { loadSchemas(); }, [loadSchemas]);

  async function generate() {
    if (!pageUrl.trim() || generating) return;
    setGenerating(true);
    const taskId = addTask({ id: `schema_${Date.now()}`, type: "schema_generation", label: "Generating Schema...", brandId: brand.id, resultUrl: "/schema-generator" });
    try {
      const res = await fetch("/api/schema/generate", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ brandId: brand.id, pageUrl: pageUrl.trim() }),
      });
      if (res.ok) {
        const data = await res.json() as GenResult;
        setGenResult(data);
        completeTask(taskId, { resultUrl: "/schema-generator", resultSummary: `Generated ${data.schemas.length} schema(s): ${data.recommendedTypes.join(", ")}` });
      } else { const d = await res.json().catch(() => ({})); failTask(taskId, d.error || "Generation failed"); }
    } catch { failTask(taskId, "Network error"); }
    setGenerating(false);
  }

  /** Kick off bulk generation against the TrackedSite's top pages.
   *  Server derives the URL list (no need to pre-load page paths
   *  into the client) and runs up to 3 concurrent generators with
   *  a 110s wall budget. Each successful result is persisted to
   *  SchemaMarkup server-side; we just refresh the list on
   *  completion so the user sees every new row without a page
   *  reload. */
  async function triggerBulk() {
    if (bulkRunning) return;
    setBulkRunning(true);
    setBulkResult(null);
    const taskId = addTask({
      id: `schema_bulk_${Date.now()}`,
      type: "schema_bulk_generate",
      label: "Bulk generating schemas…",
      brandId: brand.id,
      resultUrl: "/schema-generator",
    });
    try {
      const res = await fetch("/api/schema/bulk-generate", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        // source=trackedSite uses the brand's real-traffic page list.
        // Falls back to GSC and finally the homepage server-side, so
        // a brand without Site Tag data still gets something useful.
        body: JSON.stringify({ brandId: brand.id, source: "trackedSite" }),
      });
      if (!res.ok) {
        const d = await res.json().catch(() => ({}));
        failTask(taskId, d.error || "Bulk generation failed");
        show(d.error || "Bulk generation failed");
        return;
      }
      const data = await res.json();
      setBulkResult(data);
      completeTask(taskId, {
        resultUrl: "/schema-generator",
        resultSummary: `Bulk: ${data.succeeded} succeeded, ${data.skipped} skipped, ${data.failed} failed`,
      });
      // Refresh the saved-schemas list so every newly-created row
      // appears with its validation badge. Server already persisted;
      // this is purely a client-side re-fetch.
      await loadSchemas();
    } catch {
      failTask(taskId, "Network error");
      show("Network error");
    } finally {
      setBulkRunning(false);
    }
  }

  async function saveSchema(type: string, jsonLd: Record<string, unknown>) {
    setSaving(true);
    try {
      const res = await fetch("/api/schema/save", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ brandId: brand.id, pageUrl: pageUrl.trim(), schemaType: type, jsonLd }),
      });
      if (res.ok) { show("Schema saved"); await loadSchemas(); }
    } finally { setSaving(false); }
  }

  async function updateSchema(id: string, data: Partial<SchemaRecord>) {
    await fetch(`/api/schema/${id}`, {
      method: "PUT", headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    await loadSchemas();
  }

  async function deleteSchema(id: string) {
    if (!confirm("Delete this schema?")) return;
    await fetch(`/api/schema/${id}`, { method: "DELETE" });
    if (editingId === id) { setEditingId(null); setEditCode(""); }
    await loadSchemas();
  }

  function startEdit(s: SchemaRecord) {
    setEditingId(s.id);
    setEditCode(JSON.stringify(s.jsonLd, null, 2));
  }

  async function saveEdit() {
    if (!editingId) return;
    try {
      const parsed = JSON.parse(editCode);
      await updateSchema(editingId, { jsonLd: parsed });
      // Re-validate immediately so the badge reflects the new JSON
      // instead of the pre-edit version. loadSchemas inside
      // updateSchema fires auto-validate for every row, so this is
      // technically redundant — but explicit here so a future
      // optimisation to skip the full reload doesn't silently drop
      // the validation refresh.
      runValidation(editingId, parsed);
      show("Schema updated");
      setEditingId(null);
    } catch { show("Invalid JSON"); }
  }

  const statusColors: Record<string, string> = {
    draft: "bg-slate-100 text-slate-600",
    deployed: "bg-green-50 text-green-700",
    verified: "bg-blue-50 text-blue-700",
  };

  return (
    <div className="space-y-6 max-w-5xl">
      <div>
        <h1 className="text-2xl font-bold text-slate-900">Schema Generator</h1>
        <p className="text-sm text-slate-500 mt-1">Generate and manage JSON-LD structured data for your pages</p>
      </div>

      {/* Generate bar */}
      <div className="bg-white rounded-xl border border-slate-200 p-5">
        <div className="flex items-center gap-3 flex-wrap">
          <div className="relative flex-1 min-w-[240px]">
            <Globe className="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2" />
            <input value={pageUrl} onChange={(e) => setPageUrl(e.target.value)} placeholder={`https://${brand.domain}/about or /services`}
              onKeyDown={(e) => e.key === "Enter" && generate()}
              className="w-full pl-10 pr-4 py-2.5 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/40 focus:border-blue-500" />
          </div>
          <button onClick={generate} disabled={generating || !pageUrl.trim()}
            className="flex items-center gap-2 px-5 py-2.5 bg-slate-900 text-white text-sm font-semibold rounded-lg hover:bg-slate-800 disabled:opacity-50">
            {generating ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
            {generating ? "Generating..." : "Generate Schema"}
          </button>
          {/* Bulk generation against the brand's top-traffic pages.
              Runs up to 20 URLs (server caps), 3 concurrent, 110s
              budget. Skips pages that already have a saved schema —
              users re-generate by deleting first. Separated
              visually (outline button + divider) so it reads as a
              distinct mode from the single-URL generator on the
              left. */}
          <div className="h-10 w-px bg-slate-200 hidden md:block" />
          <button
            onClick={triggerBulk}
            disabled={bulkRunning || generating}
            title="Generate schemas for every top-traffic page in one go"
            className="flex items-center gap-2 px-5 py-2.5 bg-white border border-slate-300 text-slate-700 text-sm font-semibold rounded-lg hover:bg-slate-50 hover:border-slate-400 disabled:opacity-50"
          >
            {bulkRunning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Zap className="w-4 h-4" />}
            {bulkRunning ? "Bulk generating…" : "Generate for All Pages"}
          </button>
        </div>
      </div>

      {/* Bulk result summary. Surfaces the aggregate outcome (green
          banner on full success, amber on partial / any failures)
          plus an expandable list of per-URL results so users can
          see which pages succeeded vs which hit errors. */}
      {bulkResult && (
        <div className={`rounded-xl border p-4 ${
          bulkResult.failed === 0 && !bulkResult.partial
            ? "bg-green-50 border-green-200"
            : "bg-amber-50 border-amber-200"
        }`}>
          <div className="flex items-start gap-3">
            {bulkResult.failed === 0 && !bulkResult.partial ? (
              <CheckCircle2 className="w-5 h-5 text-green-600 mt-0.5 shrink-0" />
            ) : (
              <AlertCircle className="w-5 h-5 text-amber-600 mt-0.5 shrink-0" />
            )}
            <div className="flex-1 min-w-0">
              <p className={`text-sm font-semibold ${bulkResult.failed === 0 && !bulkResult.partial ? "text-green-900" : "text-amber-900"}`}>
                {bulkResult.message}
              </p>
              <div className="flex gap-4 mt-2 text-xs tabular-nums">
                <span className="text-green-700 font-semibold">✓ {bulkResult.succeeded} saved</span>
                {bulkResult.skipped > 0 && <span className="text-slate-600">↷ {bulkResult.skipped} skipped (already exist)</span>}
                {bulkResult.failed > 0 && <span className="text-red-700 font-semibold">✗ {bulkResult.failed} failed</span>}
              </div>
              {/* Per-URL breakdown — collapsed initially (details/
                  summary native element) so a 20-URL run doesn't
                  dominate the page, but one click reveals every
                  row's outcome. */}
              {bulkResult.results.length > 0 && (
                <details className="mt-3">
                  <summary className="text-xs font-semibold cursor-pointer text-slate-700 hover:text-slate-900">
                    Show per-page results ({bulkResult.results.length})
                  </summary>
                  <ul className="mt-2 space-y-1 text-[11px]">
                    {bulkResult.results.map((r, i) => (
                      <li key={i} className="flex items-start gap-2">
                        {r.savedSchemaIds.length > 0 ? (
                          <CheckCircle2 className="w-3 h-3 text-green-600 mt-0.5 shrink-0" />
                        ) : (
                          <AlertCircle className="w-3 h-3 text-red-500 mt-0.5 shrink-0" />
                        )}
                        <span className="flex-1 min-w-0">
                          <span className="font-mono text-slate-700 truncate block" title={r.pageUrl}>
                            {r.pageUrl.replace(/^https?:\/\/[^/]+/, "") || r.pageUrl}
                          </span>
                          {r.error && <span className="text-red-700 text-[10px]">{r.error}</span>}
                          {r.savedSchemaIds.length > 0 && <span className="text-slate-500 text-[10px]">Saved {r.savedSchemaIds.length} schema{r.savedSchemaIds.length === 1 ? "" : "s"}</span>}
                        </span>
                      </li>
                    ))}
                  </ul>
                </details>
              )}
              <button
                onClick={() => setBulkResult(null)}
                className="text-[10px] text-slate-500 hover:text-slate-700 underline underline-offset-2 mt-2"
              >
                Dismiss
              </button>
            </div>
          </div>
        </div>
      )}

      {/* AI results */}
      {genResult && (
        <div className="bg-white rounded-xl border border-slate-200 p-5 space-y-5">
          <div className="flex items-start gap-3">
            <Zap className="w-5 h-5 text-violet-500 mt-0.5 shrink-0" />
            <div>
              <p className="text-sm font-semibold text-slate-800">AI Recommendation</p>
              <p className="text-xs text-slate-500 mt-0.5">{genResult.reasoning}</p>
              <div className="flex gap-1.5 mt-2">
                {genResult.recommendedTypes.map((t) => (
                  <span key={t} className="text-[10px] font-bold px-2 py-0.5 rounded-full bg-violet-50 text-violet-700 border border-violet-200">{t}</span>
                ))}
              </div>
            </div>
          </div>

          {genResult.schemas.map((s, i) => (
            <div key={i} className="border border-slate-200 rounded-lg p-4">
              <div className="flex items-center justify-between mb-3">
                <span className="text-xs font-bold px-2 py-0.5 rounded-md bg-slate-800 text-slate-200">{s.type}</span>
                <button onClick={() => saveSchema(s.type, s.jsonLd)} disabled={saving}
                  className="flex items-center gap-1.5 px-3 py-1.5 bg-slate-900 text-white text-xs font-semibold rounded-lg hover:bg-slate-800 disabled:opacity-50">
                  <Save className="w-3 h-3" /> Save
                </button>
              </div>
              <SchemaCodeEditor value={JSON.stringify(s.jsonLd, null, 2)} onChange={(v) => {
                try { const p = JSON.parse(v); genResult.schemas[i].jsonLd = p; setGenResult({ ...genResult }); } catch {}
              }} height="h-48" />
            </div>
          ))}
        </div>
      )}

      {/* Existing schemas table */}
      <div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
        <div className="px-5 py-4 border-b border-slate-100 flex items-center justify-between gap-3 flex-wrap">
          <h2 className="text-sm font-bold text-slate-800">Saved Schemas ({schemas.length})</h2>
          {/* Download-all export controls — only rendered once there's
              at least one schema to export. HTML is the "hand this
              to my dev team" format (ready-to-paste <script> blocks
              grouped by page); JSON is the "archive / migrate /
              diff" format. Both are zero-dep Blob downloads. */}
          {schemas.length > 0 && (
            <div className="flex items-center gap-2">
              <button
                onClick={() => downloadAll("html")}
                title="Download every schema as a single HTML file with ready-to-paste <script> blocks"
                className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-slate-600 bg-white border border-slate-200 rounded-lg hover:bg-slate-50 hover:border-slate-300"
              >
                <Download className="w-3.5 h-3.5" />
                Download HTML
              </button>
              <button
                onClick={() => downloadAll("json")}
                title="Download every schema as a structured JSON archive"
                className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-slate-600 bg-white border border-slate-200 rounded-lg hover:bg-slate-50 hover:border-slate-300"
              >
                <Download className="w-3.5 h-3.5" />
                Download JSON
              </button>
            </div>
          )}
        </div>
        {loading ? (
          <div className="flex justify-center py-12"><Loader2 className="w-5 h-5 animate-spin text-slate-400" /></div>
        ) : schemas.length === 0 ? (
          <div className="text-center py-12">
            <Code2 className="w-8 h-8 text-slate-200 mx-auto mb-2" />
            <p className="text-sm text-slate-500">No schemas yet. Generate one above.</p>
          </div>
        ) : (
          <div className="divide-y divide-slate-100">
            {schemas.map((s) => {
              const v = validations[s.id];
              const isValidating = validating.has(s.id);
              // Validation badge state. Three outcomes:
              //   - null  → not yet validated (row still loading)
              //   - valid+no warnings → green "Valid"
              //   - valid+warnings    → amber "Warnings" (tooltip lists them)
              //   - invalid           → red "Invalid" (tooltip lists errors)
              const badge = !v ? null
                : !v.valid ? { color: "bg-red-50 text-red-700 border-red-200", icon: AlertCircle, label: "Invalid" }
                : v.warnings.length > 0 ? { color: "bg-amber-50 text-amber-700 border-amber-200", icon: AlertCircle, label: "Warnings" }
                : { color: "bg-green-50 text-green-700 border-green-200", icon: CheckCircle2, label: "Valid" };
              const badgeTooltip = v
                ? [...v.errors, ...v.warnings].join("\n") || "All required fields present"
                : "";
              // Pre-stringified JSON for copy buttons. Parsed-and-
              // reformatted so the copied output is pretty regardless
              // of how the DB stored it.
              const jsonString = JSON.stringify(s.jsonLd, null, 2);
              const scriptTag = `<script type="application/ld+json">\n${jsonString}\n</script>`;
              return (
                <div key={s.id}>
                  <div className="px-5 py-3 flex items-center gap-3 hover:bg-slate-50 flex-wrap">
                    <Code2 className="w-4 h-4 text-slate-400 shrink-0" />
                    <div className="flex-1 min-w-0">
                      <p className="text-sm font-medium text-slate-800 truncate">{s.pageUrl}</p>
                      <p className="text-[11px] text-slate-400">{s.schemaType} · Updated {new Date(s.updatedAt).toLocaleDateString()}</p>
                    </div>
                    {badge && (() => {
                      const Icon = badge.icon;
                      return (
                        <span
                          className={`inline-flex items-center gap-1 text-[10px] font-bold px-2 py-0.5 rounded-full border ${badge.color}`}
                          title={badgeTooltip}
                        >
                          <Icon className="w-2.5 h-2.5" /> {badge.label}
                        </span>
                      );
                    })()}
                    <span className={`text-[10px] font-bold px-2 py-0.5 rounded-full capitalize ${statusColors[s.status] || statusColors.draft}`}>{s.status}</span>
                    {/* Copy JSON-LD — the bare object. Useful when
                        pasting into an existing <script> block or a
                        Tag Manager variable. */}
                    <button
                      onClick={() => copyToClipboard(jsonString, "JSON-LD copied")}
                      title="Copy JSON-LD"
                      className="p-1 rounded text-slate-400 hover:text-slate-700"
                    >
                      <Copy className="w-3.5 h-3.5" />
                    </button>
                    {/* Copy Script Tag — wraps the JSON in a
                        <script type="application/ld+json"> so users
                        can paste it straight into the <head> of a
                        page without hand-typing the wrapper. */}
                    <button
                      onClick={() => copyToClipboard(scriptTag, "Script tag copied")}
                      title="Copy <script> tag"
                      className="p-1 rounded text-slate-400 hover:text-slate-700"
                    >
                      <FileCode className="w-3.5 h-3.5" />
                    </button>
                    {/* Re-validate. Useful after the user Edits the
                        JSON — the Edit flow updates the DB but doesn't
                        auto-re-run validation, so the badge can drift
                        stale. One click resolves it. */}
                    <button
                      onClick={() => runValidation(s.id, s.jsonLd)}
                      disabled={isValidating}
                      title="Re-run validation"
                      className="p-1 rounded text-slate-400 hover:text-slate-700 disabled:opacity-40"
                    >
                      {isValidating ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <ShieldCheck className="w-3.5 h-3.5" />}
                    </button>
                    <button onClick={() => updateSchema(s.id, { autoInject: !s.autoInject })} title={s.autoInject ? "Disable auto-inject" : "Enable auto-inject"}
                      className={`p-1 rounded ${s.autoInject ? "text-green-600" : "text-slate-300"}`}>
                      {s.autoInject ? <ToggleRight className="w-5 h-5" /> : <ToggleLeft className="w-5 h-5" />}
                    </button>
                    <button onClick={() => startEdit(s)} className="text-xs text-slate-500 hover:text-slate-700 font-medium">Edit</button>
                    <button onClick={() => deleteSchema(s.id)} className="text-slate-400 hover:text-red-600"><Trash2 className="w-3.5 h-3.5" /></button>
                  </div>
                  {/* Validation error detail — rendered inline under the
                      row (not just in tooltip) when the schema is
                      outright invalid. Warnings stay in the tooltip
                      to avoid cluttering rows for schemas that work
                      fine but could be richer. */}
                  {v && !v.valid && v.errors.length > 0 && (
                    <div className="px-5 pb-3 -mt-1">
                      <div className="bg-red-50 border border-red-200 rounded-lg p-2.5 text-[11px] text-red-800">
                        <p className="font-bold mb-1">Validation errors:</p>
                        <ul className="list-disc list-inside space-y-0.5">
                          {v.errors.map((e, i) => <li key={i}>{e}</li>)}
                        </ul>
                      </div>
                    </div>
                  )}
                  {editingId === s.id && (
                    <div className="px-5 pb-4">
                      <SchemaCodeEditor value={editCode} onChange={setEditCode} height="h-56" />
                      <div className="flex gap-2 mt-2">
                        <button onClick={saveEdit} className="px-3 py-1.5 bg-slate-900 text-white text-xs font-semibold rounded-lg hover:bg-slate-800">Save Changes</button>
                        <button onClick={() => setEditingId(null)} className="px-3 py-1.5 text-xs text-slate-500 hover:text-slate-700">Cancel</button>
                      </div>
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        )}
      </div>

      {toast && (
        <div className="fixed top-4 right-4 z-50 px-4 py-2 rounded-lg bg-slate-900 text-white text-xs font-semibold shadow-lg flex items-center gap-2">
          <Check className="w-3 h-3" /> {toast}
        </div>
      )}
    </div>
  );
}
