import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
import {
  View,
  Text,
  ActivityIndicator,
  StyleSheet,
  type LayoutChangeEvent,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { Reader, ReaderProvider, useReader } from "@epubjs-react-native/core";
import { useFileSystem } from "@epubjs-react-native/expo-file-system";
import * as FileSystem from "expo-file-system";
import type { EBookDetail } from "@cloud-reader/shared";
import { getAccessToken } from "../../api/client";
import { ebooksApi } from "../../api/ebooks";
import { getReadingProgress, updateReadingProgress } from "../../api/reader";
import {
  fetchBookmarks,
  createMarker,
  deleteBookmark,
} from "../../api/annotations";
import { useReadingSettings } from "../../hooks/useReadingSettings";
import {
  buildEpubTheme,
  FONT_STACKS,
  resolvePalette,
} from "../../utils/epubTheme";
import type { Bookmark } from "../../types";
import { ReaderToolbar } from "./ReaderToolbar";
import { TocModal, type TocItem } from "./TocModal";
import { ReadingSettingsModal } from "./ReadingSettingsModal";
import { BookmarksModal } from "./BookmarksModal";

const HIGHLIGHT_COLOR = "#ffd54a";
const PROGRESS_SAVE_MS = 800;

/** Loose view of the epubjs-react-native reader API we rely on. */
interface ReaderApi {
  goToLocation?: (target: string) => void;
  changeTheme?: (theme: Record<string, Record<string, string>>) => void;
  changeFontSize?: (size: string) => void;
  changeFontFamily?: (font: string) => void;
  addAnnotation?: (
    type: string,
    cfiRange: string,
    data?: unknown,
    styles?: Record<string, unknown>,
  ) => void;
  removeAnnotationByCfi?: (cfi: string) => void;
  removeSelection?: () => void;
  toc?: TocItem[];
}

interface EpubLocation {
  start?: { cfi?: string };
}

interface EpubSection {
  label?: string;
  index?: number;
}

interface EpubReaderViewProps {
  book: EBookDetail;
  ebookId: number;
  onClose: () => void;
}

export function EpubReaderView(props: EpubReaderViewProps): ReactNode {
  return (
    <ReaderProvider>
      <EpubReaderInner {...props} />
    </ReaderProvider>
  );
}

function EpubReaderInner({
  book,
  ebookId,
  onClose,
}: EpubReaderViewProps): ReactNode {
  const reader = useReader() as unknown as ReaderApi;
  const { settings, updateSettings } = useReadingSettings();

  const [src, setSrc] = useState<string | null>(null);
  const [initialLocation, setInitialLocation] = useState<string | undefined>();
  const [fileError, setFileError] = useState(false);
  const [ready, setReady] = useState(false);
  const [size, setSize] = useState<{ width: number; height: number } | null>(
    null,
  );

  const [progressPct, setProgressPct] = useState(
    Math.round(book.progress?.current_position ?? 0),
  );
  const [chapterTitle, setChapterTitle] = useState("");
  const [currentCfi, setCurrentCfi] = useState("");
  const [bookmarks, setBookmarks] = useState<Bookmark[]>([]);

  const [tocOpen, setTocOpen] = useState(false);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [bookmarksOpen, setBookmarksOpen] = useState(false);

  const currentCfiRef = useRef("");
  const progressPctRef = useRef(progressPct);
  const sectionRef = useRef<{ index: number; label: string }>({
    index: 0,
    label: "",
  });
  const bookmarksRef = useRef<Bookmark[]>([]);
  const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    bookmarksRef.current = bookmarks;
  }, [bookmarks]);

  // Download the EPUB with auth, and resolve the saved resume location.
  useEffect(() => {
    let active = true;
    (async () => {
      try {
        const progress = await getReadingProgress(ebookId).catch(() => null);
        if (active && progress?.epub_location) {
          setInitialLocation(progress.epub_location);
          currentCfiRef.current = progress.epub_location;
          setCurrentCfi(progress.epub_location);
        }
        const token = await getAccessToken();
        const url = ebooksApi.getFileUrl(ebookId);
        const dest = `${FileSystem.cacheDirectory}ebook-${ebookId}.epub`;
        const result = await FileSystem.downloadAsync(url, dest, {
          headers: token ? { Authorization: `Bearer ${token}` } : undefined,
        });
        if (!active) return;
        if (result.status >= 400) {
          setFileError(true);
          return;
        }
        setSrc(result.uri);
      } catch {
        if (active) setFileError(true);
      }
    })();
    return () => {
      active = false;
    };
  }, [ebookId]);

  // Load existing bookmarks/highlights for this book.
  useEffect(() => {
    let active = true;
    fetchBookmarks(ebookId)
      .then((res) => {
        if (active) setBookmarks(res.results);
      })
      .catch(() => {
        /* bookmarks are optional */
      });
    return () => {
      active = false;
    };
  }, [ebookId]);

  // Apply typography/theme to the rendition once it is ready and on changes.
  useEffect(() => {
    if (!ready) return;
    try {
      reader.changeTheme?.(buildEpubTheme(settings));
      reader.changeFontSize?.(`${settings.font_size}px`);
      reader.changeFontFamily?.(FONT_STACKS[settings.font_family]);
    } catch {
      /* ignore rendition styling errors */
    }
  }, [ready, settings, reader]);

  const flushProgress = useCallback(() => {
    const cfi = currentCfiRef.current;
    if (!cfi) return;
    updateReadingProgress(ebookId, {
      percentage: progressPctRef.current,
      epub_location: cfi,
      current_chapter: sectionRef.current.index + 1,
    }).catch(() => {
      /* progress is best-effort */
    });
  }, [ebookId]);

  // Flush the latest progress when leaving the reader.
  useEffect(
    () => () => {
      if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
      flushProgress();
    },
    [flushProgress],
  );

  const handleReady = useCallback(() => {
    setReady(true);
    bookmarksRef.current.forEach((bookmark) => {
      if (bookmark.highlight_color && bookmark.epub_cfi) {
        try {
          reader.addAnnotation?.("highlight", bookmark.epub_cfi, undefined, {
            fill: bookmark.highlight_color,
          });
        } catch {
          /* highlight rendering is best-effort */
        }
      }
    });
  }, [reader]);

  const handleLocationChange = useCallback(
    (
      _total: number,
      location: EpubLocation,
      progress: number,
      section: EpubSection | null,
    ) => {
      const cfi = location?.start?.cfi ?? "";
      if (cfi) {
        currentCfiRef.current = cfi;
        setCurrentCfi(cfi);
      }
      const pct = Math.round(progress ?? 0);
      progressPctRef.current = pct;
      setProgressPct(pct);
      const label = section?.label?.trim() ?? "";
      sectionRef.current = {
        index: section?.index ?? sectionRef.current.index,
        label,
      };
      setChapterTitle(label);

      if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
      saveTimerRef.current = setTimeout(flushProgress, PROGRESS_SAVE_MS);
    },
    [flushProgress],
  );

  const handleSelected = useCallback(
    async (selectedText: string, cfiRange: string) => {
      const excerpt = (selectedText ?? "").slice(0, 280);
      try {
        const created = await createMarker({
          ebook: ebookId,
          epub_cfi: cfiRange,
          chapter_index: sectionRef.current.index,
          chapter_title: sectionRef.current.label,
          location_text: excerpt,
          content: excerpt,
          highlight_color: HIGHLIGHT_COLOR,
        });
        setBookmarks((prev) => [created, ...prev]);
        try {
          reader.addAnnotation?.("highlight", cfiRange, undefined, {
            fill: HIGHLIGHT_COLOR,
          });
          reader.removeSelection?.();
        } catch {
          /* annotation rendering is best-effort */
        }
      } catch {
        /* ignore highlight save failures */
      }
    },
    [ebookId, reader],
  );

  const handleToggleBookmarkHere = useCallback(async () => {
    const cfi = currentCfiRef.current;
    if (!cfi) return;
    const existing = bookmarksRef.current.find((b) => b.epub_cfi === cfi);
    if (existing) {
      setBookmarks((prev) => prev.filter((b) => b.id !== existing.id));
      try {
        reader.removeAnnotationByCfi?.(cfi);
      } catch {
        /* best-effort */
      }
      deleteBookmark(existing.id).catch(() => {});
      return;
    }
    try {
      const created = await createMarker({
        ebook: ebookId,
        epub_cfi: cfi,
        chapter_index: sectionRef.current.index,
        chapter_title: sectionRef.current.label,
        location_text: sectionRef.current.label,
      });
      setBookmarks((prev) => [created, ...prev]);
    } catch {
      /* ignore bookmark save failures */
    }
  }, [ebookId, reader]);

  const handleSelectBookmark = useCallback(
    (bookmark: Bookmark) => {
      setBookmarksOpen(false);
      try {
        reader.goToLocation?.(bookmark.epub_cfi);
      } catch {
        /* best-effort */
      }
    },
    [reader],
  );

  const handleDeleteBookmark = useCallback(
    (bookmark: Bookmark) => {
      setBookmarks((prev) => prev.filter((b) => b.id !== bookmark.id));
      try {
        if (bookmark.highlight_color) {
          reader.removeAnnotationByCfi?.(bookmark.epub_cfi);
        }
      } catch {
        /* best-effort */
      }
      deleteBookmark(bookmark.id).catch(() => {});
    },
    [reader],
  );

  const handleTocSelect = useCallback(
    (href: string) => {
      setTocOpen(false);
      try {
        reader.goToLocation?.(href);
      } catch {
        /* best-effort */
      }
    },
    [reader],
  );

  const onReaderLayout = useCallback((event: LayoutChangeEvent) => {
    const { width, height } = event.nativeEvent.layout;
    setSize((prev) =>
      prev && prev.width === width && prev.height === height
        ? prev
        : { width, height },
    );
  }, []);

  const palette = resolvePalette(settings);
  const isBookmarked = bookmarks.some((b) => b.epub_cfi === currentCfi);

  return (
    <SafeAreaView
      style={[styles.container, { backgroundColor: palette.background }]}
      edges={["top", "bottom"]}
    >
      <ReaderToolbar
        title={book.title}
        chapterTitle={chapterTitle}
        progressPct={progressPct}
        settings={settings}
        isBookmarked={isBookmarked}
        onBack={onClose}
        onToggleToc={() => setTocOpen(true)}
        onToggleSettings={() => setSettingsOpen(true)}
        onToggleBookmarks={() => setBookmarksOpen(true)}
        onToggleBookmarkHere={handleToggleBookmarkHere}
      />

      <View style={styles.readerArea} onLayout={onReaderLayout}>
        {fileError ? (
          <View style={styles.centered}>
            <Text style={[styles.message, { color: palette.text }]}>
              Could not download this book. Check your connection and try again.
            </Text>
          </View>
        ) : src && size ? (
          <Reader
            src={src}
            fileSystem={useFileSystem}
            width={size.width}
            height={size.height}
            initialLocation={initialLocation}
            enableSelection
            flow="paginated"
            defaultTheme={buildEpubTheme(settings)}
            onReady={handleReady}
            onLocationChange={handleLocationChange}
            onSelected={handleSelected}
          />
        ) : (
          <View style={styles.centered}>
            <ActivityIndicator size="large" color="#4f8ef7" />
          </View>
        )}
      </View>

      <TocModal
        visible={tocOpen}
        toc={reader.toc ?? []}
        settings={settings}
        onSelect={handleTocSelect}
        onClose={() => setTocOpen(false)}
      />
      <ReadingSettingsModal
        visible={settingsOpen}
        settings={settings}
        onChange={updateSettings}
        onClose={() => setSettingsOpen(false)}
      />
      <BookmarksModal
        visible={bookmarksOpen}
        bookmarks={bookmarks}
        settings={settings}
        onSelect={handleSelectBookmark}
        onDelete={handleDeleteBookmark}
        onClose={() => setBookmarksOpen(false)}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  readerArea: {
    flex: 1,
  },
  centered: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    padding: 24,
  },
  message: {
    fontSize: 15,
    textAlign: "center",
  },
});
