import type { ReadingSettings } from "../types/reader";

type EpubSpine = {
  get: (target: string | number) => { href: string } | null;
  spineItems?: Array<{ href: string }>;
};

type EpubBookForNav = {
  ready: Promise<void>;
  spine: EpubSpine;
};

type EpubRendition = {
  themes: {
    register: (name: string, styles: { body: Record<string, string> }) => void;
    select: (name: string) => void;
    fontSize: (size: string) => void;
    override: (property: string, value: string, important?: boolean) => void;
  };
  book: EpubBookForNav & {
    locations: {
      generate: (size: number) => Promise<string[]>;
      percentageFromCfi: (cfi: string) => number | null;
    };
    navigation?: {
      get: (href: string) => { label?: string } | null;
    };
  };
  currentLocation: () => Promise<{ start?: { href?: string } } | null>;
  display: (target: string) => Promise<unknown>;
  next: () => void;
  prev: () => void;
};

type EpubManager = {
  settings: { gap?: number };
  isRendered?: () => boolean;
  updateLayout?: () => void;
};

type EpubRenditionWithManager = EpubRendition & {
  settings?: { gap?: number };
  manager?: EpubManager;
};

const FONT_STACKS: Record<ReadingSettings["font_family"], string> = {
  serif: 'Georgia, "Times New Roman", serif',
  "sans-serif": 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
  monospace: 'ui-monospace, "Cascadia Code", monospace',
};

const READER_THEME_KEY = "cloud-reader";

export function isEpubCfi(location: string | number): boolean {
  return typeof location === "string" && location.startsWith("epubcfi(");
}

/** Map nav/TOC href to a spine href epub.js can display. */
export function resolveSpineHref(book: EpubBookForNav, href: string): string {
  const hashIndex = href.indexOf("#");
  const fragment = hashIndex >= 0 ? href.slice(hashIndex) : "";
  const path = hashIndex >= 0 ? href.slice(0, hashIndex) : href;

  const candidates = [href, path];
  try {
    candidates.push(decodeURIComponent(path), encodeURI(path));
  } catch {
    /* ignore malformed URI encoding in nav hrefs */
  }
  for (const candidate of candidates) {
    if (book.spine.get(candidate)) {
      return candidate.includes("#") ? candidate : candidate + fragment;
    }
  }

  const normalizedPath = path.replace(/^\/+/, "");
  const pathFile = normalizedPath.split("/").pop() ?? normalizedPath;

  for (const item of book.spine.spineItems ?? []) {
    const spineHref = item.href.replace(/^\/+/, "");
    const spineFile = spineHref.split("/").pop() ?? spineHref;
    if (
      spineHref === normalizedPath ||
      spineHref.endsWith(normalizedPath) ||
      normalizedPath.endsWith(spineHref) ||
      spineFile === pathFile
    ) {
      return item.href + fragment;
    }
  }

  return href;
}

/** epub.js paginated side padding is gap/2; map per-side margin to total gap. */
export function marginWidthToGap(marginWidth: number): number {
  return marginWidth * 2;
}

function applyEpubGap(rendition: EpubRendition, marginWidth: number): void {
  const gap = marginWidthToGap(marginWidth);
  const r = rendition as EpubRenditionWithManager;
  if (r.settings) {
    r.settings.gap = gap;
  }
  if (r.manager?.settings) {
    r.manager.settings.gap = gap;
  }
  if (r.manager?.isRendered?.() && r.manager.updateLayout) {
    r.manager.updateLayout();
  }
}

/** Apply reading preferences inside the epub.js iframe. */
export function applyRenditionSettings(
  rendition: EpubRendition,
  settings: ReadingSettings,
): void {
  const fontStack = FONT_STACKS[settings.font_family] ?? FONT_STACKS.serif;

  applyEpubGap(rendition, settings.margin_width);

  rendition.themes.register(READER_THEME_KEY, {
    body: {
      color: `${settings.text_color} !important`,
      background: `${settings.background_color} !important`,
      "font-family": `${fontStack} !important`,
      "line-height": `${settings.line_height} !important`,
    },
  });
  rendition.themes.select(READER_THEME_KEY);
  rendition.themes.fontSize(`${settings.font_size}px`);
}

export async function updateChapterTitleFromRendition(
  rendition: EpubRendition,
): Promise<string> {
  try {
    const loc = await rendition.currentLocation();
    const href = loc?.start?.href;
    if (href && rendition.book.navigation) {
      const nav = rendition.book.navigation.get(href);
      if (nav?.label) return nav.label;
    }
  } catch {
    /* ignore navigation lookup failures */
  }
  return "";
}

export type { EpubRendition };
