import { createContext, useCallback, useContext, useEffect, useRef } from 'react';
import type { ReactNode } from 'react';

// Click delegation Context for `output/...` path links scattered across
// the detail panel (Output tab pre-blocks, Timeline comments rendered
// via MarkdownText, Subtask instruction blocks).
//
// Design: each rendering surface emits anchors with
// `data-output-path="output/foo.md"` (no href, no onClick). A single
// provider wraps the detail panel, listens for click bubbling on its
// root div, and dispatches the matched path to the configured
// `openOutputPath` callback.
//
// Why delegation: MarkdownText renders sanitized HTML via
// dangerouslySetInnerHTML, so React onClick handlers can't be attached
// to anchors generated by the Marked renderer. Delegation works for
// both plain-text (LinkifiedText) and Markdown contexts uniformly.

export interface OutputPreviewValue {
  /** Open the preview pane for an `output/...` path. No-op when no provider is mounted (e.g. share view). */
  openOutputPath: (path: string) => void;
}

const NOOP_VALUE: OutputPreviewValue = {
  openOutputPath: () => {
    /* no provider configured — link is visible but inert */
  },
};

const OutputPreviewContext = createContext<OutputPreviewValue>(NOOP_VALUE);

export function useOutputPreview(): OutputPreviewValue {
  return useContext(OutputPreviewContext);
}

interface OutputPreviewProviderProps {
  openOutputPath: (path: string) => void;
  children: ReactNode;
}

export function OutputPreviewProvider({ openOutputPath, children }: OutputPreviewProviderProps): JSX.Element {
  // Keep the latest handler in a ref so the document-level listener
  // doesn't need to be torn down + reattached when openOutputPath
  // changes — the listener reads the current ref on every event.
  const handlerRef = useRef(openOutputPath);
  useEffect(() => {
    handlerRef.current = openOutputPath;
  }, [openOutputPath]);

  // Document-level event delegation.
  //
  // We previously wrapped children in a `<div onClick=...>`, but
  // (a) a real div broke flex chains for downstream layout, and
  // (b) wrapping with `display: contents` left the click target
  //     ambiguous in practice — children didn't visibly receive the
  //     click and the bubbled handler never fired in the user's
  //     browser.
  //
  // Attaching to `document` sidesteps both: we read no DOM nodes for
  // wrapping, the provider returns a Fragment (zero layout impact),
  // and any element with `data-output-path` anywhere in the page
  // routes through this handler.
  //
  // Capture phase intentionally OFF so per-element handlers (if any
  // are ever added) get a chance to stopPropagation. Only one
  // listener per provider instance — if multiple providers ever mount
  // simultaneously (e.g. main task + subtask provider both in the
  // tree), each runs against the closest-matching anchor; we don't
  // try to scope by subtree because the subtask provider is nested
  // INSIDE the main task region and shares the same delegation
  // contract.
  useEffect(() => {
    const onClick = (e: MouseEvent) => {
      const target = e.target as HTMLElement | null;
      if (!target) return;
      const anchor = target.closest('[data-output-path]') as HTMLElement | null;
      if (!anchor) return;
      const path = anchor.getAttribute('data-output-path');
      if (!path) return;
      // Modifier-click (Ctrl/Cmd/Shift/middle) → still preview.
      // Anchors have no href so default browser behavior is a no-op,
      // but preventDefault keeps that explicit.
      e.preventDefault();
      handlerRef.current(path);
    };
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key !== 'Enter' && e.key !== ' ') return;
      const target = e.target as HTMLElement | null;
      if (!target) return;
      const anchor = target.closest('[data-output-path]') as HTMLElement | null;
      if (!anchor) return;
      const path = anchor.getAttribute('data-output-path');
      if (!path) return;
      e.preventDefault();
      handlerRef.current(path);
    };
    document.addEventListener('click', onClick);
    document.addEventListener('keydown', onKeyDown);
    return () => {
      document.removeEventListener('click', onClick);
      document.removeEventListener('keydown', onKeyDown);
    };
  }, []);

  // Fragment: zero layout impact, zero DOM nodes added.
  return (
    <OutputPreviewContext.Provider value={{ openOutputPath }}>
      {children}
    </OutputPreviewContext.Provider>
  );
}
