import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type {
  LicenseTemplate,
  TemplateFieldPlacement,
  TemplateLogoPlacement,
} from '@ema-platform/api';

/** Stable ids for new blocks; `crypto.randomUUID` is not in every test env. */
function blockId(): string {
  return `blk_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;
}

/**
 * Editor state for the selected version.
 *
 * Selection defaults to the live design because that is the one staff usually
 * open, and the fields reset whenever the selection changes so an edit can
 * never leak from one version into another.
 */
export function useTemplateDraft(templates: LicenseTemplate[]) {
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [source, setSource] = useState('');
  const [name, setName] = useState('');
  const [landscape, setLandscape] = useState(true);
  // Empty string means "use the A4 default" — only a version whose
  // pageOptions already carries a custom size (e.g. the Seaman Book) starts
  // with these populated.
  const [pageWidth, setPageWidth] = useState('');
  const [pageHeight, setPageHeight] = useState('');
  const [backgroundUrl, setBackgroundUrl] = useState('');
  const [logoUrl, setLogoUrl] = useState('');
  const [logoPlacement, setLogoPlacement] = useState<TemplateLogoPlacement>({});
  const [placements, setPlacements] = useState<TemplateFieldPlacement[]>([]);
  const [selectedBlockId, setSelectedBlockId] = useState<string | null>(null);
  const editorRef = useRef<HTMLTextAreaElement>(null);

  const selected = useMemo(
    () => templates.find((tpl) => tpl.id === selectedId) ?? null,
    [templates, selectedId],
  );

  useEffect(() => {
    if (!templates.length) {
      setSelectedId(null);
      return;
    }
    if (selectedId && templates.some((tpl) => tpl.id === selectedId)) return;
    const published = templates.find((tpl) => tpl.status === 'PUBLISHED');
    setSelectedId((published ?? templates[0]).id);
  }, [templates, selectedId]);

  useEffect(() => {
    if (!selected) return;
    setSource(selected.hbsSource);
    setName(selected.name);
    setLandscape(selected.pageOptions?.landscape ?? true);
    setPageWidth(selected.pageOptions?.width ?? '');
    setPageHeight(selected.pageOptions?.height ?? '');
    setBackgroundUrl(selected.backgroundUrl ?? '');
    setLogoUrl(selected.logoUrl ?? '');
    setLogoPlacement(selected.logoPlacement ?? {});
    setPlacements(selected.fieldPlacements ?? []);
    setSelectedBlockId(null);
  }, [selected]);

  const isPublished = selected?.status === 'PUBLISHED';

  // Compared as JSON because both are plain data the server round-trips; a
  // reference check would mark the draft dirty on every render.
  const placementsChanged =
    JSON.stringify(placements) !== JSON.stringify(selected?.fieldPlacements ?? []);
  const logoPlacementChanged =
    JSON.stringify(logoPlacement) !== JSON.stringify(selected?.logoPlacement ?? {});

  const dirty =
    Boolean(selected) &&
    (source !== selected?.hbsSource ||
      name !== selected?.name ||
      landscape !== (selected?.pageOptions?.landscape ?? true) ||
      pageWidth !== (selected?.pageOptions?.width ?? '') ||
      pageHeight !== (selected?.pageOptions?.height ?? '') ||
      backgroundUrl !== (selected?.backgroundUrl ?? '') ||
      logoUrl !== (selected?.logoUrl ?? '') ||
      logoPlacementChanged ||
      placementsChanged);

  /** True once the canvas owns the layout, which locks the raw editor. */
  const usesCanvas = placements.length > 0;

  const selectedBlock =
    placements.find((block) => block.id === selectedBlockId) ?? null;

  /**
   * Drops a new block near the top-left, where it is immediately visible.
   *
   * An image block gets a square-ish default footprint instead of the text
   * defaults (fontSize/color/align mean nothing on an `<img>`) — a seal or
   * signature dropped at 30% width and no explicit height would otherwise
   * stretch to whatever the image's own aspect ratio makes of that width,
   * which reads as broken until the author manually resizes it.
   */
  const addBlock = useCallback(
    (variable: string | null, text?: string, kind: 'text' | 'image' = 'text') => {
      const block: TemplateFieldPlacement =
        kind === 'image'
          ? {
              id: blockId(),
              variable,
              type: 'image',
              xPct: 10,
              yPct: 10,
              widthPct: 15,
            }
          : {
              id: blockId(),
              variable,
              text,
              type: 'text',
              xPct: 10,
              yPct: 10,
              widthPct: 30,
              fontSize: 14,
              fontWeight: 'normal',
              fontStyle: 'normal',
              align: 'left',
              color: '#111111',
            };
      setPlacements((prev) => [...prev, block]);
      setSelectedBlockId(block.id);
    },
    [],
  );

  const updateBlock = useCallback((next: TemplateFieldPlacement) => {
    setPlacements((prev) =>
      prev.map((block) => (block.id === next.id ? next : block)),
    );
  }, []);

  const deleteBlock = useCallback((id: string) => {
    setPlacements((prev) => prev.filter((block) => block.id !== id));
    setSelectedBlockId((current) => (current === id ? null : current));
  }, []);

  /** Inserts a placeholder where the caret is, rather than at the end. */
  function insertVariable(key: string) {
    const el = editorRef.current;
    const token = `{{${key}}}`;
    if (!el) {
      setSource((prev) => prev + token);
      return;
    }
    const start = el.selectionStart ?? source.length;
    const end = el.selectionEnd ?? start;
    setSource(source.slice(0, start) + token + source.slice(end));
    requestAnimationFrame(() => {
      el.focus();
      el.setSelectionRange(start + token.length, start + token.length);
    });
  }

  return {
    selected,
    selectedId,
    setSelectedId,
    source,
    setSource,
    name,
    setName,
    landscape,
    setLandscape,
    pageWidth,
    setPageWidth,
    pageHeight,
    setPageHeight,
    backgroundUrl,
    setBackgroundUrl,
    logoUrl,
    setLogoUrl,
    logoPlacement,
    setLogoPlacement,
    placements,
    setPlacements,
    selectedBlockId,
    setSelectedBlockId,
    selectedBlock,
    usesCanvas,
    addBlock,
    updateBlock,
    deleteBlock,
    editorRef,
    isPublished,
    dirty,
    insertVariable,
  };
}
