import {
  createContext,
  useContext,
  useReducer,
  useCallback,
  useMemo,
  type ReactNode,
} from "react";
import type { Bookmark, CreateMarkerPayload, MarkerEntry, MarkersByBook } from "@/types";
import * as annotationsApi from "@/api/annotations";
import { bookmarkToMarkerEntry, groupMarkersByBook, sortMarkers } from "@/utils/markers";

interface AnnotationsState {
  bookmarks: Bookmark[];
  bookmarksLoading: boolean;
  error: string | null;
}

const initialState: AnnotationsState = {
  bookmarks: [],
  bookmarksLoading: false,
  error: null,
};

type AnnotationsAction =
  | { type: "FETCH_BOOKMARKS_START" }
  | { type: "FETCH_BOOKMARKS_SUCCESS"; payload: Bookmark[] }
  | { type: "SET_ERROR"; payload: string }
  | { type: "REMOVE_BOOKMARK"; payload: string }
  | { type: "ADD_BOOKMARK"; payload: Bookmark };

function annotationsReducer(
  state: AnnotationsState,
  action: AnnotationsAction,
): AnnotationsState {
  switch (action.type) {
    case "FETCH_BOOKMARKS_START":
      return { ...state, bookmarksLoading: true, error: null };
    case "FETCH_BOOKMARKS_SUCCESS": {
      const sorted = [...action.payload].sort((a, b) => {
        if (a.chapter_index !== b.chapter_index) return a.chapter_index - b.chapter_index;
        return a.epub_cfi.localeCompare(b.epub_cfi);
      });
      return { ...state, bookmarks: sorted, bookmarksLoading: false };
    }
    case "SET_ERROR":
      return { ...state, error: action.payload, bookmarksLoading: false };
    case "REMOVE_BOOKMARK":
      return {
        ...state,
        bookmarks: state.bookmarks.filter((b) => b.id !== action.payload),
      };
    case "ADD_BOOKMARK": {
      const next = [...state.bookmarks, action.payload].sort((a, b) => {
        if (a.chapter_index !== b.chapter_index) return a.chapter_index - b.chapter_index;
        return a.epub_cfi.localeCompare(b.epub_cfi);
      });
      return { ...state, bookmarks: next };
    }
    default:
      return state;
  }
}

interface AnnotationsContextValue {
  state: AnnotationsState;
  markers: MarkerEntry[];
  markersByBook: MarkersByBook[];
  loadBookmarks: (ebookId?: string | number) => Promise<void>;
  addMarker: (data: CreateMarkerPayload) => Promise<Bookmark>;
  removeBookmark: (id: string) => Promise<void>;
}

const AnnotationsContext = createContext<AnnotationsContextValue | null>(null);

export function AnnotationsProvider({
  children,
}: {
  children: ReactNode;
}): React.ReactElement {
  const [state, dispatch] = useReducer(annotationsReducer, initialState);

  const loadBookmarks = useCallback(async (ebookId?: string | number) => {
    dispatch({ type: "FETCH_BOOKMARKS_START" });
    try {
      const response = await annotationsApi.fetchBookmarks(ebookId);
      dispatch({ type: "FETCH_BOOKMARKS_SUCCESS", payload: response.results });
    } catch (err) {
      dispatch({
        type: "SET_ERROR",
        payload: err instanceof Error ? err.message : "Failed to load bookmarks",
      });
    }
  }, []);

  const addMarker = useCallback(async (data: CreateMarkerPayload): Promise<Bookmark> => {
    const bookmark = await annotationsApi.createMarker(data);
    dispatch({ type: "ADD_BOOKMARK", payload: bookmark });
    return bookmark;
  }, []);

  const removeBookmark = useCallback(async (id: string) => {
    await annotationsApi.deleteBookmark(id);
    dispatch({ type: "REMOVE_BOOKMARK", payload: id });
  }, []);

  const markers = useMemo(
    () => state.bookmarks.map(bookmarkToMarkerEntry).sort(sortMarkers),
    [state.bookmarks],
  );

  const markersByBook = useMemo(
    () => groupMarkersByBook(state.bookmarks),
    [state.bookmarks],
  );

  const value: AnnotationsContextValue = {
    state,
    markers,
    markersByBook,
    loadBookmarks,
    addMarker,
    removeBookmark,
  };

  return (
    <AnnotationsContext.Provider value={value}>
      {children}
    </AnnotationsContext.Provider>
  );
}

export function useAnnotations(): AnnotationsContextValue {
  const context = useContext(AnnotationsContext);
  if (!context) {
    throw new Error("useAnnotations must be used within an AnnotationsProvider");
  }
  return context;
}
