/**
 * DiagramView.jsx — System diagrams + interactive codebase explorer.
 *
 * Tabs:
 *   Explore        — Interactive concept map (AI-generated, clearly labelled)
 *   Architecture   — Real import edges from AST + AI descriptions     [verified]
 *   Class Hierarchy— Real inheritance from AST + AI descriptions      [verified]
 *   Sequence       — AI-generated call flow                           [speculative]
 *   Data Flow      — AI-generated data pipeline                       [speculative]
 *
 * Interactions:
 *   - Click a diagram node  → inline NodeDetailPanel slides in (no view switch)
 *   - Click a diagram edge  → NodeDetailPanel explains the relationship
 *   - Hover a node          → dims unrelated nodes, highlights connections
 */

import { useEffect, useRef, useState } from "react";
import { streamDiagram } from "../api";
import ExploreView from "./ExploreView";
import GraphDiagram from "./GraphDiagram";
import NodeDetailPanel from "./NodeDetailPanel";

// ── Diagram tab definitions ───────────────────────────────────────────────────
const EXPLORE_TAB = {
  id: "explore", label: "Explore", desc: "Guided concept tour",
};

// Only AST-verified diagrams — Sequence and Data Flow were removed because
// they were fully LLM-generated with no static analysis backing, making them
// unreliable for a learning tool where accuracy matters.
const DIAGRAM_TABS = [
  {
    id:    "architecture",
    label: "Architecture",
    desc:  "Components & connections",
  },
  {
    id:    "class",
    label: "Class Hierarchy",
    desc:  "Classes & relationships",
  },
];

// ── Tab icons (SVG) ───────────────────────────────────────────────────────────
// Inline SVGs render crisp at every DPI — unicode glyphs (◈ ⬡ ◫) are
// rasterised at screen resolution and look blurry on high-DPI displays.
function TabIcon({ id }) {
  if (id === "explore") return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      {/* Compass rose — N point pulses in the landing animation */}
      <path d="M12 2L14.5 7.5L12 12L9.5 7.5Z" fill="currentColor"/>
      <path d="M12 22L13.5 16.5L12 12L10.5 16.5Z" fill="currentColor" opacity="0.45"/>
      <path d="M22 12L16.5 10.5L12 12L16.5 13.5Z" fill="currentColor" opacity="0.45"/>
      <path d="M2 12L7.5 10.5L12 12L7.5 13.5Z" fill="currentColor" opacity="0.45"/>
      <circle cx="12" cy="12" r="1.5" fill="currentColor"/>
    </svg>
  );
  if (id === "architecture") return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" aria-hidden="true">
      {/* Three nodes connected by edges — represents a dependency/import graph */}
      <circle cx="5" cy="12" r="2.5"/>
      <circle cx="19" cy="6.5" r="2.5"/>
      <circle cx="19" cy="17.5" r="2.5"/>
      <line x1="7.4" y1="11" x2="16.6" y2="7.5"/>
      <line x1="7.4" y1="13" x2="16.6" y2="16.5"/>
    </svg>
  );
  if (id === "class") return (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
      {/* Parent node + two children — represents inheritance / class tree */}
      <rect x="8.5" y="2" width="7" height="5" rx="1.5"/>
      <rect x="1" y="17" width="7" height="5" rx="1.5"/>
      <rect x="16" y="17" width="7" height="5" rx="1.5"/>
      <line x1="12" y1="7" x2="12" y2="12"/>
      <line x1="12" y1="12" x2="4.5" y2="17"/>
      <line x1="12" y1="12" x2="19.5" y2="17"/>
    </svg>
  );
  return null;
}

const ALL_TABS = [EXPLORE_TAB, ...DIAGRAM_TABS];

export default function DiagramView({ repo, onAskAbout, focusFiles, onFullscreenChange }) {
  const [diagramType, setDiagramType] = useState(
    () => localStorage.getItem("ghrc_diagramType") || "explore"
  );
  function setType(t) {
    setDiagramType(t);
    localStorage.setItem("ghrc_diagramType", t);
    // Clear any open detail panel when switching tabs
    setSelected(null);
  }

  const [loading, setLoading]         = useState(false);
  const [loadStage, setLoadStage]     = useState(null);  // { stage, progress, message }
  const [error, setError]             = useState(null);
  const [diagramData, setDiagramData] = useState(null);
  const [cache, setCache]             = useState({});
  // Per-type retry counter — using a single shared counter caused switching tabs
  // while retryKey > 0 to bypass the cache for the OTHER diagram type too.
  const [retryKeys, setRetryKeys]     = useState({});  // { "architecture": 1, "class": 0, ... }
  const [fullscreen, setFullscreen]   = useState(false);

  // ── Inline detail panel state ─────────────────────────────────────────────
  // subject = { kind, label, type, file, description, items, autoQuestion? }
  const [selected, setSelected] = useState(null);

  // Ref passed into ExploreView so we can call its force-reload from our header
  const exploreRegenRef = useRef(null);

  const isExplore = diagramType === "explore";
  const selectedTypeDef = ALL_TABS.find(t => t.id === diagramType);

  // localStorage key for a diagram — used to persist across page refreshes.
  function diagLsKey(r, type) { return `ghrc_diagram_${r.replace(/\//g, "_")}_${type}`; }

  // ── Fetch diagram ─────────────────────────────────────────────────────────
  useEffect(() => {
    if (!repo || isExplore) return;
    const isForced = !!retryKeys[diagramType];
    // 1. In-memory cache: survives tab switches.
    if (!isForced && cache[diagramType]) {
      setDiagramData(cache[diagramType]);
      setError(null);
      return;
    }
    // 2. localStorage cache: survives page refreshes.
    if (!isForced) {
      try {
        const stored = localStorage.getItem(diagLsKey(repo, diagramType));
        if (stored) {
          const parsed = JSON.parse(stored);
          setCache(prev => ({ ...prev, [diagramType]: parsed }));
          setDiagramData(parsed);
          setError(null);
          return;
        }
      } catch { /* corrupt — fall through to fetch */ }
    }
    setLoading(true);
    setLoadStage(null);
    setError(null);
    setDiagramData(null);
    // force=true when retryKeys[diagramType] > 0 (user hit Regenerate) so the
    // backend bypasses its disk cache and actually produces a fresh diagram.
    const cancel = streamDiagram(repo, diagramType, {
      force: isForced,
      onProgress: (ev) => setLoadStage(ev),
      onDone: ({ diagram, type }) => {
        setLoading(false);
        setLoadStage(null);
        setCache(prev => ({ ...prev, [diagramType]: diagram }));
        setDiagramData(diagram);
        setRetryKeys(prev => ({ ...prev, [diagramType]: 0 }));
        try { localStorage.setItem(diagLsKey(repo, diagramType), JSON.stringify(diagram)); } catch {}
      },
      onError: (msg) => {
        setLoading(false);
        setLoadStage(null);
        setError(msg);
      },
    });
    return cancel;
  }, [repo, diagramType, retryKeys[diagramType], isExplore]);

  // Reset on repo change — always land on Explore so the concept map is the
  // first thing seen when switching repos.
  useEffect(() => {
    setCache({});
    setDiagramData(null);
    setError(null);
    setRetryKeys({});
    setSelected(null);
    setDiagramType("explore");
    localStorage.setItem("ghrc_diagramType", "explore");
  }, [repo]);

  // Escape exits fullscreen
  useEffect(() => {
    if (!fullscreen) return;
    function onKey(e) { if (e.key === "Escape") setFullscreen(false); }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [fullscreen]);

  // Notify parent so it can collapse the sidebar while fullscreen is on.
  // The sidebar sits under the z:300 overlay anyway, but collapsing it also
  // reclaims the grid column behind the overlay — less reflow on exit.
  useEffect(() => {
    onFullscreenChange?.(fullscreen);
  }, [fullscreen, onFullscreenChange]);

  function handleRegenerate() {
    try { localStorage.removeItem(diagLsKey(repo, diagramType)); } catch {}
    setCache(prev => { const n = {...prev}; delete n[diagramType]; return n; });
    setDiagramData(null);
    setError(null);
    setSelected(null);
    setRetryKeys(prev => ({ ...prev, [diagramType]: (prev[diagramType] || 0) + 1 }));
  }

  // When user clicks "Open in full chat →" from the detail panel
  function handleOpenInChat(question) {
    setSelected(null);
    onAskAbout?.(question);
  }

  return (
    <div className={`diagram-container${fullscreen ? " diagram-fullscreen" : ""}`}>

      {/* ── Header ── */}
      <div className="diagram-header">
        {!fullscreen && (
          <span className="diagram-title">
            {isExplore ? `Explore — ${repo}` : `System Diagram — ${repo}`}
          </span>
        )}
        {/* Fullscreen breadcrumb — the chat-header (which normally carries the
            repo context) is hidden in fullscreen, so we surface the same
            signal inline: a backend dot + owner/repo slug + current view. */}
        {fullscreen && (
          <div className="diagram-fs-crumbs" aria-label="Current view">
            <span className="diagram-fs-slug">
              {(() => {
                const [owner, name] = repo.split("/");
                return (<>
                  <span className="diagram-fs-owner">{owner}/</span>
                  <span className="diagram-fs-name">{name}</span>
                </>);
              })()}
            </span>
            <span className="diagram-fs-sep" aria-hidden="true">›</span>
            <span className="diagram-fs-view">{selectedTypeDef?.label || "Explore"}</span>
          </div>
        )}
        {/* Right-side controls — action buttons then fullscreen on the far right */}
        <div style={{ display: "flex", gap: 8, alignItems: "center", marginLeft: "auto" }}>
          {isExplore ? (
            <button className="diagram-retry-btn" onClick={() => exploreRegenRef.current?.()} title="Generate a fresh tour">
              ↺ Regenerate
            </button>
          ) : diagramData && (
            <>
              <button className="diagram-retry-btn" onClick={handleRegenerate} title="Generate a fresh diagram">
                ↺ Regenerate
              </button>
              <button
                className="diagram-ask-btn"
                onClick={() => onAskAbout?.(
                  `Explain the ${selectedTypeDef?.label.toLowerCase()} of ${repo} — walk me through each component and how they connect`
                )}
              >
                Ask about this →
              </button>
            </>
          )}
          <button
            className="diagram-fullscreen-btn"
            onClick={() => setFullscreen(f => !f)}
            title={fullscreen ? "Exit fullscreen" : "Fullscreen"}
            aria-label={fullscreen ? "Exit fullscreen" : "Fullscreen"}
          >
            {fullscreen ? (
              /* Compress — 4 inward corners */
              <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" style={{ display: "block" }}>
                <path d="M6 1v5H1"/><path d="M10 1v5h5"/>
                <path d="M6 15v-5H1"/><path d="M10 15v-5h5"/>
              </svg>
            ) : (
              /* Expand — 4 outward corners */
              <svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" style={{ display: "block" }}>
                <path d="M1 6V1h5"/><path d="M15 6V1h-5"/>
                <path d="M1 10v5h5"/><path d="M15 10v5h-5"/>
              </svg>
            )}
          </button>
        </div>
      </div>

      {/* ── Focus Files banner ── */}
      {!fullscreen && focusFiles?.length > 0 && (
        <div className="diagram-focus-banner">
          <span className="diagram-focus-icon">◎</span>
          <span>Focused on {focusFiles.length} file{focusFiles.length !== 1 ? "s" : ""} from your last answer:</span>
          <span className="diagram-focus-files">
            {focusFiles.slice(0, 3).join(", ")}
            {focusFiles.length > 3 ? ` +${focusFiles.length - 3} more` : ""}
          </span>
        </div>
      )}

      {/* ── Tab selector ── */}
      {!fullscreen && (
        <div className="diagram-type-bar">
          {/* ── Explore — hero tab, visually distinct from technical diagrams ── */}
          <button
            key="explore"
            className={`diagram-type-btn${diagramType === "explore" ? " active" : ""}`}
            onClick={() => setType("explore")}
            style={{
              background: diagramType === "explore"
                ? "rgba(91,143,249,0.14)"
                : "rgba(91,143,249,0.04)",
              borderColor: diagramType === "explore"
                ? "rgba(91,143,249,0.55)"
                : "rgba(91,143,249,0.20)",
            }}
          >
            <span className="diagram-type-icon" style={{ color: "var(--accent-soft)" }}><TabIcon id="explore" /></span>
            <span className="diagram-type-label" style={{ color: diagramType === "explore" ? "var(--accent)" : "var(--accent-soft)" }}>Explore</span>
            <span className="diagram-type-desc">Guided concept tour</span>
          </button>

          {/* Vertical divider between Explore and technical tabs */}
          <div style={{
            width: 1, alignSelf: "stretch",
            background: "var(--border)",
            margin: "0 4px", flexShrink: 0,
          }} />

          {/* ── Technical diagram tabs ── */}
          {DIAGRAM_TABS.map(t => (
            <button
              key={t.id}
              className={`diagram-type-btn${diagramType === t.id ? " active" : ""}`}
              onClick={() => setType(t.id)}
            >
              <span className="diagram-type-icon"><TabIcon id={t.id} /></span>
              <span className="diagram-type-label">{t.label}</span>
              <span className="diagram-type-desc">{t.desc}</span>
            </button>
          ))}
        </div>
      )}

      {/* ── Canvas area (diagram on top, detail tray below) ── */}
      <div
        className="diagram-canvas has-cursor-glow"
        style={{ display: "flex", flexDirection: "column", overflow: "hidden", alignItems: "stretch", minHeight: 0, "--glow-size": "480px", "--glow-intensity": "7%" }}
        onMouseMove={(e) => {
          // Cursor-glow primitive — fed via CSS vars, painted on the compositor.
          const r = e.currentTarget.getBoundingClientRect();
          e.currentTarget.style.setProperty("--mx", `${e.clientX - r.left}px`);
          e.currentTarget.style.setProperty("--my", `${e.clientY - r.top}px`);
        }}
      >
        {/* Keyed wrapper — replays .view-switch-in when the diagram type changes,
            so Explore/Architecture/Class tab switches share the app's motion language. */}
        <div
          key={diagramType}
          className="view-switch-in"
          style={{ flex: 1, position: "relative", overflow: "hidden", minHeight: 0 }}
        >
          {isExplore ? (
            <ExploreView repo={repo} onAskAbout={onAskAbout} onRegenerateRef={exploreRegenRef} />
          ) : (
            <>
              {loading && (
                <div className="diagram-loading">
                  <span className="spinner" />
                  <div style={{ flex: 1, maxWidth: 300 }}>
                    <div>{loadStage?.message || `Generating ${selectedTypeDef?.label.toLowerCase()} diagram…`}</div>
                    {loadStage && (
                      <div style={{ marginTop: 8 }}>
                        <div style={{
                          height: 3, background: "var(--border)", borderRadius: 2, overflow: "hidden",
                        }}>
                          <div style={{
                            height: "100%",
                            width:  `${Math.round((loadStage.progress || 0) * 100)}%`,
                            background:  "var(--accent)",
                            borderRadius: 2,
                            transition:  "width 0.4s ease",
                          }} />
                        </div>
                        <div style={{ fontSize: 11, color: "var(--muted)", marginTop: 3 }}>
                          {Math.round((loadStage.progress || 0) * 100)}%
                        </div>
                      </div>
                    )}
                  </div>
                </div>
              )}
              {error && (
                <div className="diagram-error">
                  {error}
                  <button className="diagram-retry-btn" onClick={handleRegenerate}>Retry</button>
                </div>
              )}
              {diagramData && (
                <GraphDiagram
                  data={diagramData}
                  repo={repo}
                  diagramType={diagramType}
                  onNodeSelect={setSelected}
                  onEdgeSelect={setSelected}
                  onAskAbout={onAskAbout}
                  panelOpen={!!selected}
                />
              )}
            </>
          )}
        </div>

        {/* Bottom tray detail panel */}
        {selected && (
          <NodeDetailPanel
            subject={selected}
            repo={repo}
            onClose={() => setSelected(null)}
            onOpenInChat={handleOpenInChat}
          />
        )}
      </div>
    </div>
  );
}
