/*
 * This file is part of the Meeds project (https://meeds.io/).
 * Copyright (C) 2020 Meeds Association
 * contact@meeds.io
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package io.meeds.wallet.service;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.exoplatform.social.core.identity.model.Identity;

import io.meeds.wallet.model.ContractDetail;
import io.meeds.wallet.model.Wallet;
import io.meeds.wallet.model.WalletAddressLabel;
import io.meeds.wallet.model.WalletProvider;
import io.meeds.wallet.model.WalletState;
import io.meeds.wallet.model.WalletType;

/**
 * A service to manage wallets of different types: user, space, admin
 */
public interface WalletAccountService {

  /**
   * Retrieves the list registered wallets
   *
   * @return list of associated wallets to users and spaces
   */
  Set<Wallet> listWallets();

  /**
   * Retrieves the list registered wallets identified by identities list
   *
   * @return list of associated wallets to users and spaces
   */
  Set<Wallet> listWalletsByIdentityIds(List<Long> identityIds);

  /**
   * Retrieve wallets count
   * 
   * @return associated wallets count
   */
  long getWalletsCount();

  /**
   * Retrieve wallet details by identity technical id
   * 
   * @param identityId User/Space identity technical id
   * @return {@link Wallet} wallet details identified by identity technical id
   */
  Wallet getWalletByIdentityId(long identityId);

  /**
   * Retrieve wallet details by identity type and remoteId accessed by a user
   * 
   * @param type 'user' or 'space'
   * @param remoteId username or space pretty name
   * @param currentUser current username saving wallet private key
   * @return {@link Wallet} wallet details identified by type and remote Id
   */
  Wallet getWalletByTypeAndId(String type, String remoteId, String currentUser);

  /**
   * Retrieve wallet details by identity type and remoteId
   * 
   * @param type 'user' or 'space'
   * @param remoteId username or space pretty name
   * @return {@link Wallet} wallet details identified by type and remote Id
   */
  Wallet getWalletByTypeAndId(String type, String remoteId);

  /**
   * Retrieves admin wallet from internal database
   * 
   * @return {@link Wallet} of administrator
   */
  Wallet getAdminWallet();

  /**
   * Save wallet private key for a wallet identified by identity type and
   * remoteId
   * 
   * @param type 'user' or 'space'
   * @param remoteId username or space pretty name
   * @param content crypted private key
   * @param currentUser current username
   * @throws IllegalAccessException when the current user is not allowed to save
   *           the encrypted private key of wallet
   */
  void savePrivateKeyByTypeAndId(String type,
                                 String remoteId,
                                 String content,
                                 String currentUser) throws IllegalAccessException;

  /**
   * Retrieve wallet private key by identity type and remoteId
   * 
   * @param type 'user' or 'space'
   * @param remoteId username or space pretty name
   * @param currentUser current username getting wallet private key
   * @return encrypted wallet private key identified by type and remote Id
   * @throws IllegalAccessException when the current user is not allowed to get
   *           the encrypted private key of wallet
   */
  String getPrivateKeyByTypeAndId(String type, String remoteId, String currentUser) throws IllegalAccessException;

  /**
   * Retrieve wallet private key by identity type and remoteId
   * 
   * @param type 'user' or 'space'
   * @param remoteId username or space pretty name
   * @return encrypted wallet private key identified by type and remote Id
   */
  String getPrivateKeyByTypeAndId(String type, String remoteId);

  /**
   * Removes wallet private key by identity type and remoteId
   * 
   * @param type 'user' or 'space'
   * @param remoteId username or space pretty name
   * @param currentUser current username removing wallet private key
   * @throws IllegalAccessException when the current user is not an owner of
   *           wallet
   */
  void removePrivateKeyByTypeAndId(String type, String remoteId, String currentUser) throws IllegalAccessException;

  /**
   * Retrieve wallet by address with blockchain state if current user can access
   * wallet data
   * 
   * @param address address of wallet to retrieve
   * @param currentUser current username accessing wallet information
   * @return {@link Wallet} wallet details identified by type and remote Id
   */
  Wallet getWalletByAddress(String address, String currentUser);

  /**
   * Retrieve wallet by address
   * 
   * @param address address of wallet to retrieve
   * @return {@link Wallet} wallet details identified by type and remote Id
   */
  Wallet getWalletByAddress(String address);

  /**
   * Save wallet state on blockchain
   * 
   * @param wallet
   * @param contractAddress
   */
  void saveWalletBlockchainState(Wallet wallet, String contractAddress);

  /**
   * Change wallet backup state
   * 
   * @param identityId user/space technical identty id
   * @param backupState true if backedUp else false
   * @param currentUser current username saving wallet backup state
   * @return modified {@link Wallet}
   * @throws IllegalAccessException when currentUser is not owner of wallet
   */
  Wallet saveWalletBackupState(String currentUser, long identityId, boolean backupState) throws IllegalAccessException;

  /**
   * Save wallet address to currentUser or to a space managed by current user
   * 
   * @param wallet {@link Wallet} wallet details to save
   * @param currentUser current username saving wallet details
   * @throws IllegalAccessException when the current user is not able to save a
   *           new address to the wallet
   */
  void saveWalletAddress(Wallet wallet, String currentUser) throws IllegalAccessException;

  /**
   * Save wallet instance in internal database
   * 
   * @param wallet
   * @param isNew
   * @return save {@link Wallet}
   */
  Wallet saveWallet(Wallet wallet, boolean isNew);

  /**
   * Creates a transient wallet instance with pre-filled default properties of a
   * wallet that will be ready to store.
   *
   * @param provider new {@link WalletProvider}
   * @param address new address of {@link Wallet} of selected identity
   * @param identityId user/space technical identty id
   * @return new {@link Wallet} instance
   */
  Wallet createWalletInstance(WalletProvider provider, String address, long identityId);

  /**
   * Switches the provider of selected identity and changes the address
   * 
   * @param identityId {@link Identity} unique identifier
   * @param provider new {@link WalletProvider}
   * @param newAddress new address of {@link Wallet} of selected identity
   * @param rawMessage Raw Message to sign by user to verify that it has the
   *          private key
   * @param signedMessage Signed message by user
   */
  void switchWalletProvider(long identityId, WalletProvider provider, String newAddress, String rawMessage, String signedMessage);

  /**
   * Switches the selected identity to internal wallet signer and changes the
   * address
   * 
   * @param identityId {@link Identity} unique identifier
   */
  void switchToInternalWallet(long identityId);

  /**
   * Remove User or Space wallet address association
   * 
   * @param address wallet address association to remove
   * @param currentUser current username removing wallet details
   * @throws IllegalAccessException if current user is not an administrator
   */
  void removeWalletByAddress(String address, String currentUser) throws IllegalAccessException;

  /**
   * Remove wallet address association by type and remote id
   * 
   * @param type USER/SPACE/ADMIN, see {@link WalletType}
   * @param remoteId username or space pretty name
   * @param currentUser current username saving wallet details
   * @throws IllegalAccessException
   */
  void removeWalletByTypeAndId(String type, String remoteId, String currentUser) throws IllegalAccessException;

  /**
   * Enable/Disable User or Space wallet
   * 
   * @param address address of wallet to enable/disable
   * @param enable whether enable or disable wallet
   * @param currentUser username of current user making the operation
   * @return true if modified else false
   * @throws IllegalAccessException if current user is not an administrator
   */
  boolean enableWalletByAddress(String address, boolean enable, String currentUser) throws IllegalAccessException;

  /**
   * @param wallet
   * @param currentUser
   * @return true if user is accessing his wallet or is accessing a space that
   *           he manages wallet
   */
  boolean isWalletOwner(Wallet wallet, String currentUser);

  /**
   * Saves label if label is not empty else, delete it
   * 
   * @param label label details object to process
   * @param currentUser current user making the label
   *          creation/modification/deletion
   * @return {@link WalletAddressLabel} saved or deleted label details
   */
  WalletAddressLabel saveOrDeleteAddressLabel(WalletAddressLabel label, String currentUser);

  /**
   * List of labels that current user can access
   * 
   * @param currentUser current username accessing the list of addresses labels
   * @return a {@link Set} of label details
   */
  Set<WalletAddressLabel> getAddressesLabelsVisibleBy(String currentUser);

  /**
   * Change wallet initialization status
   * 
   * @param address wallet address
   * @param initializationState wallet initialization status of type
   *          {@link WalletState}
   * @param currentUserId user changing wallet status
   * @throws IllegalAccessException if current user is not allowed to modify
   *           wallet initialization status
   */
  void setInitializationStatus(String address,
                               WalletState initializationState,
                               String currentUserId) throws IllegalAccessException;

  /**
   * Change wallet initialization status
   * 
   * @param address wallet address
   * @param initializationState wallet initialization status of type
   *          {@link WalletState}
   */
  void setInitializationStatus(String address, WalletState initializationState);

  /**
   * Creates admin account wallet in server side
   * 
   * @param privateKey admin account wallet private key
   * @param currentUser current user creating wallet
   * @throws IllegalAccessException if current user is not allowed to create
   *           admin wallet account
   */
  void createAdminAccount(String privateKey, String currentUser) throws IllegalAccessException;

  /**
   * @return admin account password from configuration
   */
  String getAdminAccountPassword();

  /**
   * Refreshes wallets from blockchain
   * 
   * @param walletsModifications modified wallets on blockchain with the set of
   *          invoked methods on contract
   */
  void refreshWalletsFromBlockchain(Map<String, Set<String>> walletsModifications);

  /**
   * Refreshes wallet state from blockchain
   * 
   * @param wallet
   * @param contractDetail
   * @param walletsModifications
   */
  void refreshWalletFromBlockchain(Wallet wallet, ContractDetail contractDetail, Map<String, Set<String>> walletsModifications);

  /**
   * Retrieve wallet state from internal database
   * 
   * @param wallet object to refresh
   */
  void retrieveWalletBlockchainState(Wallet wallet);

  /**
   * @return true if wallet admin is enabled on Token contract else return false
   */
  boolean isAdminAccountEnabled();

}
