import {
  addReducer, getDispatch, getGlobal, setGlobal,
} from '../../../lib/teact/teactn';

import { ApiUser } from '../../../api/types';
import { ManagementProgress } from '../../../types';

import { debounce } from '../../../util/schedulers';
import { buildCollectionByKey } from '../../../util/iteratees';
import { isChatPrivate } from '../../helpers';
import { callApi } from '../../../api/gramjs';
import { selectChat, selectUser } from '../../selectors';
import {
  addChats, addUsers, updateChat, updateManagementProgress, updateUser, updateUsers,
} from '../../reducers';

const runDebouncedForFetchFullUser = debounce((cb) => cb(), 500, false, true);
const TOP_PEERS_REQUEST_COOLDOWN = 60; // 1 min

addReducer('loadFullUser', (global, actions, payload) => {
  const { userId } = payload!;
  const user = selectUser(global, userId);
  if (!user) {
    return;
  }

  const { id, accessHash } = user;

  runDebouncedForFetchFullUser(() => callApi('fetchFullUser', { id, accessHash }));
});

addReducer('loadUser', (global, actions, payload) => {
  const { userId } = payload!;
  const user = selectUser(global, userId);
  if (!user) {
    return;
  }

  (async () => {
    const updatedUsers = await callApi('fetchUsers', { users: [user] });
    if (!updatedUsers) {
      return;
    }

    global = getGlobal();
    global = updateUsers(global, buildCollectionByKey(updatedUsers, 'id'));
    setGlobal(global);
  })();
});

addReducer('loadTopUsers', (global) => {
  const {
    serverTimeOffset,
    topPeers: {
      hash, lastRequestedAt,
    },
  } = global;

  if (!lastRequestedAt || Date.now() / 1000 + serverTimeOffset - lastRequestedAt > TOP_PEERS_REQUEST_COOLDOWN) {
    void loadTopUsers(hash);
  }
});

addReducer('loadContactList', (global) => {
  const { hash } = global.contactList || {};
  void loadContactList(hash);
});

addReducer('loadCurrentUser', () => {
  void callApi('fetchCurrentUser');
});

addReducer('updateContact', (global, actions, payload) => {
  const {
    userId, isMuted, firstName, lastName,
  } = payload!;

  void updateContact(userId, isMuted, firstName, lastName);
});

addReducer('deleteUser', (global, actions, payload) => {
  const { userId } = payload!;

  void deleteUser(userId);
});

async function loadTopUsers(usersHash?: number) {
  const result = await callApi('fetchTopUsers', { hash: usersHash });
  if (!result) {
    return;
  }

  const { hash, ids, users } = result;

  let global = getGlobal();
  global = addUsers(global, buildCollectionByKey(users, 'id'));
  global = {
    ...global,
    topPeers: {
      ...global.topPeers,
      hash,
      userIds: ids,
      lastRequestedAt: Date.now() / 1000 + global.serverTimeOffset,
    },
  };
  setGlobal(global);
}

async function loadContactList(hash?: number) {
  const contactList = await callApi('fetchContactList', { hash });
  if (!contactList) {
    return;
  }

  let global = addUsers(getGlobal(), buildCollectionByKey(contactList.users, 'id'));
  global = addChats(global, buildCollectionByKey(contactList.chats, 'id'));

  // Sort contact list by Last Name (or First Name), with latin names being placed first
  const getCompareString = (user: ApiUser) => (user.lastName || user.firstName || '');
  const collator = new Intl.Collator('en-US');

  const sortedUsers = contactList.users.sort((a, b) => (
    collator.compare(getCompareString(a), getCompareString(b))
  )).filter((user) => !user.isSelf);

  setGlobal({
    ...global,
    contactList: {
      hash: contactList.hash,
      userIds: sortedUsers.map((user) => user.id),
    },
  });
}

async function updateContact(
  userId: number,
  isMuted: boolean,
  firstName: string,
  lastName?: string,
) {
  const global = getGlobal();
  const user = selectUser(global, userId);
  if (!user) {
    return;
  }

  getDispatch().updateChatMutedState({ chatId: userId, isMuted });

  setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.InProgress));

  const result = await callApi('updateContact', { phone: user.phoneNumber, firstName, lastName });

  if (result) {
    setGlobal(updateUser(
      getGlobal(),
      user.id,
      {
        firstName,
        lastName,
      },
    ));
  }

  setGlobal(updateManagementProgress(getGlobal(), ManagementProgress.Complete));
}

async function deleteUser(userId: number) {
  const global = getGlobal();
  const user = selectUser(global, userId);

  if (!user) {
    return;
  }

  const { id, accessHash } = user;

  await callApi('deleteUser', { id, accessHash });
}

addReducer('loadProfilePhotos', (global, actions, payload) => {
  const { profileId } = payload!;
  const isPrivate = isChatPrivate(profileId);
  const user = isPrivate ? selectUser(global, profileId) : undefined;
  const chat = !isPrivate ? selectChat(global, profileId) : undefined;

  (async () => {
    const result = await callApi('fetchProfilePhotos', user, chat);
    if (!result || !result.photos) {
      return;
    }

    let newGlobal = getGlobal();
    if (isPrivate) {
      newGlobal = updateUser(newGlobal, profileId, { photos: result.photos });
    } else {
      newGlobal = addUsers(newGlobal, buildCollectionByKey(result.users!, 'id'));
      newGlobal = updateChat(newGlobal, profileId, { photos: result.photos });
    }

    setGlobal(newGlobal);
  })();
});
