import type { EpubRendition } from "./epubRendition";
import { normalizeHighlightColor } from "@/constants/bookmarkHighlightColors";

export interface BookmarkHighlight {
  id: string;
  epub_cfi: string;
  highlight_color?: string | null;
}

type EpubAnnotations = {
  highlight?: (
    cfiRange: string,
    data: Record<string, unknown>,
    callback?: () => void,
    id?: string,
    styles?: Record<string, string>,
  ) => void;
  remove?: (cfiRange: string, type: string) => void;
};

export type EpubRenditionWithHighlights = EpubRendition & {
  annotations?: EpubAnnotations;
  on?: (event: string, handler: () => void) => void;
  off?: (event: string, handler: () => void) => void;
};

function annotationId(bookmarkId: string): string {
  return `bookmark-hl-${bookmarkId}`;
}

export function applyBookmarkHighlight(
  rendition: EpubRenditionWithHighlights,
  bookmark: BookmarkHighlight,
): void {
  const cfi = (bookmark.epub_cfi || "").trim();
  if (!cfi || !rendition.annotations?.highlight) return;

  const color = normalizeHighlightColor(bookmark.highlight_color);
  try {
    rendition.annotations.highlight(
      cfi,
      {},
      undefined,
      annotationId(bookmark.id),
      {
        fill: color,
        "fill-opacity": "0.45",
        "mix-blend-mode": "multiply",
      },
    );
  } catch {
    /* CFI may not be in the current spine slice */
  }
}

export function removeBookmarkHighlight(
  rendition: EpubRenditionWithHighlights,
  bookmark: BookmarkHighlight,
): void {
  const cfi = (bookmark.epub_cfi || "").trim();
  if (!cfi || !rendition.annotations?.remove) return;
  try {
    rendition.annotations.remove(cfi, "highlight");
  } catch {
    /* ignore */
  }
}

export function syncBookmarkHighlights(
  rendition: EpubRenditionWithHighlights | null,
  bookmarks: BookmarkHighlight[],
  applied: Map<string, string>,
): void {
  if (!rendition?.annotations?.highlight) return;

  const nextById = new Map(bookmarks.map((b) => [b.id, b]));

  for (const [id, cfi] of applied) {
    if (!nextById.has(id)) {
      removeBookmarkHighlight(rendition, { id, epub_cfi: cfi });
      applied.delete(id);
    }
  }

  for (const bookmark of bookmarks) {
    const prevCfi = applied.get(bookmark.id);
    if (prevCfi && prevCfi !== bookmark.epub_cfi) {
      removeBookmarkHighlight(rendition, { id: bookmark.id, epub_cfi: prevCfi });
      applied.delete(bookmark.id);
    }
    if (applied.has(bookmark.id)) continue;
    applyBookmarkHighlight(rendition, bookmark);
    applied.set(bookmark.id, bookmark.epub_cfi);
  }
}
