import AppStorage from '../storage';
import appMessagesManager, { Dialog, DialogsStorage, FiltersStorage } from './appMessagesManager';
import appMessagesIDsManager from './appMessagesIDsManager';
import appPeersManager from './appPeersManager';
import appChatsManager from './appChatsManager';
import appUsersManager from './appUsersManager';
import apiUpdatesManager from './apiUpdatesManager';
import { copy } from '../utils';
import { logger } from '../logger';

const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day

type State = Partial<{
  dialogs: Dialog[],
  allDialogsLoaded: DialogsStorage['allDialogsLoaded'], 
  peers: {[peerID: string]: any},
  messages: any[],
  contactsList: number[],
  updates: any,
  filters: FiltersStorage['filters'],
  maxSeenMsgID: number,
  stateCreatedTime: number,
  recentEmoji: string[],
  topPeers: number[],
  recentSearch: number[]
}>;

export class AppStateManager {
  public loaded: Promise<State>;
  private log = logger('STATE'/* , LogLevels.error */);

  private state: State = {};

  constructor() {
    this.loadSavedState();
  }

  public loadSavedState() {
    if(this.loaded) return this.loaded;
    return this.loaded = new Promise((resolve, reject) => {
      AppStorage.get<State>('state').then((state) => {
        const time = Date.now();
        if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) {
          this.log('will refresh state', state.stateCreatedTime, time);
          (['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime',
          'updates', 'maxSeenMsgID', 'filters', 'topPeers'] as any as Array<keyof State>).forEach(key => {
            delete state[key];
          });
          //state = {};
        }

        const {dialogs, allDialogsLoaded, peers, messages, contactsList, maxSeenMsgID, updates, filters} = state;
        
        this.state = state || {};
        this.state.peers = peers || {};

        if(!this.state.hasOwnProperty('stateCreatedTime')) {
          this.state.stateCreatedTime = Date.now();
        }

        this.log('state res', dialogs, messages);

        if(maxSeenMsgID && !appMessagesIDsManager.getMessageIDInfo(maxSeenMsgID)[1]) {
          appMessagesManager.maxSeenID = maxSeenMsgID;
        }

        //return resolve();

        if(peers) {
          for(let peerID in peers) {
            let peer = peers[peerID];
            if(+peerID < 0) appChatsManager.saveApiChat(peer);
            else appUsersManager.saveApiUser(peer);
          }
        }

        if(contactsList && Array.isArray(contactsList) && contactsList.length) {
          contactsList.forEach(userID => {
            appUsersManager.pushContact(userID);
          });
          appUsersManager.contactsFillPromise = Promise.resolve(appUsersManager.contactsList);
        }

        if(messages) {
          /* let tempID = this.tempID;

          for(let message of messages) {
            if(message.id < tempID) {
              tempID = message.id;
            }
          }

          if(tempID != this.tempID) {
            this.log('Set tempID to:', tempID);
            this.tempID = tempID;
          } */

          appMessagesManager.saveMessages(messages);

          // FIX FILE_REFERENCE_EXPIRED KOSTIL'1999
          for(let message of messages) {
            if(message.media) {
              appMessagesManager.wrapSingleMessage(message.mid, true);
            }
          }
        }
        
        if(allDialogsLoaded) {
          appMessagesManager.dialogsStorage.allDialogsLoaded = allDialogsLoaded;
        }

        if(filters) {
          for(const filterID in filters) {
            appMessagesManager.filtersStorage.saveDialogFilter(filters[filterID], false);
          }
        }

        if(dialogs) {
          dialogs.forEachReverse(dialog => {
            appMessagesManager.saveConversation(dialog);
          });
        }

        apiUpdatesManager.attach(updates ?? null);
  
        resolve(state);
      }).catch(resolve).finally(() => {
        setInterval(() => this.saveState(), 10000);
      });
    });
  }

  public getState() {
    return this.loadSavedState();
  }

  public saveState() {
    const messages: any[] = [];
    const dialogs: Dialog[] = [];
    const peers = this.state.peers;
    
    for(const folderID in appMessagesManager.dialogsStorage.byFolders) {
      const folder = appMessagesManager.dialogsStorage.getFolder(+folderID);

      for(let dialog of folder) {
        const historyStorage = appMessagesManager.historiesStorage[dialog.peerID];
        const history = [].concat(historyStorage?.pending ?? [], historyStorage?.history ?? []);
  
        dialog = copy(dialog);
        let removeUnread = 0;
        for(const mid of history) {
          const message = appMessagesManager.getMessage(mid);
          if(/* message._ != 'messageEmpty' &&  */message.id > 0) {
            messages.push(message);
    
            if(message.fromID != dialog.peerID) {
              peers[message.fromID] = appPeersManager.getPeer(message.fromID);
            }
  
            dialog.top_message = message.mid;
  
            break;
          } else if(message.pFlags && message.pFlags.unread) {
            ++removeUnread;
          }
        }
  
        if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread; 
  
        dialogs.push(dialog);
  
        peers[dialog.peerID] = appPeersManager.getPeer(dialog.peerID);
      }
    }
    

    const us = apiUpdatesManager.updatesState;
    const updates = {
      seq: us.seq,
      pts: us.pts,
      date: us.date
    };

    const contactsList = [...appUsersManager.contactsList];
    for(const userID of contactsList) {
      if(!peers[userID]) {
        peers[userID] = appUsersManager.getUser(userID);
      }
    }

    const filters = appMessagesManager.filtersStorage.filters;
    //const pinnedOrders = appMessagesManager.dialogsStorage.pinnedOrders;

    AppStorage.set({
      state: Object.assign({}, this.state, {
        dialogs, 
        messages, 
        allDialogsLoaded: appMessagesManager.dialogsStorage.allDialogsLoaded, 
        peers, 
        contactsList,
        filters,
        //pinnedOrders,
        updates,
        maxSeenMsgID: appMessagesManager.maxSeenID
      })
    });
  }

  public pushToState<T extends keyof State>(key: T, value: State[T]) {
    this.state[key] = value;
  }

  public pushPeer(peerID: number) {
    this.state.peers[peerID] = appPeersManager.getPeer(peerID);
  }
}

const appStateManager = new AppStateManager();
// @ts-ignore
if(process.env.NODE_ENV != 'production') {
  (window as any).appStateManager = appStateManager;
}
export default appStateManager;