package bisq.core.grpc;

import bisq.core.btc.Balances;
import bisq.core.btc.wallet.WalletsManager;

import bisq.common.util.Tuple2;

import org.bitcoinj.crypto.KeyCrypterScrypt;

import javax.inject.Inject;

import org.spongycastle.crypto.params.KeyParameter;

import java.util.Timer;
import java.util.TimerTask;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;

import static bisq.core.grpc.ApiStatus.OK;
import static bisq.core.grpc.ApiStatus.WALLET_ALREADY_LOCKED;
import static java.util.concurrent.TimeUnit.SECONDS;

@Slf4j
class CoreWalletService {

    private final Balances balances;
    private final WalletsManager walletsManager;

    @Nullable
    private String tempLockWalletPassword;

    @Inject
    public CoreWalletService(Balances balances, WalletsManager walletsManager) {
        this.balances = balances;
        this.walletsManager = walletsManager;
    }

    public long getAvailableBalance() {
        if (!walletsManager.areWalletsAvailable())
            throw new IllegalStateException("wallet is not yet available");

        if (walletsManager.areWalletsEncrypted())
            throw new IllegalStateException("wallet is locked");

        var balance = balances.getAvailableBalance().get();
        if (balance == null)
            throw new IllegalStateException("balance is not yet available");

        return balance.getValue();
    }

    public void setWalletPassword(String password, String newPassword) {
        try {
            if (!walletsManager.areWalletsAvailable())
                throw new IllegalStateException("wallet is not yet available");

            KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt();
            if (keyCrypterScrypt == null)
                throw new IllegalStateException("wallet encrypter is not available");

            if (newPassword != null && !newPassword.isEmpty()) {
                // TODO Validate new password before replacing old password.
                if (!walletsManager.areWalletsEncrypted())
                    throw new IllegalStateException("wallet is not encrypted with a password");

                KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
                if (!walletsManager.checkAESKey(aesKey))
                    throw new IllegalStateException("incorrect old password");

                walletsManager.decryptWallets(aesKey);
                aesKey = keyCrypterScrypt.deriveKey(newPassword);
                walletsManager.encryptWallets(keyCrypterScrypt, aesKey);
            }

            if (walletsManager.areWalletsEncrypted())
                throw new IllegalStateException("wallet is encrypted with a password");

            // TODO Validate new password.
            KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
            walletsManager.encryptWallets(keyCrypterScrypt, aesKey);
        } catch (Throwable t) {
            t.printStackTrace();
            throw new IllegalStateException(t);
        }
    }

    public Tuple2<Boolean, ApiStatus> lockWallet() {
        if (tempLockWalletPassword != null) {
            setWalletPassword(tempLockWalletPassword, null);
            tempLockWalletPassword = null;
            return new Tuple2<>(true, OK);
        }
        return new Tuple2<>(false, WALLET_ALREADY_LOCKED);
    }

    public Tuple2<Boolean, ApiStatus> unlockWallet(String password, long timeout) {
        removeWalletPassword(password);

        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                log.info("Locking wallet");
                setWalletPassword(password, null);
                tempLockWalletPassword = null;
            }
        };
        Timer timer = new Timer("Lock Wallet Timer");
        timer.schedule(timerTask, SECONDS.toMillis(timeout));

        // Cache wallet password for timeout (secs), in case
        // user wants to lock the wallet for timeout expires.
        tempLockWalletPassword = password;
        return new Tuple2<>(true, OK);
    }

    // Provided for automated wallet protection method testing, despite the
    // security risks exposed by providing users the ability to decrypt their wallets.
    public void removeWalletPassword(String password) {
        if (!walletsManager.areWalletsAvailable())
            throw new IllegalStateException("wallet is not yet available");

        if (!walletsManager.areWalletsEncrypted())
            throw new IllegalStateException("wallet is not encrypted with a password");

        KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt();
        if (keyCrypterScrypt == null)
            throw new IllegalStateException("wallet encrypter is not available");

        KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
        if (!walletsManager.checkAESKey(aesKey))
            throw new IllegalStateException("incorrect password");

        walletsManager.decryptWallets(aesKey);
    }
}

@@ -1466,26 +1466,8 @@
 ) %7B%0A
-        try %7B%0A    
     
@@ -1517,36 +1517,32 @@
 ())%0A            
-   
- 
 throw new Illega
@@ -1582,36 +1582,32 @@
 t available%22);%0A%0A
-    
         KeyCrypt
@@ -1676,28 +1676,24 @@
 ();%0A        
-  
-  
 if (keyCrypt
@@ -1702,36 +1702,32 @@
 Scrypt == null)%0A
-    
             thro
@@ -1797,28 +1797,24 @@
 );%0A%0A        
- 
-   
 if (newPassw
@@ -1850,28 +1850,24 @@
 sEmpty()) %7B%0A
-    
             
@@ -1931,36 +1931,32 @@
 rd.%0A            
-    
 if (!walletsMana
@@ -1990,36 +1990,32 @@
                 
-   
- 
 throw new Illega
@@ -2079,36 +2079,32 @@
 );%0A%0A            
-    
 KeyParameter aes
@@ -2151,36 +2151,32 @@
 d);%0A            
-  
-  
 if (!walletsMana
@@ -2192,36 +2192,32 @@
 AESKey(aesKey))%0A
-    
                 
@@ -2280,36 +2280,32 @@
 );%0A%0A            
- 
-   
 walletsManager.d
@@ -2319,36 +2319,32 @@
 allets(aesKey);%0A
-    
             aesK
@@ -2393,36 +2393,32 @@
 d);%0A            
-    
 walletsManager.e
@@ -2466,27 +2466,19 @@
         
-   
- 
 %7D%0A%0A
-    
         
@@ -2523,36 +2523,32 @@
 ())%0A            
-  
-  
 throw new Illega
@@ -2604,36 +2604,32 @@
 ord%22);%0A%0A        
-    
 // TODO Validate
@@ -2643,36 +2643,32 @@
 ssword.%0A        
- 
-   
 KeyParameter aes
@@ -2703,36 +2703,32 @@
 eKey(password);%0A
-    
         walletsM
@@ -2780,131 +2780,8 @@
 y);%0A
-        %7D catch (Throwable t) %7B%0A            t.printStackTrace();%0A            throw new IllegalStateException(t);%0A        %7D%0A
     

Remove obsolete try/catch block in #setWalletPassword