// v1.14.x-html-artifact-panes: dedicated full-height Markdown viewer used
// when a user clicks "Open in pane" on an assistant message that has NO
// html_artifact part. Header carries Copy (raw source) + Download (server-
// materialised .md under <projectRoot>/.boocode/artifacts/) + close.
//
// Pane state is a reference only (chat_id + message_id + title); the markdown
// body is fetched on mount from GET /api/chats/:chat_id/messages by locating
// the matching message_id. This keeps sessions.workspace_panes jsonb small
// and the assistant message row remains the single source of truth.
import { useEffect, useState } from 'react';
import { Check, Copy, Download, X } from 'lucide-react';
import { toast } from 'sonner';
import { api } from '@/api/client';
import type { MarkdownArtifactState } from '@/api/types';
import { MarkdownRenderer } from './MarkdownRenderer';

interface Props {
  chatId: string;
  state: MarkdownArtifactState;
  onClose: () => void;
}

export function MarkdownArtifactPane({ chatId, state, onClose }: Props) {
  const [justCopied, setJustCopied] = useState(false);
  const [downloading, setDownloading] = useState(false);
  const [content, setContent] = useState<string | null>(null);
  const [loadError, setLoadError] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;
    setContent(null);
    setLoadError(null);
    void (async () => {
      try {
        // No single-message GET endpoint exists; the chat-messages list is
        // already cached server-side and the lookup is O(n) over a small
        // window. Cheaper than adding a new route for one call site.
        const messages = await api.chats.messages(chatId);
        if (cancelled) return;
        const msg = messages.find((m) => m.id === state.message_id);
        if (!msg) {
          setLoadError('Message not found');
          return;
        }
        setContent(msg.content ?? '');
      } catch (err) {
        if (cancelled) return;
        setLoadError(err instanceof Error ? err.message : 'failed to load message');
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [chatId, state.message_id]);

  async function copy() {
    if (content === null) return;
    try {
      await navigator.clipboard.writeText(content);
      setJustCopied(true);
      setTimeout(() => setJustCopied(false), 1200);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'copy failed');
    }
  }

  async function download() {
    if (downloading) return;
    setDownloading(true);
    try {
      const { url, path } = await api.messages.downloadArtifact(
        chatId,
        state.message_id,
        'md',
      );
      // Trigger browser download from the returned URL. The endpoint stamps
      // Content-Disposition: attachment so the click lands as a save.
      const a = document.createElement('a');
      a.href = url;
      a.rel = 'noopener';
      a.click();
      toast.success(`Saved to ${path}`);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'download failed');
    } finally {
      setDownloading(false);
    }
  }

  return (
    <div className="flex flex-col h-full min-h-0">
      <div className="flex items-center gap-2 border-b border-border bg-muted/30 px-2 py-1 shrink-0">
        <span className="text-xs text-muted-foreground truncate flex-1" title={state.title}>
          {state.title || 'Markdown artifact'}
        </span>
        <button
          type="button"
          onClick={() => void copy()}
          disabled={content === null}
          className="inline-flex items-center justify-center size-5 rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 max-md:min-h-[44px] max-md:min-w-[44px]"
          aria-label="Copy markdown source"
          title="Copy"
        >
          {justCopied ? <Check size={12} /> : <Copy size={12} />}
        </button>
        <button
          type="button"
          onClick={() => void download()}
          disabled={downloading || content === null}
          className="inline-flex items-center justify-center size-5 rounded text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-40 max-md:min-h-[44px] max-md:min-w-[44px]"
          aria-label="Download markdown"
          title="Download"
        >
          <Download size={12} />
        </button>
        <button
          type="button"
          onClick={onClose}
          className="inline-flex items-center justify-center size-5 rounded text-muted-foreground hover:bg-muted hover:text-foreground max-md:min-h-[44px] max-md:min-w-[44px]"
          aria-label="Close artifact pane"
          title="Close"
        >
          <X size={12} />
        </button>
      </div>
      <div className="flex-1 min-h-0 overflow-auto px-4 py-3 text-sm">
        {loadError ? (
          <div className="text-destructive">Failed to load: {loadError}</div>
        ) : content === null ? (
          <div className="text-muted-foreground">Loading…</div>
        ) : (
          <MarkdownRenderer content={content} />
        )}
      </div>
    </div>
  );
}
