// Renders one `TermRow` — flat `<div class="row …">` or expandable
// `<details class="row …">`, driven straight off the wire shape
// (`lib/termMsg.ts`'s `TermMsg`, mirroring hive-agent's `term_msg.rs`):
// `level` picks the colour class, an empty `summary` + markdown `body`
// is a flat row with just the body (assistant text), anything else with
// a body is an expandable details row gated by the operator's
// expand-tool-output preference. No separate classification step —
// mara: "StreamRow should now match what the server sends in TermMsg."
import { useEffect, useRef } from "preact/hooks";
import type { TermRow } from "../lib/termMsg.js";
import { linkifyToNodes } from "../lib/linkify.js";
import { renderMarkdown } from "../lib/markdown.js";
import { getExpandDetailsPref, getHideDebugPref } from "@hive/shared/prefs.js";

function MarkdownBody({ text }: { text: string }) {
  const ref = useRef<HTMLDivElement>(null);
  const html = renderMarkdown(text);
  useEffect(() => {
    // marked autolinks URLs but leaves them same-tab — open externally
    // so a click never navigates the terminal away.
    ref.current?.querySelectorAll("a[href]").forEach((a) => {
      a.setAttribute("target", "_blank");
      a.setAttribute("rel", "noopener noreferrer");
    });
  }, [html]);
  // eslint-disable-next-line react/no-danger -- sanitized by DOMPurify in renderMarkdown
  return (
    <div className="md" ref={ref} dangerouslySetInnerHTML={{ __html: html }} />
  );
}

function DiffBody({ text }: { text: string }) {
  const lines = String(text).split("\n");
  return (
    <pre className="tool-body diff-body">
      {lines.map((line, i) => {
        const cls = line.startsWith("+ ")
          ? "diff-add"
          : line.startsWith("- ")
            ? "diff-del"
            : "diff-ctx";
        return (
          <span key={i} className={cls}>
            {line}
            {"\n"}
          </span>
        );
      })}
    </pre>
  );
}

export function Row({ row }: { row: TermRow }) {
  // "hide debug output" is a skip, not a dimmer — a hidden debug row
  // still occupies no DOM node at all, same as it never streamed, rather
  // than a CSS `display: none` that would keep it in the layout/DOM for
  // no benefit.
  if (row.level === "debug" && getHideDebugPref()) return null;

  const cssClass = "level-" + row.level;
  const icon = row.icon != null && row.icon !== "" && (
    <span className="row-glyph">{row.icon}</span>
  );

  if (row.body == null) {
    return (
      <div className={`row ${cssClass}`}>
        {icon}
        {linkifyToNodes(row.summary)}
      </div>
    );
  }

  // Empty summary + markdown body → the body itself is the whole row
  // (assistant text), no summary prefix line, never collapsible.
  if (row.body_format === "markdown" && row.summary === "") {
    return (
      <div className={`row ${cssClass}`}>
        {icon}
        <MarkdownBody text={row.body} />
      </div>
    );
  }

  return (
    <details
      className={`row ${cssClass}`}
      open={getExpandDetailsPref() || undefined}
    >
      <summary>
        {icon}
        <span className="summary-text">{row.summary}</span>
      </summary>
      {row.body_format === "diff" && <DiffBody text={row.body} />}
      {row.body_format === "markdown" && <MarkdownBody text={row.body} />}
      {row.body_format == null && (
        <pre className="tool-body">{linkifyToNodes(row.body)}</pre>
      )}
    </details>
  );
}
