import { BookDoc } from '@/libs/document';
import { BookNote, BookSearchConfig, BookSearchResult } from '@/types/book';
import { TTSGranularity } from '@/services/tts';
import { TTS } from 'foliate-js/tts.js';
import { LocaleWithTextInfo } from './misc';

export const NOTE_PREFIX = 'foliate-note:';

type RangeAnchor = (doc: Document) => Range;

export interface Renderer extends HTMLElement {
  scrolled?: boolean;
  scrollLocked: boolean;
  size: number; // current page height
  viewSize: number; // whole document view height
  start: number;
  end: number;
  page: number; // section page index (0-based)
  pages: number; // section page count
  atStart: boolean;
  atEnd: boolean;
  containerPosition: number;
  scrollProp: 'scrollLeft' | 'scrollTop';
  sideProp: 'width' | 'height';
  pageColors?: {
    background: string;
    foreground: string;
  };
  columnCount?: number;
  open: (book: BookDoc) => Promise<void>;
  setAttribute: (name: string, value: string | number) => void;
  removeAttribute: (name: string) => void;
  next: () => Promise<void>;
  prev: () => Promise<void>;
  nextSection?: () => Promise<void>;
  prevSection?: () => Promise<void>;
  render?: () => Promise<void>;
  goTo: (params: { index: number; anchor?: number | RangeAnchor }) => Promise<void>;
  setStyles?: (css: string) => void;
  primaryIndex: number;
  getContents: () => { doc: Document; index?: number; overlayer?: unknown }[];
  scrollToAnchor?: (anchor: number | Range, reason?: string, smooth?: boolean) => void;
  addEventListener: (
    type: string,
    listener: EventListener,
    option?: AddEventListenerOptions,
  ) => void;
  removeEventListener: (type: string, listener: EventListener) => void;
  showLoupe?: (
    x: number,
    y: number,
    options: {
      isVertical: boolean;
      color: string;
      gap: number;
      margin: number;
      radius: number;
      magnification: number;
    },
  ) => void;
  hideLoupe?: () => void;
  destroyLoupe?: () => void;
  pinchZoom?: (ratio: number) => void;
  pinchEnd?: () => void;
  destroy: () => void;
}

export interface FoliateView extends HTMLElement {
  open: (book: BookDoc) => Promise<void>;
  close: () => void;
  init: (options: { lastLocation: string }) => void;
  goTo: (href: string) => void;
  goToFraction: (fraction: number) => void;
  getSectionFractions: () => number[];
  prev: (distance?: number) => void;
  next: (distance?: number) => void;
  pan: (dx: number, dy: number) => void;
  isOverflowX: () => boolean;
  isOverflowY: () => boolean;
  goLeft: () => void;
  goRight: () => void;
  getCFI: (index: number, range: Range) => string;
  getCFIProgress: (cfi: string) => Promise<{
    fraction: number;
    section: { current: number; total: number };
    location: { current: number; next: number; total: number };
    time: { section: number; total: number };
  } | null>;
  resolveCFI: (cfi: string) => { index: number; anchor: RangeAnchor };
  resolveNavigation: (cfiOrHrefOrIndex: string | number) => { index: number; anchor?: RangeAnchor };
  addAnnotation: (
    note: BookNote & { value?: string },
    remove?: boolean,
  ) => { index: number; label: string };
  search: (config: BookSearchConfig) => AsyncGenerator<BookSearchResult | string, void, void>;
  clearSearch: () => void;
  select: (target: string | number | { fraction: number }) => void;
  deselect: () => void;
  initTTS: (
    granularity?: TTSGranularity,
    nodeFilter?: (node: Node) => number,
    highlight?: (range: Range) => void,
  ) => Promise<void>;
  book: BookDoc;
  tts: TTS | null;
  // The most recent relocate location, set synchronously by foliate on every
  // relocate — fresher than the rAF-debounced readerStore progress.
  lastLocation?: { cfi?: string; range?: Range | null };
  isFixedLayout: boolean;
  language: {
    locale?: LocaleWithTextInfo;
    isCJK?: boolean;
    canonical?: string;
    direction?: string;
  };
  history: {
    canGoBack: boolean;
    canGoForward: boolean;
    back: () => void;
    forward: () => void;
    clear: () => void;
  };
  renderer: Renderer;
}

export const wrappedFoliateView = (originalView: FoliateView): FoliateView => {
  const originalAddAnnotation = originalView.addAnnotation.bind(originalView);
  originalView.addAnnotation = (note: BookNote, remove = false) => {
    // transform BookNote to foliate annotation
    const annotation = {
      value: note.cfi,
      ...note,
    };
    return originalAddAnnotation(annotation, remove);
  };
  return originalView;
};
