import { useEffect, useState, useCallback } from "react";
import { API_BASE } from "./config";

const DEFAULT_SECTIONS = ["Instructions", "Identity", "Career", "Projects", "Preferences"];

function groupBySection(items) {
  const map = {};
  for (const item of items) {
    const sec = item.section || "General";
    if (!map[sec]) map[sec] = [];
    map[sec].push(item);
  }
  return map;
}

function indentLevelOf(text) {
  const m = text.match(/^( *)/);
  return Math.floor((m ? m[0].length : 0) / 2);
}

// Collapse a section's items into top-level "groups": each group is a parent
// (indent 0) plus its consecutive indented children.
function computeGroups(sectionItems) {
  const groups = [];
  const groupIndexById = {};
  for (const item of sectionItems) {
    const lvl = indentLevelOf(item.text);
    if (lvl === 0 || groups.length === 0) {
      groups.push([item]);
    } else {
      groups[groups.length - 1].push(item);
    }
    groupIndexById[item.id] = groups.length - 1;
  }
  return { groups, groupIndexById };
}

export function Memory() {
  const [items, setItems] = useState([]);
  const [editing, setEditing] = useState(null); // { id, section, text } or null
  const [adding, setAdding] = useState(null);   // { section } for inline new-item form
  const [newSection, setNewSection] = useState("");
  const [showNewSection, setShowNewSection] = useState(false);
  const [message, setMessage] = useState("");
  const [draggingId, setDraggingId] = useState(null);
  const [dragOver, setDragOver] = useState(null); // { id, position: "before"|"after" }

  const loadItems = useCallback(async () => {
    try {
      const res = await fetch(`${API_BASE}/memory`);
      const data = res.ok ? await res.json() : { items: [] };
      setItems(data.items || []);
    } catch {
      setItems([]);
    }
  }, []);

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

  const flash = (msg) => { setMessage(msg); setTimeout(() => setMessage(""), 3000); };

  const saveNew = async (section, text) => {
    // Split on newlines so a pasted list becomes one memory per line (leading
    // spaces kept for nesting). Direct write — no LLM curator, saves every line.
    const lines = text.split("\n").map(l => l.replace(/\s+$/, "")).filter(l => l.trim());
    if (!lines.length) return;
    let ok = 0;
    for (const line of lines) {
      const res = await fetch(`${API_BASE}/memory`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ section, text: line }),
      });
      if (res.ok) ok++;
    }
    if (ok) { loadItems(); setAdding(null); flash(ok === 1 ? "Saved." : `Saved ${ok} items.`); }
    else flash("Save failed.");
  };

  const saveEdit = async (id, section, text) => {
    const cleaned = text.replace(/\s+$/, "");
    if (!cleaned.trim()) return;
    const res = await fetch(`${API_BASE}/memory/${id}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ section, text: cleaned }),
    });
    if (res.ok) { loadItems(); setEditing(null); flash("Updated."); }
    else flash("Update failed.");
  };

  const deleteItem = async (id) => {
    if (!window.confirm("Delete this item?")) return;
    const res = await fetch(`${API_BASE}/memory/${id}`, { method: "DELETE" });
    if (res.ok) { loadItems(); flash("Deleted."); }
    else flash("Delete failed.");
  };

  const reorderSection = async (section, ids) => {
    try {
      const res = await fetch(`${API_BASE}/memory/reorder`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ section, ids }),
      });
      const result = await res.json();
      if (!res.ok || result.ok === false) {
        throw new Error("Memory reorder failed");
      }
      loadItems();
    } catch (err) {
      setMessage(err.message);
    }
  };

  const moveItem = (id, direction) => {
    const item = items.find(i => i.id === id);
    if (!item) return;
    const section = item.section || "General";
    const sectionItems = items.filter(i => (i.section || "General") === section);
    const { groups, groupIndexById } = computeGroups(sectionItems);
    const lvl = indentLevelOf(item.text);

    if (lvl === 0) {
      // Swap whole group with previous/next group
      const gIdx = groupIndexById[id];
      const tIdx = direction === "up" ? gIdx - 1 : gIdx + 1;
      if (tIdx < 0 || tIdx >= groups.length) return;
      const newGroups = [...groups];
      [newGroups[gIdx], newGroups[tIdx]] = [newGroups[tIdx], newGroups[gIdx]];
      reorderSection(section, newGroups.flat().map(i => i.id));
    } else {
      // Swap child with previous/next sibling within same group; never past the parent
      const gIdx = groupIndexById[id];
      const group = groups[gIdx];
      const localIdx = group.findIndex(i => i.id === id);
      const tLocal = direction === "up" ? localIdx - 1 : localIdx + 1;
      if (tLocal <= 0 || tLocal >= group.length) return; // tLocal === 0 is the parent
      const newGroup = [...group];
      [newGroup[localIdx], newGroup[tLocal]] = [newGroup[tLocal], newGroup[localIdx]];
      const newGroups = [...groups];
      newGroups[gIdx] = newGroup;
      reorderSection(section, newGroups.flat().map(i => i.id));
    }
  };

  const onItemDragStart = (e, item) => {
    setDraggingId(item.id);
    e.dataTransfer.effectAllowed = "move";
    try { e.dataTransfer.setData("text/plain", item.id); } catch { /* some browsers restrict */ }
  };

  const onItemDragOver = (e, targetItem, section) => {
    if (!draggingId || draggingId === targetItem.id) return;
    const dragged = items.find(i => i.id === draggingId);
    if (!dragged || (dragged.section || "General") !== section) return;

    const sectionItems = items.filter(i => (i.section || "General") === section);
    const { groups, groupIndexById } = computeGroups(sectionItems);
    const draggedLevel = indentLevelOf(dragged.text);
    const targetGroupIdx = groupIndexById[targetItem.id];
    const draggedGroupIdx = groupIndexById[dragged.id];
    const rect = e.currentTarget.getBoundingClientRect();
    const inUpperHalf = e.clientY < rect.top + rect.height / 2;

    if (draggedLevel === 0) {
      // Top-level drag: snap to group boundaries; ignore hover over own group
      if (targetGroupIdx === draggedGroupIdx) {
        if (dragOver) setDragOver(null);
        return;
      }
      e.preventDefault();
      e.dataTransfer.dropEffect = "move";
      const targetGroup = groups[targetGroupIdx];
      const isTargetParent = targetItem.id === targetGroup[0].id;
      const position = (isTargetParent && inUpperHalf) ? "before" : "after";
      if (dragOver?.kind !== "group" || dragOver?.section !== section || dragOver?.groupIdx !== targetGroupIdx || dragOver?.position !== position) {
        setDragOver({ kind: "group", section, groupIdx: targetGroupIdx, position });
      }
    } else {
      // Child drag: confined to its own parent group
      if (targetGroupIdx !== draggedGroupIdx) {
        if (dragOver) setDragOver(null);
        return;
      }
      e.preventDefault();
      e.dataTransfer.dropEffect = "move";
      const targetGroup = groups[targetGroupIdx];
      const isTargetParent = targetItem.id === targetGroup[0].id;
      let position = inUpperHalf ? "before" : "after";
      if (isTargetParent) position = "after"; // child can't go before its parent
      if (dragOver?.kind !== "item" || dragOver?.section !== section || dragOver?.id !== targetItem.id || dragOver?.position !== position) {
        setDragOver({ kind: "item", section, id: targetItem.id, position });
      }
    }
  };

  const onItemDrop = (e, targetItem, section) => {
    e.preventDefault();
    const dragged = items.find(i => i.id === draggingId);
    if (!dragged || dragged.id === targetItem.id || (dragged.section || "General") !== section || !dragOver) {
      setDraggingId(null);
      setDragOver(null);
      return;
    }
    const sectionItems = items.filter(i => (i.section || "General") === section);
    const { groups, groupIndexById } = computeGroups(sectionItems);
    const draggedLevel = indentLevelOf(dragged.text);

    let block, anchorId, after;
    if (draggedLevel === 0 && dragOver.kind === "group") {
      block = groups[groupIndexById[dragged.id]];
      const targetGroup = groups[dragOver.groupIdx];
      anchorId = dragOver.position === "before" ? targetGroup[0].id : targetGroup[targetGroup.length - 1].id;
      after = dragOver.position === "after";
    } else if (draggedLevel > 0 && dragOver.kind === "item") {
      block = [dragged];
      anchorId = dragOver.id;
      after = dragOver.position === "after";
    } else {
      setDraggingId(null);
      setDragOver(null);
      return;
    }

    const blockIds = new Set(block.map(b => b.id));
    const remainder = sectionItems.filter(i => !blockIds.has(i.id));
    let insertIdx = remainder.findIndex(i => i.id === anchorId);
    if (insertIdx < 0) {
      setDraggingId(null);
      setDragOver(null);
      return;
    }
    if (after) insertIdx += 1;
    const newOrder = [...remainder.slice(0, insertIdx), ...block, ...remainder.slice(insertIdx)];

    setDraggingId(null);
    setDragOver(null);
    reorderSection(section, newOrder.map(i => i.id));
  };

  const onItemDragEnd = () => {
    setDraggingId(null);
    setDragOver(null);
  };

  const addSection = () => {
    const sec = newSection.trim();
    if (!sec) return;
    // Create a placeholder item to establish the section, then let user fill it
    setAdding({ section: sec });
    setNewSection("");
    setShowNewSection(false);
  };

  const grouped = groupBySection(items);
  const sections = Object.keys(grouped);

  const inputStyle = {
    background: "#1a1a1a",
    color: "#eee",
    border: "1px solid #444",
    borderRadius: "6px",
    padding: "0.4rem 0.6rem",
    fontSize: "0.9rem",
    width: "100%",
    boxSizing: "border-box",
  };

  const btnStyle = (color = "#007acc") => ({
    padding: "0.3rem 0.7rem",
    background: color,
    color: "#fff",
    border: "none",
    borderRadius: "6px",
    cursor: "pointer",
    fontSize: "0.8rem",
  });

  return (
    <div style={{ flexGrow: 1 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "1.25rem" }}>
        <div>
          <h2 style={{ margin: 0, fontSize: "1.1rem" }}>Memory</h2>
          <p style={{ margin: "0.25rem 0 0", color: "#888", fontSize: "0.85rem" }}>
            Facts injected into every conversation. Format mirrors your claude.md.
          </p>
        </div>
        <button onClick={() => setShowNewSection(s => !s)} style={btnStyle()}>
          + New Section
        </button>
      </div>

      {showNewSection && (
        <div style={{ display: "flex", gap: "0.5rem", marginBottom: "1.25rem" }}>
          <input
            autoFocus
            type="text"
            placeholder="Section name (e.g. Health, Finance)"
            value={newSection}
            onChange={e => setNewSection(e.target.value)}
            onKeyDown={e => { if (e.key === "Enter") addSection(); if (e.key === "Escape") setShowNewSection(false); }}
            style={{ ...inputStyle, maxWidth: "320px" }}
          />
          <button onClick={addSection} style={btnStyle()}>Add</button>
          <button onClick={() => setShowNewSection(false)} style={btnStyle("#444")}>Cancel</button>
        </div>
      )}

      {sections.length === 0 && !adding && (
        <div style={{ color: "#666", fontSize: "0.9rem", marginTop: "2rem" }}>
          No memory yet. Add a section to get started.
        </div>
      )}

      {/* Render sections in claude.md style */}
      {[...sections, ...(adding && !sections.includes(adding.section) ? [adding.section] : [])].map(section => (
        <div key={section} style={{ marginBottom: "2rem" }}>
          {/* ## Section Header */}
          <div style={{ display: "flex", alignItems: "center", gap: "0.75rem", marginBottom: "0.5rem" }}>
            <h3 style={{ margin: 0, fontSize: "1rem", color: "#4db8ff" }}>## {section}</h3>
            <button
              onClick={() => setAdding({ section })}
              style={{ ...btnStyle("#2a3a2a"), border: "1px solid #3a5a3a", color: "#7dcc7d", fontSize: "0.75rem" }}
            >
              + Add item
            </button>
          </div>

          {/* Bullet items */}
          {(() => {
            const sectionItems = grouped[section] || [];
            const { groups, groupIndexById } = computeGroups(sectionItems);
            return (
          <div style={{ paddingLeft: "1rem", borderLeft: "2px solid #222" }}>
            {sectionItems.map((item) => {
              const indentMatch = item.text.match(/^( *)(.*)$/s);
              const indentLevel = indentMatch ? Math.floor(indentMatch[1].length / 2) : 0;
              const displayText = indentMatch ? indentMatch[2] : item.text;
              const gIdx = groupIndexById[item.id];
              const group = groups[gIdx];
              const localIdx = group.findIndex(i => i.id === item.id);
              const isParentRow = localIdx === 0;
              const isLastInGroup = localIdx === group.length - 1;
              const arrowUpDisabled = indentLevel === 0 ? gIdx === 0 : localIdx <= 1;
              const arrowDownDisabled = indentLevel === 0 ? gIdx === groups.length - 1 : isLastInGroup;
              const isDragging = draggingId === item.id;
              let showLineBefore = false, showLineAfter = false;
              if (dragOver && dragOver.section === section) {
                if (dragOver.kind === "group" && dragOver.groupIdx === gIdx) {
                  if (dragOver.position === "before" && isParentRow) showLineBefore = true;
                  if (dragOver.position === "after" && isLastInGroup) showLineAfter = true;
                } else if (dragOver.kind === "item" && dragOver.id === item.id) {
                  if (dragOver.position === "before") showLineBefore = true;
                  else showLineAfter = true;
                }
              }
              return (
              <div
                key={item.id}
                draggable={!editing}
                onDragStart={(e) => onItemDragStart(e, item)}
                onDragOver={(e) => onItemDragOver(e, item, section)}
                onDrop={(e) => onItemDrop(e, item, section)}
                onDragEnd={onItemDragEnd}
                style={{
                  marginBottom: "0.4rem",
                  paddingLeft: `${indentLevel * 1.5}rem`,
                  borderTop: showLineBefore ? "2px solid #4db8ff" : "2px solid transparent",
                  borderBottom: showLineAfter ? "2px solid #4db8ff" : "2px solid transparent",
                  opacity: isDragging ? 0.4 : 1,
                }}
              >
                {editing?.id === item.id ? (
                  <EditRow
                    initial={editing.text}
                    onSave={text => saveEdit(item.id, section, text)}
                    onCancel={() => setEditing(null)}
                    inputStyle={inputStyle}
                    btnStyle={btnStyle}
                  />
                ) : (
                  <div
                    onDoubleClick={() => setEditing({ id: item.id, section, text: item.text })}
                    title="Drag to reorder · double-click to edit"
                    style={{ display: "flex", alignItems: "flex-start", gap: "0.5rem", cursor: "grab" }}
                  >
                    <span style={{ color: "#aaa", marginTop: "0.1rem", flexShrink: 0 }}>-</span>
                    <span style={{ color: "#ddd", fontSize: "0.9rem", flexGrow: 1 }}>{displayText}</span>
                    <div style={{ display: "flex", gap: "0.3rem", flexShrink: 0, opacity: 0.4 }}
                      onMouseEnter={e => e.currentTarget.style.opacity = 1}
                      onMouseLeave={e => e.currentTarget.style.opacity = 0.4}
                    >
                      <button
                        onClick={() => moveItem(item.id, "up")}
                        disabled={arrowUpDisabled}
                        title="Move up"
                        style={{ ...btnStyle("#333"), fontSize: "0.72rem", padding: "0.2rem 0.4rem", opacity: arrowUpDisabled ? 0.3 : 1, cursor: arrowUpDisabled ? "default" : "pointer" }}
                      >▲</button>
                      <button
                        onClick={() => moveItem(item.id, "down")}
                        disabled={arrowDownDisabled}
                        title="Move down"
                        style={{ ...btnStyle("#333"), fontSize: "0.72rem", padding: "0.2rem 0.4rem", opacity: arrowDownDisabled ? 0.3 : 1, cursor: arrowDownDisabled ? "default" : "pointer" }}
                      >▼</button>
                      <button
                        onClick={() => deleteItem(item.id)}
                        style={{ ...btnStyle("#5a1a1a"), fontSize: "0.72rem", padding: "0.2rem 0.5rem" }}
                      >del</button>
                    </div>
                  </div>
                )}
              </div>
              );
            })}

            {/* Inline add form */}
            {adding?.section === section && (
              <AddRow
                onSave={text => saveNew(section, text)}
                onCancel={() => setAdding(null)}
                inputStyle={inputStyle}
                btnStyle={btnStyle}
              />
            )}
          </div>
            );
          })()}
        </div>
      ))}

      {message && (
        <div style={{ position: "fixed", bottom: "1.5rem", right: "1.5rem", background: "#1a2e1a", border: "1px solid #3a5a3a", color: "#7dcc7d", padding: "0.6rem 1rem", borderRadius: "8px", fontSize: "0.85rem" }}>
          {message}
        </div>
      )}
    </div>
  );
}

function handleIndentKey(e, setText) {
  if (e.key !== "Tab") return false;
  e.preventDefault();
  if (e.shiftKey) {
    setText(t => t.startsWith("  ") ? t.slice(2) : t);
  } else {
    setText(t => "  " + t);
  }
  return true;
}

function AddRow({ onSave, onCancel, inputStyle, btnStyle }) {
  const [text, setText] = useState("");
  return (
    <div style={{ display: "flex", gap: "0.5rem", alignItems: "center", marginTop: "0.4rem" }}>
      <span style={{ color: "#555", flexShrink: 0 }}>-</span>
      <textarea
        autoFocus
        rows={1}
        placeholder="New item… (paste multiple lines to add several at once)"
        value={text}
        onChange={e => setText(e.target.value)}
        onKeyDown={e => {
          if (handleIndentKey(e, setText)) return;
          if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); onSave(text); }
          if (e.key === "Escape") onCancel();
        }}
        style={{ ...inputStyle, resize: "vertical", fontFamily: "inherit", minHeight: "2.1rem" }}
      />
      <button onClick={() => onSave(text)} style={btnStyle()}>Save</button>
      <button onClick={onCancel} style={btnStyle("#444")}>✕</button>
    </div>
  );
}

function EditRow({ initial, onSave, onCancel, inputStyle, btnStyle }) {
  const [text, setText] = useState(initial);
  return (
    <div style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}>
      <span style={{ color: "#555", flexShrink: 0 }}>-</span>
      <input
        autoFocus
        type="text"
        value={text}
        onChange={e => setText(e.target.value)}
        onKeyDown={e => {
          if (handleIndentKey(e, setText)) return;
          if (e.key === "Enter") onSave(text);
          if (e.key === "Escape") onCancel();
        }}
        style={{ ...inputStyle }}
      />
      <button onClick={() => onSave(text)} style={btnStyle()}>Save</button>
      <button onClick={onCancel} style={btnStyle("#444")}>✕</button>
    </div>
  );
}
