// Explorer pane: top tab bar, folder tree, content/markdown view, home view.

const { useState: useStateE, useMemo: useMemoE, useEffect: useEffectE, useRef: useRefE } = React;

/* ---------- Download helper ---------- */
function downloadFile(path, meta) {
  const filename = path.split("/").pop();
  const a = document.createElement("a");
  if (meta.kind === "binary") {
    a.href = meta.downloadUrl;
  } else {
    const blob = new Blob([meta.body], { type: "text/markdown;charset=utf-8" });
    a.href = URL.createObjectURL(blob);
    setTimeout(() => URL.revokeObjectURL(a.href), 1000);
  }
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
}

/* ---------- Tab bar ---------- */
function TabBar({ tabs, activeId, onSelect, onClose }) {
  return (
    <div className="tabbar" role="tablist">
      {tabs.map((t) => {
        const isHome = t.id === "home";
        return (
          <div
            key={t.id}
            className={`tab ${t.id === activeId ? "active" : ""} ${isHome ? "home" : ""}`}
            onClick={() => onSelect(t.id)}
            role="tab"
            aria-selected={t.id === activeId}
          >
            <span className="tab-icon">
              {isHome ? <window.Icons.Home size={14} /> : <window.Icons.File size={13} />}
            </span>
            <span className="tab-label" title={t.label}>{t.label}</span>
            {!isHome && (
              <button
                className="tab-close"
                onClick={(e) => {
                  e.stopPropagation();
                  onClose(t.id);
                }}
                aria-label={`Close ${t.label}`}
              >
                <window.Icons.Close size={11} stroke={2} />
              </button>
            )}
          </div>
        );
      })}
    </div>
  );
}

/* ---------- Folder tree ---------- */
function FileTree({ folders, openPath, onOpenFile }) {
  const [expanded, setExpanded] = useStateE(() =>
    Object.fromEntries(folders.map((f) => [f.name, true]))
  );
  // Menu state: { path, x, y } — x/y are viewport coords (fixed positioning)
  const [menu, setMenu] = useStateE(null);
  const treeRef = useRefE(null);

  useEffectE(() => {
    if (!menu) return;
    const onDown = (e) => {
      if (!e.target.closest(".tree-more, .tree-menu")) setMenu(null);
    };
    const onKey = (e) => { if (e.key === "Escape") setMenu(null); };
    const onScroll = () => setMenu(null);
    document.addEventListener("mousedown", onDown);
    document.addEventListener("keydown", onKey);
    treeRef.current?.addEventListener("scroll", onScroll, true);
    window.addEventListener("resize", onScroll);
    return () => {
      document.removeEventListener("mousedown", onDown);
      document.removeEventListener("keydown", onKey);
      treeRef.current?.removeEventListener("scroll", onScroll, true);
      window.removeEventListener("resize", onScroll);
    };
  }, [menu]);

  const toggle = (name) =>
    setExpanded((e) => ({ ...e, [name]: !e[name] }));

  const openMenu = (e, path) => {
    e.stopPropagation();
    if (menu?.path === path) { setMenu(null); return; }
    const rect = e.currentTarget.getBoundingClientRect();
    setMenu({
      path,
      x: rect.right,         // right edge of button → right edge of menu
      y: rect.bottom + 4,    // 4px below button
    });
  };

  return (
    <nav className="tree" aria-label="Folder tree" ref={treeRef}>
      <div className="tree-section-title">~ / portfolio</div>
      {folders.map((folder) => {
        const FolderIcon = window.FOLDER_ICONS[folder.name] || window.Icons.Folder;
        const open = expanded[folder.name];
        return (
          <div key={folder.name} className="tree-folder">
            <div
              className="tree-row folder"
              onClick={() => toggle(folder.name)}
            >
              <window.Icons.Chevron
                size={12}
                stroke={2}
                style={{
                  transform: open ? "rotate(90deg)" : "rotate(0deg)",
                  transition: "transform 120ms",
                  opacity: 0.55,
                }}
              />
              <span className="tree-folder-icon">
                <FolderIcon size={15} />
              </span>
              <span className="tree-label">{folder.name}</span>
            </div>
            {open && (
              <div className="tree-children">
                {folder.files.map((path) => {
                  const meta = window.PORTFOLIO_DATA.FILES[path];
                  const filename = path.split("/").pop();
                  const menuOpen = menu?.path === path;
                  return (
                    <div
                      key={path}
                      className={`tree-row file ${path === openPath ? "active" : ""}`}
                      onClick={() => onOpenFile(path)}
                      title={meta?.title || filename}
                    >
                      <span style={{ width: 12, flexShrink: 0 }}></span>
                      <span className="tree-file-icon">
                        <window.Icons.File size={13} />
                      </span>
                      <span className="tree-label">{filename}</span>
                      <button
                        className={`tree-more ${menuOpen ? "open" : ""}`}
                        onClick={(e) => openMenu(e, path)}
                        aria-label={`More actions for ${filename}`}
                        aria-haspopup="menu"
                        aria-expanded={menuOpen}
                      >
                        <window.Icons.MoreH size={14} />
                      </button>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        );
      })}

      {menu && (
        <div
          className="tree-menu"
          role="menu"
          style={{
            position: "fixed",
            top: menu.y,
            left: menu.x,
            transform: "translateX(-100%)",
          }}
          onClick={(e) => e.stopPropagation()}
        >
          <button
            className="tree-menu-item"
            onClick={() => {
              const meta = window.PORTFOLIO_DATA.FILES[menu.path];
              downloadFile(menu.path, meta);
              setMenu(null);
            }}
          >
            <span className="ic"><window.Icons.Download size={14} /></span>
            <span>Download</span>
          </button>
        </div>
      )}
    </nav>
  );
}

/* ---------- Minimal markdown renderer ----------
 * Hardcoded content only — no XSS concerns. Supports the subset we use:
 * # / ## / ### headings, paragraphs, **bold**, *em*, `code`, > blockquote,
 * - / 1. lists, [text](href), --- hr.
 */
function renderInline(text) {
  // Order matters: code → bold → em → link
  let parts = [{ t: "txt", v: text }];
  const re = [
    { kind: "code", rx: /`([^`]+)`/ },
    { kind: "strong", rx: /\*\*([^*]+)\*\*/ },
    { kind: "em", rx: /\*([^*]+)\*/ },
    { kind: "link", rx: /\[([^\]]+)\]\(([^)]+)\)/ },
  ];
  for (const { kind, rx } of re) {
    const next = [];
    for (const p of parts) {
      if (p.t !== "txt") { next.push(p); continue; }
      let s = p.v;
      let m;
      while ((m = s.match(rx))) {
        const before = s.slice(0, m.index);
        if (before) next.push({ t: "txt", v: before });
        if (kind === "link") next.push({ t: "link", v: m[1], href: m[2] });
        else next.push({ t: kind, v: m[1] });
        s = s.slice(m.index + m[0].length);
      }
      if (s) next.push({ t: "txt", v: s });
    }
    parts = next;
  }
  return parts.map((p, i) => {
    if (p.t === "txt") return p.v;
    if (p.t === "code") return <code key={i}>{p.v}</code>;
    if (p.t === "strong") return <strong key={i}>{p.v}</strong>;
    if (p.t === "em") return <em key={i}>{p.v}</em>;
    if (p.t === "link") return (
      <a key={i} href={p.href} target="_blank" rel="noopener">{p.v}</a>
    );
    return null;
  });
}

function Markdown({ source }) {
  const blocks = useMemoE(() => {
    const lines = source.replace(/\r\n/g, "\n").split("\n");
    const out = [];
    let i = 0;
    while (i < lines.length) {
      const line = lines[i];
      if (line.startsWith("# ")) { out.push({ t: "h1", v: line.slice(2) }); i++; continue; }
      if (line.startsWith("## ")) { out.push({ t: "h2", v: line.slice(3) }); i++; continue; }
      if (line.startsWith("### ")) { out.push({ t: "h3", v: line.slice(4) }); i++; continue; }
      if (line.trim() === "---") { out.push({ t: "hr" }); i++; continue; }
      if (line.startsWith("> ")) {
        const buf = [];
        while (i < lines.length && lines[i].startsWith("> ")) {
          buf.push(lines[i].slice(2));
          i++;
        }
        out.push({ t: "quote", v: buf.join(" ") });
        continue;
      }
      if (/^[-*] /.test(line)) {
        const items = [];
        while (i < lines.length && /^[-*] /.test(lines[i])) {
          items.push(lines[i].replace(/^[-*] /, ""));
          i++;
        }
        out.push({ t: "ul", items });
        continue;
      }
      if (/^\d+\.\s/.test(line)) {
        const items = [];
        while (i < lines.length && /^\d+\.\s/.test(lines[i])) {
          items.push(lines[i].replace(/^\d+\.\s/, ""));
          i++;
        }
        out.push({ t: "ol", items });
        continue;
      }
      if (line.trim() === "") { i++; continue; }
      // Paragraph: gather until blank line
      const buf = [line];
      i++;
      while (i < lines.length && lines[i].trim() !== "" &&
             !/^(#{1,3}\s|>\s|[-*]\s|\d+\.\s|---)/.test(lines[i])) {
        buf.push(lines[i]);
        i++;
      }
      out.push({ t: "p", v: buf.join(" ") });
    }
    return out;
  }, [source]);

  return (
    <div className="md">
      {blocks.map((b, idx) => {
        switch (b.t) {
          case "h1": return <h1 key={idx}>{renderInline(b.v)}</h1>;
          case "h2": return <h2 key={idx}>{renderInline(b.v)}</h2>;
          case "h3": return <h3 key={idx}>{renderInline(b.v)}</h3>;
          case "p":  return <p key={idx}>{renderInline(b.v)}</p>;
          case "hr": return <hr key={idx} />;
          case "quote": return <blockquote key={idx}>{renderInline(b.v)}</blockquote>;
          case "ul": return (
            <ul key={idx}>
              {b.items.map((it, j) => <li key={j}>{renderInline(it)}</li>)}
            </ul>
          );
          case "ol": return (
            <ol key={idx}>
              {b.items.map((it, j) => <li key={j}>{renderInline(it)}</li>)}
            </ol>
          );
          default: return null;
        }
      })}
    </div>
  );
}

/* ---------- File view (markdown OR binary download stub) ---------- */
function FileView({ path }) {
  const meta = window.PORTFOLIO_DATA.FILES[path];
  if (!meta) return null;
  if (meta.kind === "binary") return <BinaryView path={path} meta={meta} />;

  const segments = path.split("/");
  return (
    <div className="content-inner">
      <div className="content-path">
        <span>~</span>
        <span className="sep">/</span>
        {segments.map((s, i) => (
          <React.Fragment key={i}>
            <span className={i === segments.length - 1 ? "leaf" : ""}>{s}</span>
            {i < segments.length - 1 && <span className="sep">/</span>}
          </React.Fragment>
        ))}
      </div>
      <article className="card">
        <div className="card-meta">
          <span className="card-meta-dot"></span>
          <span>{meta.meta}</span>
        </div>
        <Markdown source={meta.body} />
      </article>
    </div>
  );
}

/* ---------- Binary view: off-white, no dot grid, just download ---------- */
function BinaryView({ path, meta }) {
  const filename = path.split("/").pop();
  const ext = (filename.split(".").pop() || "").toUpperCase();
  return (
    <div className="binary-view">
      <div className="binary-card">
        <div className="binary-filename">{filename}</div>
        <div className="binary-sub">
          This file can’t be previewed in the browser. Download it to view.
        </div>
        <div className="binary-meta">
          <span>{ext}</span>
          {meta.sizeLabel && <span className="dot"></span>}
          {meta.sizeLabel && <span>{meta.sizeLabel}</span>}
          {meta.meta && <span className="dot"></span>}
          {meta.meta && <span>{meta.meta}</span>}
        </div>
        <button
          className="binary-download"
          onClick={() => downloadFile(path, meta)}
        >
          <window.Icons.Download size={15} />
          <span>Download {filename}</span>
        </button>
      </div>
    </div>
  );
}

/* ---------- Home view ---------- */
function HomeView({ onOpenFile }) {
  const { PERSON } = window.PORTFOLIO_DATA;
  return (
    <div className="home-content-inner">
      <div className="home-card">
        <div className="home-hero-row">
          <div className="home-hero">
            <div className="home-eyebrow">Personal portfolio</div>
            <h1 className="home-title">{PERSON.name}</h1>
            <p className="home-sub">{PERSON.blurb}</p>
          </div>
          <div className="home-seal">
            <img src="assets/bonsai.png" alt="" className="home-seal-img" />
          </div>
        </div>

        <div className="home-meta-grid">
          <div className="home-meta-item">
            <div className="k">Role</div>
            <div className="v">{PERSON.role}</div>
          </div>
          <div className="home-meta-item">
            <div className="k">Based in</div>
            <div className="v">{PERSON.location}</div>
          </div>
          <div className="home-meta-item">
            <div className="k">Email</div>
            <div className="v">
              <a href={`mailto:${PERSON.email}`}>{PERSON.email}</a>
            </div>
          </div>
          <div className="home-meta-item">
            <div className="k">Elsewhere</div>
            <div className="v">{PERSON.handle} · github · linkedin</div>
          </div>
        </div>
      </div>
    </div>
  );
}

window.TabBar = TabBar;
window.FileTree = FileTree;
window.FileView = FileView;
window.HomeView = HomeView;
