import {
  addCallback, addReducer, getGlobal, removeCallback,
} from '../lib/teact/teactn';

import { GlobalState } from './types';
import { MAIN_THREAD_ID } from '../api/types';

import { onIdle, throttle } from '../util/schedulers';
import {
  DEBUG,
  GLOBAL_STATE_CACHE_DISABLED,
  GLOBAL_STATE_CACHE_KEY,
  GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
  GRAMJS_SESSION_ID_KEY,
  MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN,
} from '../config';
import { IS_MOBILE_SCREEN } from '../util/environment';
import { pick } from '../util/iteratees';
import { INITIAL_STATE } from './initial';
import { selectCurrentMessageList } from '../modules/selectors';

const UPDATE_THROTTLE = 1000;

const updateCacheThrottled = throttle(updateCache, UPDATE_THROTTLE, false);

export function initCache() {
  if (GLOBAL_STATE_CACHE_DISABLED) {
    return;
  }

  addReducer('saveSession', () => {
    addCallback(updateCacheThrottled);
  });

  addReducer('signOut', () => {
    removeCallback(updateCacheThrottled);
    localStorage.removeItem(GLOBAL_STATE_CACHE_KEY);
  });
}

export function loadCache(initialState: GlobalState) {
  if (!GLOBAL_STATE_CACHE_DISABLED) {
    const hasActiveSession = localStorage.getItem(GRAMJS_SESSION_ID_KEY);
    if (hasActiveSession) {
      addCallback(updateCacheThrottled);
      return readCache(initialState);
    }
  }

  return undefined;
}

function readCache(initialState: GlobalState) {
  if (DEBUG) {
    // eslint-disable-next-line no-console
    console.time('global-state-cache-read');
  }

  const json = localStorage.getItem(GLOBAL_STATE_CACHE_KEY);
  const cached = json ? JSON.parse(json) as GlobalState : undefined;

  if (DEBUG) {
    // eslint-disable-next-line no-console
    console.timeEnd('global-state-cache-read');
  }

  if (cached) {
    // Pre-fill defaults for new settings which may be missing in older cache
    cached.settings.byKey = {
      ...initialState.settings.byKey,
      ...cached.settings.byKey,
    };
  }

  return {
    ...initialState,
    ...cached,
  };
}

function updateCache() {
  onIdle(() => {
    const global = getGlobal();

    if (global.isLoggingOut) {
      return;
    }

    const reducedGlobal: GlobalState = {
      ...INITIAL_STATE,
      ...pick(global, [
        'authState',
        'authPhoneNumber',
        'authRememberMe',
        'authIsSessionRemembered',
        'authNearestCountry',
        'currentUserId',
        'contactList',
        'chatFolders',
        'topPeers',
        'recentEmojis',
      ]),
      isChatInfoShown: reduceShowChatInfo(global),
      users: reduceUsers(global),
      chats: reduceChats(global),
      messages: reduceMessages(global),
      globalSearch: {
        recentlyFoundChatIds: global.globalSearch.recentlyFoundChatIds,
      },
      settings: reduceSettings(global),
    };

    const json = JSON.stringify(reducedGlobal);
    localStorage.setItem(GLOBAL_STATE_CACHE_KEY, json);
  });
}

function reduceShowChatInfo(global: GlobalState): boolean {
  return window.innerWidth > MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN
    ? global.isChatInfoShown
    : false;
}

function reduceUsers(global: GlobalState): GlobalState['users'] {
  const { users: { byId, selectedId } } = global;

  return {
    byId,
    selectedId: window.innerWidth > MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN ? selectedId : undefined,
  };
}

function reduceChats(global: GlobalState): GlobalState['chats'] {
  const chatIdsToSave = [
    ...(global.chats.listIds.active || []).slice(0, GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT),
  ];
  const { chatId: currentChatId } = selectCurrentMessageList(global) || {};

  return {
    ...global.chats,
    byId: pick(global.chats.byId, currentChatId ? [...chatIdsToSave, currentChatId] : chatIdsToSave),
    listIds: {
      active: chatIdsToSave,
    },
    isFullyLoaded: {},
    orderedPinnedIds: {
      active: global.chats.orderedPinnedIds.active,
    },
  };
}

function reduceMessages(global: GlobalState): GlobalState['messages'] {
  const byChatId: GlobalState['messages']['byChatId'] = {};
  const { chatId: currentChatId } = selectCurrentMessageList(global) || {};

  const chatIdsToSave = [
    ...(global.chats.listIds.active || []).slice(0, GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT),
    ...(currentChatId ? [currentChatId] : []),
  ];
  chatIdsToSave.forEach((chatId) => {
    const current = global.messages.byChatId[chatId];
    if (!current) {
      return;
    }

    const mainThread = current.threadsById[MAIN_THREAD_ID];
    if (!mainThread || !mainThread.viewportIds) {
      return;
    }

    byChatId[chatId] = {
      byId: pick(current.byId, mainThread.viewportIds),
      threadsById: {
        [MAIN_THREAD_ID]: mainThread,
      },
    };
  });

  const currentMessageList = selectCurrentMessageList(global);

  return {
    byChatId,
    messageLists: !currentMessageList || IS_MOBILE_SCREEN ? undefined : [{
      ...currentMessageList,
      threadId: MAIN_THREAD_ID,
      type: 'thread',
    }],
  };
}

function reduceSettings(global: GlobalState): GlobalState['settings'] {
  const { byKey } = global.settings;

  return {
    byKey,
    privacy: {},
  };
}
