/**
 * @mergeTarget
 * @module Src/Controllers/Storages
 */

import { LisioUserParsed, StorageInterface } from "@lisio/lisio-profils";

/**
 * Class representing a extension storage controller.\
 * This class implements the StorageInterface of private library lisio-profiles.\
 * It aims to centralize the processus of storing items in extension storage.\
 * If something need to store datas in extension storage it has to use this class.\
 * To execute his responsability this class does :
 *  * Store items in extension storage
 *  * Retrieve items in extension storage
 */
class ExtensionStorageController implements StorageInterface {
  /**
   * @async
   * Public method to retrieve all stored users
   * @returns Returns a promise containing all users
   * @source
   */
  public async getAllUsers(): Promise<LisioUserParsed[]> {
    const users: LisioUserParsed[] = [];
    const usernames: string[] = await this.getUsernames();
    for (const username of usernames) {
      const user: LisioUserParsed | undefined = await this.getUser(username);
      if (user != undefined) {
        users.push(user);
      }
    }
    return users;
  }
  /**
   * @async
   * Public method to retrieve desired stored user
   * @param {string} username - Username of desired user
   * @returns Returns a promise containing desired user or undefined if no user exists
   * @source
   */
  public async getUser(username: string): Promise<LisioUserParsed | undefined> {
    const user = (await chrome.storage.local.get(`user_${username}`))[
      `user_${username}`
    ];
    if (user === null) {
      return undefined;
    } else {
      return user as LisioUserParsed;
    }
  }
  /**
   * @async
   * Public method to save user
   * @param {LisioUserParsed} user - User to save
   * @returns Returns a promise containing nothing
   * @source
   */
  public async setUser(user: LisioUserParsed): Promise<void> {
    this.setUsername(user.name);
    const obj: { [key: string]: LisioUserParsed } = {};
    obj[`user_${user.name}`] = user;
    await chrome.storage.local.set(obj);
  }
  /**
   * @async
   * Public method to delete user
   * @param {string} username - Username of user to be deleted
   * @returns Returns a promise containing nothing
   * @source
   */
  public async deleteUser(username: string): Promise<void> {
    this.removeUsername(username);
    await chrome.storage.local.remove(`user_${username}`);
  }
  /**
   * @async
   * Public method to delete all user
   * @returns Returns a promise containing nothing
   * @source
   */
  public async deleteAllUsers(): Promise<void> {
    const usernames: string[] = await this.getUsernames();
    for (const username of usernames) {
      await chrome.storage.local.remove(`user_${username}`);
    }
    await chrome.storage.local.set({ usernames: [] });
  }

  /**
   * @async
   * Public method to clear all user
   * @returns Returns a promise containing nothing
   * @source
   */
  public async clearUsers(): Promise<void> {
    for (const entry of Object.keys(localStorage)) {
      const match = entry.match(/user_(.+)/);
      if (match != undefined) {
        await chrome.storage.local.remove(`user_${match[1]}`);
      }
    }
    await chrome.storage.local.set({ usernames: [] });
  }

  /**
   * @async
   * Public method to save current storage version
   * @param {number} storageVersion - Current storage version
   * @returns Returns a promise containing nothing
   * @source
   */
  public async setStorageVersion(storageVersion: number): Promise<void> {
    await chrome.storage.local.set({ storage_version: storageVersion });
  }
  /**
   * @async
   * Public method to retrieve stored storage version
   * @returns Returns a promise containing stored storage version
   * @throws {number} If no storage version stored
   * @source
   */
  public async getStorageVersion(): Promise<number> {
    const storageVersion: number | undefined = (
      await chrome.storage.local.get("storage_version")
    )["storage_version"];
    if (storageVersion == undefined) {
      return -1;
    } else {
      return storageVersion;
    }
  }

  /**
   * @async
   * Public method to delete all cookies
   * @returns Returns a promise containing nothing
   * @source
   */
  public async clearAll(): Promise<void> {
    await chrome.storage.local.clear();
  }

  /**
   * @async
   * Public method to retrieve stored popin state
   * @returns Returns a promise containing stored popin state
   * @throws {boolean} If no popin state stored
   * @source
   */
  public async getIsPopinHidden(): Promise<boolean> {
    return (await chrome.storage.local.get("is_popin_hidden"))[
      "is_popin_hidden"
    ];
  }
  /**
   * @async
   * Public method to save popin state (hidden or not)
   * @param {boolean} value - If popin is hidden or not
   * @returns Returns a promise containing nothing
   * @source
   */
  public async setIsPopinHidden(value: boolean): Promise<void> {
    await chrome.storage.local.set({ is_popin_hidden: value });
  }

  /**
   * @async
   * Public method to retrieve stored process state
   * @returns Returns a promise containing stored process state
   * @throws {boolean} If no process state stored
   * @source
   */
  public async getIsProcessEnd(): Promise<boolean> {
    return (
      (await chrome.storage.local.get("is_process_end"))["is_process_end"] ===
      "true"
    );
  }
  /**
   * @async
   * Public method to save process state (ended or not)
   * @param {boolean} value - If process is ended or not
   * @returns Returns a promise containing nothing
   * @source
   */
  public async setIsProcessEnd(value: boolean): Promise<void> {
    await chrome.storage.local.set({ is_process_end: value });
  }

  /**
   * @async
   * Public method to remove a desired username
   * @param {string} username - Desired username to be removed
   * @returns Returns a promise containing nothing
   * @source
   */
  public async removeUsername(username: string): Promise<void> {
    const currentUsernames: string[] = await this.getUsernames();
    const indexOfUsernameToRemove: number = currentUsernames.indexOf(username);
    if(indexOfUsernameToRemove>=0){
      currentUsernames.splice(indexOfUsernameToRemove, 1);
      await chrome.storage.local.set({ usernames: currentUsernames });
    }
  }
  /**
   * @async
   * Public method to save a new username
   * @param {string} username - Username to save
   * @returns Returns a promise containing nothing
   * @source
   */
  public async setUsername(username: string): Promise<void> {
    const savedUsernames: string[] = await this.getUsernames();
    if (!savedUsernames.some((savedUsername) => username === savedUsername)) {
      savedUsernames.push(username);
      await chrome.storage.local.set({ usernames: savedUsernames });
    }
  }

  /**
   * @async
   * Public method to retrieve all usernames
   * @returns Returns a promise containing all usernames
   * @throws {string[]} If not usernames stored
   * @source
   */
  public async getUsernames(): Promise<string[]> {
    const usernames: string[] = [];
    try {
      const currentUsers: string | undefined = (
        await chrome.storage.local.get("usernames")
      )["usernames"];
      if (currentUsers == null) {
        throw new Error("no usernames");
      }
      return usernames.concat(currentUsers);
    } catch (err) {
      return usernames;
    }
  }

  /**
   * @async
   * Public method to rename a saved user.\
   * Thsi method will change the username in cookie name of current user and replace old username by new username in usernames array
   * @param {string} username - Current username to rename
   * @param {string} newUsername - New username
   * @returns Returns a promise containing nothing
   * @source
   */
  public async renameUser(
    username: string,
    newUsername: string,
  ): Promise<void> {
    const user: LisioUserParsed | undefined = await this.getUser(username);
    if (user != undefined) {
      const savedUsernames: string[] = await this.getUsernames();
      savedUsernames[savedUsernames.indexOf(username)] = newUsername;
      await chrome.storage.local.set({ usernames: savedUsernames });

      user.name = newUsername;

      localStorage.removeItem(`user_${username}`);
      const obj: { [key: string]: LisioUserParsed } = {};
      obj[`user_${newUsername}`] = user;
      await chrome.storage.local.set(obj);
    }
  }
}

export { ExtensionStorageController };
