import { FC, PropsWithChildren, createContext, useCallback, useContext, useEffect, useState } from 'react';
import { COMMANDS, DATA, LIST, LOAD, SAVE } from '../constants';

interface IDataContext {
  list?: IList[];
  file?: string;
  bookMarks?: IBookMark[];
  isLoading?: boolean;
  isSaving?: boolean;
  refresh?: (path?: string) => void;
  load?: (path: string) => void;
  addBookMark?: (path: string, bookMarks: IBookMark) => void;
  removeBookMark?: (path: string, index: number) => void;
}

interface IData {
  file: string;
  bookMarks?: IBookMark[];
}

interface IJSONResponse {
  id: typeof LIST | typeof LOAD | typeof SAVE;
  data: IList[] | IData;
}

export interface IList {
  name: string;
  type: 'file' | 'directory';
  // Base64 PNG
  thumb?: string;
  pages?: number;
}

export interface IBookMark {
  page: number;
  title: string;
}

export const DataContext = createContext<IDataContext>({});

export const useData = () => {
  const { list, file, bookMarks, isLoading, isSaving, refresh, load, addBookMark, removeBookMark } =
    useContext(DataContext);

  if (
    list !== undefined &&
    file !== undefined &&
    bookMarks !== undefined &&
    isLoading !== undefined &&
    isSaving !== undefined &&
    refresh &&
    load &&
    addBookMark &&
    removeBookMark
  )
    return { list, file, bookMarks, isLoading, isSaving, refresh, load, addBookMark, removeBookMark };
  else
    throw new Error(
      `Couldn't find context. Is your component inside a DataProvider? ${JSON.stringify({
        list: list !== undefined,
        file: file !== undefined,
        bookMarks: bookMarks !== undefined,
        isLoading: isLoading !== undefined,
        isSaving: isSaving !== undefined,
        refresh: refresh !== undefined,
        load: load !== undefined,
        addBookMark: addBookMark !== undefined,
        removeBookMark: removeBookMark !== undefined,
      })}`
    );
};

interface IDataContextProps {
  dataListener: ViewListener.ViewListener;
}

const DataContextProvider: FC<PropsWithChildren<IDataContextProps>> = ({ dataListener, children }) => {
  const [list, setList] = useState<IList[]>([]);
  const [file, setFile] = useState('');
  const [bookMarks, setBookMarks] = useState<IBookMark[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [isSaving, setIsSaving] = useState(false);

  useEffect(() => {
    dataListener.on(DATA, dataCallback);
    setIsLoading(true);
    setTimeout(() => refresh(), 1000);

    return () => dataListener?.off(DATA, dataCallback);
  }, []);

  const dataCallback = useCallback((args: string) => {
    try {
      const json: IJSONResponse = JSON.parse(args);
      console.log(json);
      switch (json.id) {
        case LIST: {
          setList((json.data as IList[]).sort((a, b) => a.name.localeCompare(b.name)) ?? []);
          break;
        }
        case LOAD: {
          const d = json.data as IData;
          setFile(d.file);
          if (d.bookMarks) {
            setBookMarks(d.bookMarks);
          }
          break;
        }
        case SAVE: {
          setIsSaving(false);
          break;
        }
      }
      window.dispatchEvent(new Event('resize'));
    } catch (e) {
      console.warn(e);
    }
    setIsLoading(false);
  }, []);

  const refresh = (path = '') => {
    setIsLoading(true);
    dataListener.call('COMM_BUS_WASM_CALLBACK', COMMANDS, JSON.stringify({ cmd: LIST, path }));
  };

  const load = (path: string) => {
    setIsLoading(true);
    dataListener.call('COMM_BUS_WASM_CALLBACK', COMMANDS, JSON.stringify({ cmd: LOAD, file: path }));
  };

  const save = (path: string, _bookMarks: IBookMark[]) => {
    setIsSaving(true);
    dataListener.call('COMM_BUS_WASM_CALLBACK', COMMANDS, JSON.stringify({ cmd: SAVE, path, bookMarks: _bookMarks }));
  };

  const addBookMark = (path: string, bookMark: IBookMark) => {
    setBookMarks((prev) => {
      const _new = [...prev, bookMark];
      _new.sort((a, b) => a.page - b.page);
      save(path, _new);
      return _new;
    });
  };

  const removeBookMark = (path: string, index: number) => {
    setBookMarks((prev) => {
      prev.splice(index, 1);
      prev.sort((a, b) => a.page - b.page);
      save(path, prev);
      return [...prev];
    });
  };

  return (
    <DataContext.Provider
      value={{ list, file, bookMarks, isLoading, isSaving, refresh, load, addBookMark, removeBookMark }}
    >
      {children}
    </DataContext.Provider>
  );
};

export default DataContextProvider;
