import { ToasterService } from '@features/global/services/toaster-service';
import { LoadingStateInitTrue } from '@features/global/state/atoms/Loading';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { useCallback, useState } from 'react';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { DriveItemAtom, DriveItemChildrenAtom, DriveItemPagination } from '../state/store';
import { DriveItem } from '../types';
import { useDriveActions } from './use-drive-actions';
import { useDriveUpload } from './use-drive-upload';
import short from 'short-uuid';

/**
 * Get in store single item and expose methods to operate on it
 * @param id
 * @returns
 */
export const useDriveItem = (id: string) => {
  const companyId = useRouterCompany();
  const item = useRecoilValue(DriveItemAtom(id));
  // const children = useRecoilValue(DriveItemChildrenAtom(id));
  const [children, setChildren] = useRecoilState(DriveItemChildrenAtom(id));
  const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
  const [_, setPaginateItem] = useRecoilState(DriveItemPagination);
  const {
    refresh: refreshItem,
    create,
    update: _update,
    updateLevel: _updateLevel,
    remove: _remove,
    restore: _restore,
    nextPage,
  } = useDriveActions();
  const { uploadVersion: _uploadVersion } = useDriveUpload();
  const refresh = useCallback(
    async (parentId: string, resetPagination?: boolean) => {
      setLoading(true);
      try {
        setPaginateItem(prev => ({ ...prev, page: 0 }));
        await refreshItem(parentId, resetPagination);
      } finally {
        setLoading(false);
      }
    },
    [id, setLoading, refreshItem],
  );

  const remove = useCallback(async () => {
    setLoading(true);
    try {
      await _remove(id, item?.item?.parent_id || '');
    } catch (e) {
      ToasterService.error('Unable to remove this file.');
    }
    setLoading(false);
  }, [id, setLoading, refresh, item?.item?.parent_id]);

  const restore = useCallback(async () => {
    setLoading(true);
    try {
      await _restore(id, item?.item?.parent_id || '');
    } catch (e) {
      ToasterService.error('Unable to restore this item.');
    }
    setLoading(false);
  }, [id, setLoading, refresh, item?.item?.parent_id]);

  const update = useCallback(
    async (update: Partial<DriveItem>, skipLoading = false) => {
      if (!skipLoading) setLoading(true);
      try {
        await _update(update, id, item?.item?.parent_id || '');
      } catch (e) {
        ToasterService.error('Unable to update this file.');
      }
      setLoading(false);
    },
    [id, setLoading, refresh, item?.item?.parent_id],
  );

  const updateLevel = useCallback(
    async (userId: string, level: string) => {
      setLoading(true);
      try {
        await _updateLevel(id, userId, level);
      } catch (e) {
        ToasterService.error('Unable to update user access.');
      }
      setLoading(false);
    },
    [id, setLoading, refresh, item?.item?.parent_id],
  );

  const uploadVersion = useCallback(
    async (file: File) => {
      setLoading(true);
      try {
        await _uploadVersion(file, { companyId, id });
      } catch (e) {
        ToasterService.error('Unable to create a new version of this file.');
      }
      setLoading(false);
    },
    [companyId, id, setLoading, refresh, item?.item?.parent_id],
  );

  const loadNextPage = useRecoilCallback(
    ({ set, snapshot }) =>
      async (id: string) => {
        // get current pagination state
        const pagination = await snapshot.getPromise(DriveItemPagination);

        // if end is true, do not load more
        if (pagination.lastPage === true) return;
        setLoading(true);

        try {
          const details = await nextPage(id);
          if (details.children.length === 0) {
            set(DriveItemPagination, prev => ({
              ...prev,
              lastPage: true,
            }));
          }
          // set children and remove duplicates
          setChildren(prev => [
            ...prev,
            ...details.children.filter(
              (item, index, self) =>
                index === self.findIndex(t => t.id === item.id),
            ),
          ]);
        } catch (e) {
          // set pagination end to true
          set(DriveItemPagination, prev => ({
            ...prev,
            lastPage: true,
          }));
          console.log('error loading next page: ', e);
          ToasterService.error('Unable to load more items.');
        } finally {
          set(DriveItemPagination, prev => ({
            ...prev,
            page: (prev.page + prev.limit),
          }));
        }
        setLoading(false);
      },
    [id, nextPage],
  );

  const inTrash =
    id.includes('trash') ||
    item?.path?.some(i => i?.parent_id?.includes('trash')) ||
    item?.item?.is_in_trash;
  const sharedWithMe = id == 'shared_with_me';

  return {
    sharedWithMe,
    inTrash,
    loading: loading,
    children: children || [],
    details: item,
    path: item?.path,
    item: item?.item,
    access: item?.access,
    websockets: item?.websockets,
    versions: item?.versions,
    uploadVersion,
    create,
    update,
    updateLevel,
    remove,
    refresh,
    loadNextPage,
  };
};

const translator = short();
export const getPublicLink = (item?: DriveItem): string => {
  let publicLink = `${document.location.protocol}//${document.location.host}`;
  try {
    publicLink +=
      `/shared/${translator.fromUUID(item?.company_id || '')}` +
      `/drive/${translator.fromUUID(item?.id || '')}` +
      `/t/${item?.access_info?.public?.token}`;
  } catch (e) {
    return publicLink;
  }

  return publicLink;
};
