/**
 * NodeDetailPanel.jsx — Bottom-tray detail panel for diagram nodes and edges.
 *
 * Layout: sits below the diagram canvas, collapses vertically.
 * Collapse = shrinks to a 44px header strip; expand = 280px tray.
 *
 * Features:
 *  - Module-level summary cache (Map) — AI answers are stable for a session,
 *    so we never re-fetch the same node in the same browser session.
 *  - Auto-generates a concise AI summary on first open for each unique node.
 *  - Inline mini-chat scoped to the selected node.
 *  - "Open in full chat →" escape hatch.
 *  - Consistent design: uses CSS custom properties (--surface-2, --border, etc.)
 *    and the same button classes as the rest of the app (diagram-ask-btn, session-delete).
 */
import { useState, useEffect, useRef } from "react";
import { streamQuery } from "../api";

// ── Module-level cache ────────────────────────────────────────────────────────
// Keyed by `repo::label` — stable for the browser session since indexed
// repo content doesn't change between diagram interactions.
const summaryCache = new Map();

// ── Type colours (matches GraphDiagram.jsx TYPE_STYLE dots) ──────────────────
const TYPE_COLOR = {
  module:    "#818CF8", class:    "#5B8FF9", abstract: "#7DABFF",
  mixin:     "#60A5FA", service:  "#2DD4BF", database: "#38BDF8",
  external:  "#4E5E80", input:    "#2DD4BF", transform: "#818CF8",
  output:    "#5B8FF9", edge:     "#4E5E80",
};

// ── Chevron SVGs — same viewport as DiagramView fullscreen icons ──────────────
function ChevronDown() {
  return (
    <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
      <path d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"/>
    </svg>
  );
}
function ChevronUp() {
  return (
    <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
      <path d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>
    </svg>
  );
}

export default function NodeDetailPanel({ subject, repo, onClose, onOpenInChat }) {
  const { kind = "node", label, type, file, description, items = [], autoQuestion } = subject;
  const typeColor = TYPE_COLOR[type] || "#818CF8";
  const cacheKey  = `${repo}::${label}`;

  const [summary, setSummary]     = useState(() => summaryCache.get(cacheKey) || "");
  const [streaming, setStreaming] = useState(false);
  const [input, setInput]         = useState("");
  const [messages, setMessages]   = useState([]);
  const [collapsed, setCollapsed] = useState(false);
  const stopRef   = useRef(null);
  const bottomRef = useRef(null);

  // ── Auto-summary on open — skipped if already cached ─────────────────────
  useEffect(() => {
    setSummary(summaryCache.get(cacheKey) || "");
    setMessages([]);
    setInput("");
    setCollapsed(false); // always expand when switching to a new subject

    if (summaryCache.has(cacheKey)) {
      setStreaming(false);
      return;
    }

    setStreaming(true);
    const q = autoQuestion ||
      `Give a concise 3–4 sentence explanation of "${label}" in ${repo}: what it does, its key responsibilities, and what other parts of the codebase depend on it. Be specific.`;

    let content = "";
    const stop = streamQuery({
      question: q, repo, mode: "hybrid",
      onToken:   (t) => { content += t; setSummary(content); },
      onSources: () => {},
      onDone:    () => {
        summaryCache.set(cacheKey, content); // cache on completion
        setStreaming(false);
        stopRef.current = null;
      },
      onError:   () => { setStreaming(false); stopRef.current = null; },
    });
    stopRef.current = stop;
    return () => { stopRef.current?.(); };
  }, [label, repo]); // re-run only when a different node is selected

  // Auto-scroll on new messages
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages, summary]);

  // ── Inline chat ───────────────────────────────────────────────────────────
  function handleAsk(e) {
    e?.preventDefault();
    const q = input.trim();
    if (!q || streaming) return;
    setInput("");

    const assistantId = Date.now();
    setMessages(prev => [
      ...prev,
      { role: "user", content: q },
      { id: assistantId, role: "assistant", content: "" },
    ]);
    setStreaming(true);
    // Expand tray if collapsed so user can see the answer
    setCollapsed(false);

    const history = messages.map(m => ({ role: m.role, content: m.content }));
    let content = "";
    const stop = streamQuery({
      question: `Regarding "${label}" (${file || type}) in ${repo}: ${q}`,
      repo, mode: "hybrid", history,
      onToken: (t) => {
        content += t;
        setMessages(prev => prev.map(m => m.id === assistantId ? { ...m, content } : m));
      },
      onSources: () => {},
      onDone:  () => { setStreaming(false); stopRef.current = null; },
      onError: (err) => {
        setMessages(prev => prev.map(m =>
          m.id === assistantId ? { ...m, content: `Error: ${err}` } : m
        ));
        setStreaming(false);
        stopRef.current = null;
      },
    });
    stopRef.current = stop;
  }

  function handleKeyDown(e) {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleAsk(); }
  }

  return (
    <div style={{
      width: "100%",
      height: collapsed ? 44 : 280,
      flexShrink: 0,
      overflow: "hidden",
      background: "var(--surface-2)",
      borderTop: "1px solid var(--border)",
      display: "flex", flexDirection: "column",
      fontFamily: "var(--mono)",
      transition: "height var(--transition-slow)",
    }}>

      {/* ── Header strip (always visible) ── */}
      <div style={{
        height: 44, minHeight: 44,
        padding: "0 12px",
        borderBottom: collapsed ? "none" : "1px solid var(--border-subtle)",
        background: "var(--surface-3)",
        display: "flex", alignItems: "center", gap: 8,
        cursor: "default",
      }}>
        {/* Type pill */}
        <span style={{
          fontSize: 8, fontWeight: 700, letterSpacing: "0.12em",
          textTransform: "uppercase", color: typeColor,
          background: `${typeColor}18`, border: `1px solid ${typeColor}38`,
          borderRadius: "var(--radius-sm)", padding: "2px 6px",
          flexShrink: 0,
        }}>{type || kind}</span>

        {/* Label */}
        <span style={{
          fontSize: 12, fontWeight: 700, color: "var(--text)",
          overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
          flex: 1,
        }}>{label}</span>

        {/* File path */}
        {file && (
          <span style={{
            fontSize: 9, color: "var(--faint)",
            overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
            maxWidth: 160, flexShrink: 1,
          }}>{file}</span>
        )}

        {/* Streaming indicator */}
        {streaming && messages.length === 0 && (
          <span style={{
            width: 6, height: 6, borderRadius: "50%",
            background: "var(--accent-soft)",
            animation: "pulse 1.2s ease-in-out infinite",
            flexShrink: 0,
          }} />
        )}

        {/* Collapse/expand button */}
        <button
          onClick={() => setCollapsed(c => !c)}
          title={collapsed ? "Expand panel" : "Collapse panel"}
          className="session-delete"
          style={{ fontSize: "inherit", lineHeight: 1, padding: "3px 5px", opacity: 1 }}
        >
          {collapsed ? <ChevronUp /> : <ChevronDown />}
        </button>

        {/* Close button */}
        <button onClick={onClose} className="session-delete" style={{ fontSize: 16, padding: "0 3px", opacity: 1 }}>
          ×
        </button>
      </div>

      {/* ── Scrollable content (hidden when collapsed via parent height clip) ── */}
      <div style={{ flex: 1, overflowY: "auto", padding: "10px 14px 6px" }}>

        {/* Static description from AST */}
        {description && (
          <p style={{
            fontSize: 10.5, color: "var(--faint)", lineHeight: 1.6,
            margin: "0 0 10px",
          }}>{description}</p>
        )}

        {/* Method / export pills */}
        {items.length > 0 && (
          <div style={{ marginBottom: 10 }}>
            <div style={{
              fontSize: 8.5, color: "var(--faint)", letterSpacing: "0.1em",
              textTransform: "uppercase", marginBottom: 5,
            }}>Methods / Exports</div>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 3 }}>
              {items.map((item, i) => (
                <span key={i} style={{
                  fontSize: 9.5, background: "var(--accent-dim)",
                  border: "1px solid var(--accent-border)",
                  borderRadius: "var(--radius-sm)", padding: "2px 6px",
                  color: "var(--accent-soft)",
                }}>{item}</span>
              ))}
            </div>
          </div>
        )}

        <div style={{ borderTop: "1px solid var(--border-subtle)", margin: "0 0 10px" }} />

        {/* AI Summary */}
        <div>
          <div style={{
            fontSize: 8.5, color: "var(--faint)", letterSpacing: "0.1em",
            textTransform: "uppercase", marginBottom: 6,
          }}>AI Summary</div>
          {summary ? (
            <div style={{ fontSize: 11, color: "var(--accent-soft)", lineHeight: 1.65 }}>
              {summary}
              {streaming && messages.length === 0 && <span style={{ opacity: 0.4 }}>▋</span>}
            </div>
          ) : (
            <div style={{ fontSize: 11, color: "var(--faint)", opacity: 0.5 }}>Generating…</div>
          )}
        </div>

        {/* Follow-up messages */}
        {messages.length > 0 && (
          <div style={{ marginTop: 12 }}>
            <div style={{ borderTop: "1px solid var(--border-subtle)", marginBottom: 10 }} />
            {messages.map((m, i) => (
              <div key={i} style={{ marginBottom: 8 }}>
                <div style={{
                  fontSize: 8.5, fontWeight: 700, letterSpacing: "0.08em",
                  textTransform: "uppercase", marginBottom: 2,
                  color: m.role === "user" ? "var(--text-2)" : "var(--accent-soft)",
                }}>
                  {m.role === "user" ? "You" : "Claude"}
                </div>
                <div style={{
                  fontSize: 11, lineHeight: 1.65, whiteSpace: "pre-wrap",
                  color: m.role === "user" ? "var(--muted)" : "var(--accent-soft)",
                }}>
                  {m.content}
                  {m.role === "assistant" && streaming && i === messages.length - 1 && (
                    <span style={{ opacity: 0.4 }}>▋</span>
                  )}
                </div>
              </div>
            ))}
          </div>
        )}

        <div ref={bottomRef} />
      </div>

      {/* ── Pinned footer: input + action (hidden when collapsed) ── */}
      {!collapsed && <div style={{
        flexShrink: 0,
        borderTop: "1px solid var(--border-subtle)",
        padding: "8px 12px",
        display: "flex", gap: 8, alignItems: "center",
        background: "var(--surface-3)",
      }}>
        <form onSubmit={handleAsk} style={{ display: "flex", gap: 5, flex: 1 }}>
          <input
            value={input}
            onChange={e => setInput(e.target.value)}
            onKeyDown={handleKeyDown}
            disabled={streaming}
            placeholder={`Ask about ${label}…`}
            style={{
              flex: 1, background: "var(--accent-dim)",
              border: "1px solid var(--accent-border)",
              borderRadius: "var(--radius-sm)", color: "var(--text)", fontSize: 10.5,
              padding: "6px 8px", fontFamily: "var(--mono)",
              outline: "none", opacity: streaming ? 0.5 : 1,
            }}
          />
          <button type="submit" disabled={!input.trim() || streaming} className="diagram-ask-btn" style={{
            padding: "0 10px", fontSize: 13, fontWeight: 700,
            opacity: (!input.trim() || streaming) ? 0.35 : 1,
          }}>→</button>
        </form>
        <button
          onClick={() => onOpenInChat(
            `Explain "${label}" in ${repo} in detail — what does it do, what are its key methods or responsibilities, what calls it, and what does it depend on?`
          )}
          className="diagram-retry-btn"
          style={{ flexShrink: 0, padding: "5px 10px", fontSize: 9.5, letterSpacing: "0.04em" }}
        >
          Open in full chat →
        </button>
      </div>}
    </div>
  );
}
