// @flow

/**
 * This serves a bridge between tabs using localStorage to indicate whether an
 * upload is currently in progress (locked) or removed.
 *
 * An alternative is to sync the redux's 'publish::currentUploads' through the
 * wallet's sync process, but let's not pollute the wallet for now.
 */

import { v4 as uuid } from 'uuid';
import { TUS_LOCKED_UPLOADS, TUS_REFRESH_LOCK, TUS_REMOVED_UPLOADS } from 'constants/storage';
import { isLocalStorageAvailable } from 'util/storage';
import { doUpdateUploadRemove, doUpdateUploadProgress } from 'redux/actions/publish';

const localStorageAvailable = isLocalStorageAvailable();

let gTabId: string = '';

function getTabId() {
  if (!gTabId) {
    // We want to maximize bootup speed, so only initialize
    // the tab ID on first use instead when declared.
    gTabId = uuid();
  }
  return gTabId;
}

// ****************************************************************************
// Locked
// ****************************************************************************

function getLockedUploads() {
  if (localStorageAvailable) {
    const storedValue = window.localStorage.getItem(TUS_LOCKED_UPLOADS);
    return storedValue ? JSON.parse(storedValue) : {};
  }
  return {};
}

export function tusIsSessionLocked(guid: string) {
  const lockedUploads = getLockedUploads();
  return lockedUploads[guid] && lockedUploads[guid] !== getTabId();
}

export function tusLockAndNotify(guid: string) {
  const lockedUploads = getLockedUploads();
  if (!lockedUploads[guid] && localStorageAvailable) {
    lockedUploads[guid] = getTabId();
    window.localStorage.setItem(TUS_LOCKED_UPLOADS, JSON.stringify(lockedUploads));
  }
}

/**
 * tusUnlockAndNotify
 *
 * @param guid The upload session to unlock and notify other tabs of.
 *             Passing 'undefined' will clear all sessions locked by this tab.
 */
export function tusUnlockAndNotify(guid?: string) {
  if (!localStorageAvailable) return;

  const lockedUploads = getLockedUploads();

  if (guid) {
    delete lockedUploads[guid];
  } else {
    const ourTabId = getTabId();
    const lockedUploadsEntries = Object.entries(lockedUploads);
    lockedUploadsEntries.forEach(([lockedGuid, tabId]) => {
      if (tabId === ourTabId) {
        delete lockedUploads[lockedGuid];
      }
    });
  }

  if (Object.keys(lockedUploads).length > 0) {
    window.localStorage.setItem(TUS_LOCKED_UPLOADS, JSON.stringify(lockedUploads));
  } else {
    window.localStorage.removeItem(TUS_LOCKED_UPLOADS);
  }
}

// ****************************************************************************
// Removed
// ****************************************************************************

function getRemovedUploads() {
  if (localStorageAvailable) {
    const storedValue = window.localStorage.getItem(TUS_REMOVED_UPLOADS);
    return storedValue ? storedValue.split(',') : [];
  }
  return [];
}

export function tusRemoveAndNotify(guid: string) {
  if (!localStorageAvailable) return;
  const removedUploads = getRemovedUploads();
  removedUploads.push(guid);
  window.localStorage.setItem(TUS_REMOVED_UPLOADS, removedUploads.join(','));
}

export function tusClearRemovedUploads() {
  if (!localStorageAvailable) return;
  window.localStorage.removeItem(TUS_REMOVED_UPLOADS);
}

export function tusClearLockedUploads() {
  if (!localStorageAvailable) return;
  window.localStorage.removeItem(TUS_LOCKED_UPLOADS);
  window.localStorage.setItem(TUS_REFRESH_LOCK, Math.random());
}

// ****************************************************************************
// Respond to changes from other tabs.
// ****************************************************************************

export function tusHandleTabUpdates(storageKey: string) {
  switch (storageKey) {
    case TUS_LOCKED_UPLOADS:
      // The locked IDs are in localStorage, but related GUI is unaware.
      // Send a redux update to force an update.
      window.store.dispatch(doUpdateUploadProgress({ guid: 'force--update' }));
      break;

    case TUS_REFRESH_LOCK:
      window.store.dispatch(doUpdateUploadProgress({ guid: 'refresh--lock' }));
      break;

    case TUS_REMOVED_UPLOADS:
      // The other tab's store has removed this upload, so it's safe to do the
      // same without affecting rehydration.
      if (localStorageAvailable) {
        const removedUploads = getRemovedUploads();
        removedUploads.forEach((guid) => window.store.dispatch(doUpdateUploadRemove(guid)));
      }
      break;
  }
}