package com.example.paymentsheet;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.samsung.android.sdk.blockchain.CoinType;
import com.samsung.android.sdk.blockchain.ListenableFutureTask;
import com.samsung.android.sdk.blockchain.SBlockchain;
import com.samsung.android.sdk.blockchain.account.Account;
import com.samsung.android.sdk.blockchain.account.AccountManager;
import com.samsung.android.sdk.blockchain.account.ethereum.EthereumAccount;
import com.samsung.android.sdk.blockchain.coinservice.CoinNetworkInfo;
import com.samsung.android.sdk.blockchain.coinservice.CoinService;
import com.samsung.android.sdk.blockchain.coinservice.CoinServiceFactory;
import com.samsung.android.sdk.blockchain.coinservice.ethereum.EthereumService;
import com.samsung.android.sdk.blockchain.coinservice.ethereum.EthereumTransactionType;
import com.samsung.android.sdk.blockchain.coinservice.ethereum.EthereumUtils;
import com.samsung.android.sdk.blockchain.exception.SsdkUnsupportedException;
import com.samsung.android.sdk.blockchain.network.EthereumNetworkType;
import com.samsung.android.sdk.blockchain.network.NetworkType;
import com.samsung.android.sdk.blockchain.wallet.HardwareWallet;
import com.samsung.android.sdk.blockchain.wallet.HardwareWalletManager;
import com.samsung.android.sdk.blockchain.wallet.HardwareWalletType;

import org.jetbrains.annotations.NotNull;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.ExecutionException;

public class MainActivity extends AppCompatActivity {
    private SBlockchain mSblockchain;
    private HardwareWallet mHardwareWallet;
    private CoinNetworkInfo mCoinNetworkInfo;
    private CoinService mCoinService;
    private EthereumService ethereumService;
    private BigDecimal convertedAmount;
    private List<Account> accountList;
    private Account mFirstAccount;
    private static final String LOG_TAG = "TestApp";

    private Button connectButton;
    private Button createAccountButton;
    private Button balanceButton;
    private Button buyButton;

    private ProgressBar mProgressBar;

    public MainActivity() {
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        connectButton = findViewById(R.id.connect_button);
        createAccountButton = findViewById(R.id.create_account);
        balanceButton = findViewById(R.id.balance_button);
        buyButton = findViewById(R.id.buy_button);
        mProgressBar = findViewById(R.id.progressBar);

        mProgressBar.setVisibility(View.INVISIBLE);
    }

    //..................................//
    //......... Object Creation ........//
    //..................................//
    public void OnClickCreateObject(View view) {

        try {
            mSblockchain = new SBlockchain();
            mSblockchain.initialize(getApplicationContext());
            connectButton.setEnabled(true);
            Log.i(LOG_TAG, "Object Created.");
            Toast.makeText(getApplicationContext(), "Object has created.", Toast.LENGTH_SHORT).show();
        } catch (SsdkUnsupportedException e) {
            switch (e.getErrorType()) {
                case SsdkUnsupportedException.DEVICE_NOT_SUPPORTED:
                    Log.e(LOG_TAG, "Device is not Supported");
                    break;
                case SsdkUnsupportedException.LIBRARY_NOT_INSTALLED:
                    Log.e(LOG_TAG, "Platform SDK Library is not Installed");
                    break;
                case SsdkUnsupportedException.LIBRARY_UPDATE_IS_REQUIRED:
                    Log.e(LOG_TAG, "Platform SDK Library Update is Needed");
                    break;
                case SsdkUnsupportedException.LIBRARY_UPDATE_IS_RECOMMENDED:
                    Log.e(LOG_TAG, "Platform SDK Library is Recommended");
                    break;
                case SsdkUnsupportedException.VENDOR_NOT_SUPPORTED:
                    Log.e(LOG_TAG, "Platform SDK is not supported on Non-Samsung Devices");
                    break;
                default:
                    Log.e(LOG_TAG, "Unknown Platform SDK Unsupported Error");
            }
        }
    }

    //..................................//
    //... Connect to Hardware Wallet ...//
    //..................................//

    public void OnClickConnectToHardwareWallet(View view) {

        // Connect to Hardware Wallet
        HardwareWalletType mHardwareWalletType = HardwareWalletType.SAMSUNG;
        HardwareWalletManager mHardwareWalletManager = mSblockchain.getHardwareWalletManager();

        mHardwareWalletManager.connect(mHardwareWalletType, true)
                .setCallback(new ListenableFutureTask.Callback<HardwareWallet>() {
            @Override
            public void onSuccess(HardwareWallet hardwareWallet) {
                mHardwareWallet = hardwareWallet;
                Log.i(LOG_TAG, "Wallet is connected successfully.");
                Log.i(LOG_TAG,
                        "Object Created." + mSblockchain.getHardwareWalletManager().getConnectedHardwareWallet().getWalletId());
                runOnUiThread(() -> {
                            createAccountButton.setEnabled(true);
                            Toast.makeText(getApplicationContext(),
                                    "Wallet ID: " + hardwareWallet.getWalletId(), Toast.LENGTH_SHORT).show();
                        }
                );
            }

            @Override
            public void onFailure(@NotNull ExecutionException e) {
                Log.e(LOG_TAG, "Wallet connection failed.");
            }

            @Override
            public void onCancelled(@NotNull InterruptedException e) {
                Log.e(LOG_TAG, "Wallet is connection cancelled.");
            }
        });
    }

    //..................................//
    //............. Account ............//
    //..................................//

    public void OnClickCreateAccount(View view) {
        AccountManager mAccountManager = mSblockchain.getAccountManager();
        CoinType coinType = CoinType.ETH;
        NetworkType networkType = EthereumNetworkType.GOERLI;
        String rpc = "https://goerli.infura.io/v3/71cf9f88c5b54962a394d80890e41d5b";

        mCoinNetworkInfo = new CoinNetworkInfo(coinType, networkType, rpc);
        mProgressBar.setVisibility(View.VISIBLE);

        //Check empty account list.
        getAccount();

        if (accountList.isEmpty()) {

            // Restore Account
            // Restore first
            // If keystore has already an unused account, generating new account will be failed.
            // As new account can not be generated unless all accounts are used at least once.
            mAccountManager.restoreAccounts(
                    mHardwareWallet,
                    true,
                    mCoinNetworkInfo, "m/44'/60'/0'/0"
            ).setCallback(new ListenableFutureTask.Callback<Boolean>() {
                @Override
                public void onSuccess(Boolean aBoolean) {
                    if (accountList.isEmpty()) {
                        // After restoring if no account found.
                        // Then generate a new account.
                        generateAccount(mAccountManager);
                    } else{
                        Log.i(LOG_TAG, "Account is restored.");
                        runOnUiThread(() ->
                                getAccount()
                        );
                    }
                }

                @Override
                public void onFailure(@NotNull ExecutionException e) {
                    mProgressBar.setVisibility(View.INVISIBLE);
                    Log.e(LOG_TAG, "Restore account is failed.");
                    Log.e(LOG_TAG, "" + e.getMessage());
                }

                @Override
                public void onCancelled(@NotNull InterruptedException e) {
                    mProgressBar.setVisibility(View.INVISIBLE);
                    Log.e(LOG_TAG, "Restore account is cancelled.");
                }
            });
        }
    }

    protected void generateAccount(AccountManager mAccountManager) {
        // Create New Account
        mAccountManager.generateNewAccount(mHardwareWallet, mCoinNetworkInfo)
                .setCallback(new ListenableFutureTask.Callback<Account>() {
            @Override
            public void onSuccess(Account account) {
                mProgressBar.setVisibility(View.INVISIBLE);
                Log.i(LOG_TAG, "Account is created.");
                Log.i(LOG_TAG, "Account: " + account.getAddress());
                runOnUiThread(() ->
                        getAccount()
                );
            }

            @Override
            public void onFailure(@NotNull ExecutionException e) {
                mProgressBar.setVisibility(View.INVISIBLE);
                Log.e(LOG_TAG, "Account creation is failed.");
                Log.e(LOG_TAG, "" + e.getMessage());
            }

            @Override
            public void onCancelled(@NotNull InterruptedException e) {
                mProgressBar.setVisibility(View.INVISIBLE);
                Log.e(LOG_TAG, "Account creation is cancelled.");
            }
        });
    }

    private void getAccount() {
        accountList = mSblockchain.getAccountManager()
                .getAccounts(mHardwareWallet.getWalletId(), mCoinNetworkInfo.getCoinType(), mCoinNetworkInfo.getNetworkType());

        if (accountList.size() != 0) {
            mProgressBar.setVisibility(View.INVISIBLE);
            mFirstAccount = accountList.get(0);
            TextView accountTextView = findViewById(R.id.show_account);
            accountTextView.setText(mFirstAccount.getAddress());
            balanceButton.setEnabled(true);
        }
    }

    //..................................//
    //.......... Fetch Balance .........//
    //..................................//

    public void onClickGetBalance(View view) {

        mCoinService = CoinServiceFactory.getCoinService(getApplicationContext(), mCoinNetworkInfo);
        ethereumService = (EthereumService) mCoinService;
        TextView etherAmountTextView = findViewById(R.id.amount_ether);
        mProgressBar.setVisibility(View.VISIBLE);

        mCoinService.getBalance(mFirstAccount).setCallback(new ListenableFutureTask.Callback<BigInteger>() {
            @Override
            public void onSuccess(BigInteger bigInteger) {
                mProgressBar.setVisibility(View.INVISIBLE);
                convertedAmount = EthereumUtils.convertWeiToEth(bigInteger);
                Log.i(LOG_TAG, "Balance is fetched successfully.");
                Log.i(LOG_TAG, "Balance is:" + convertedAmount.toString());
                runOnUiThread(() -> {
                            buyButton.setEnabled(true);
                            etherAmountTextView.setText(convertedAmount.toString());
                        }
                );
            }

            @Override
            public void onFailure(@NotNull ExecutionException e) {
                mProgressBar.setVisibility(View.INVISIBLE);
                runOnUiThread(() ->
                        Toast.makeText(getApplicationContext(), "Fetch Balance Failed: " +
                                e.getMessage(), Toast.LENGTH_SHORT).show()
                );
                Log.e(LOG_TAG, "Fetching balance is failed.");
                Log.e(LOG_TAG, "" + e.getMessage());
            }

            @Override
            public void onCancelled(@NotNull InterruptedException e) {
                mProgressBar.setVisibility(View.INVISIBLE);
                Log.e(LOG_TAG, "Fetching balance is cancelled.");
            }
        });
    }

    //..................................//
    //.......... Buy Product ...........//
    //..................................//

    public void onClickBuy(View view) {

        String toAddress = "0xb0BDFC76C24fC9F14a8Dea028B5f6BBA80F10972";
        BigDecimal sendAmount = new BigDecimal("0.005");
        BigInteger convertedSendAmount = EthereumUtils.convertEthToWei(sendAmount);

        try {
            Intent intent = ethereumService.createEthereumPaymentSheetActivityIntent(
                    this,
                    mHardwareWallet,
                    EthereumTransactionType.EIP1559,
                    (EthereumAccount) mFirstAccount,
                    toAddress,
                    convertedSendAmount,
                    null,
                    null
            );
            activityResultLauncher.launch(intent);
        } catch (Exception e) {
            Log.e(LOG_TAG, "Error in sending: " + e);
        }
    }

    ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                switch (result.getResultCode()) {
                    case Activity.RESULT_OK:
                        Log.d(LOG_TAG, "Transaction Hash: " + result.getData());
                        runOnUiThread(() -> {
                            Toast.makeText(getApplicationContext(), "Transaction Hash: " +
                                    result.getData().getStringExtra("txid"), Toast.LENGTH_SHORT).show();
                        });
                        break;
                    case Activity.RESULT_CANCELED:
                        Log.e(LOG_TAG, "Transaction Failed: " + result.getData().getStringExtra("error"));
                        runOnUiThread(() ->
                                Toast.makeText(getApplicationContext(), "Transaction Failed: " +
                                        result.getData().getStringExtra("error"), Toast.LENGTH_SHORT).show()
                        );
                        break;
                }
            }
        }
    );

}