import { useCallback, useEffect, useRef, useState } from "react";
import type { EpubRendition } from "../utils/epubRendition";
import { isEpubCfi } from "../utils/epubRendition";

export interface PendingSelection {
  epubCfi: string;
  locationText: string;
  chapterIndex: number;
  chapterTitle: string;
  anchorTop: number;
  anchorLeft: number;
}

type EpubContents = {
  document: Document;
  window?: Window;
  cfiFromRange?: (range: Range) => string;
};

type EpubRenditionWithHooks = EpubRendition & {
  on?: (event: string, handler: (cfiRange: unknown, contents: EpubContents) => void) => void;
  off?: (event: string, handler: (cfiRange: unknown, contents: EpubContents) => void) => void;
  location?: { start?: { index?: number; cfi?: string } };
  hooks?: {
    content?: {
      register: (fn: (contents: EpubContents) => void) => void;
    };
  };
};

function extractSelection(
  contents: EpubContents,
  rendition: EpubRenditionWithHooks,
): PendingSelection | null {
  const text = contents.window?.getSelection?.()?.toString?.()?.trim()
    ?? contents.document?.getSelection?.()?.toString?.()?.trim()
    ?? "";
  if (!text) return null;

  const sel = contents.document.getSelection();
  if (!sel || sel.rangeCount === 0) return null;

  const range = sel.getRangeAt(0);
  let cfi = "";
  try {
    cfi = contents.cfiFromRange?.(range) ?? "";
  } catch {
    cfi = "";
  }
  if (!cfi || !isEpubCfi(cfi)) {
    const locCfi = rendition.location?.start?.cfi;
    if (locCfi && isEpubCfi(locCfi)) cfi = locCfi;
    else return null;
  }

  const rect = range.getBoundingClientRect();
  const chapterIndex = rendition.location?.start?.index ?? 0;

  return {
    epubCfi: cfi,
    locationText: text.slice(0, 2000),
    chapterIndex: typeof chapterIndex === "number" ? chapterIndex : 0,
    chapterTitle: "",
    anchorTop: rect.bottom + 8,
    anchorLeft: Math.min(rect.left, window.innerWidth - 280),
  };
}

export function useEpubSelection(
  renditionRef: React.RefObject<EpubRendition | null>,
  chapterTitle: string,
  renditionVersion: number,
) {
  const [pendingSelection, setPendingSelection] = useState<PendingSelection | null>(null);
  const chapterTitleRef = useRef(chapterTitle);
  chapterTitleRef.current = chapterTitle;

  const clearSelection = useCallback(() => {
    setPendingSelection(null);
  }, []);

  useEffect(() => {
    const rendition = renditionRef.current as EpubRenditionWithHooks | null;
    if (!rendition) return;

    const applySelection = (partial: PendingSelection | null) => {
      if (!partial) {
        setPendingSelection(null);
        return;
      }
      setPendingSelection({
        ...partial,
        chapterTitle: chapterTitleRef.current,
      });
    };

    const onSelected = (_cfiRange: unknown, contents: EpubContents) => {
      applySelection(extractSelection(contents, rendition));
    };

    if (rendition.on) {
      rendition.on("selected", onSelected);
    }

    rendition.hooks?.content?.register?.((contents: EpubContents) => {
      const handler = () => {
        window.setTimeout(() => {
          applySelection(extractSelection(contents, rendition));
        }, 10);
      };
      contents.document.addEventListener("mouseup", handler);
    });

    return () => {
      rendition.off?.("selected", onSelected);
    };
  }, [renditionRef, renditionVersion]);

  return { pendingSelection, clearSelection };
}
