package cz.moneta.test.regression.transactional.payments.cashless.cache24;

import com.google.gson.JsonObject;
import cz.moneta.test.dsl.Harness;
import cz.moneta.test.dsl.newib.mainpage.AccountsPage;
import cz.moneta.test.dsl.newib.mainpage.DashboardPage;
import cz.moneta.test.dsl.util.TestUtils;
import cz.moneta.test.harness.annotations.Environment;
import cz.moneta.test.harness.annotations.JiraTestCase;
import cz.moneta.test.harness.annotations.TestCase;
import cz.moneta.test.harness.annotations.TestScenarioWithOrder;
import cz.moneta.test.harness.support.auth.Credentials;
import cz.moneta.test.harness.support.auth.Key;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

import static cz.moneta.test.dsl.ib.account.current.CurrentAccountPage.AVAILABLE_BALANCE_VALUE_KEY;
import static cz.moneta.test.dsl.payment_engine.PaymentEngineDetailAccountPage.DISPO_BALANCE_VALUE_KEY;

@TestScenarioWithOrder(name = "Test accounting for instant payments in IB - ICBS - PaymentEnginee online")
public class AvailableBalances {

    private double dispositionBalanceApi;
    private double dispositionBalance;
    private double payment;
    private double expectedAvailableBalance;
    private String accountNumberClient;
    private String creditorAccountNumber;

    private double parseValueFromStoreElementContentIb(Harness harness, String keyStoreElementContentIb) {
        String parsedAvailableBalance = harness.get(keyStoreElementContentIb)
                .toString()
                .replaceAll("\\s", "")
                .replaceAll("CZK", "")
                .replace(",", ".");
        return Double.valueOf(parsedAvailableBalance);
    }

    private double parseValueGlobalStorePaymentEngine(Harness harness, String keyGlobalStorePaymentEngine) {
        String parsedAvailableBalance = harness.get(keyGlobalStorePaymentEngine)
                .toString()
                .replaceAll("\\.", "")
                .replace(",", ".");
        return Double.valueOf(parsedAvailableBalance);
    }

    private double calculateExpectedAvailableBalance(double dispositionBalance, double payment) {
        double expectedBalanceValue = dispositionBalance - payment;
        //Double.toString() The best way to create a BigDecimal object with an initial decimal value is via a string because solve problem to precision decimal numbers
        BigDecimal decimal = new BigDecimal(Double.toString(expectedBalanceValue));
        return decimal.setScale(2, RoundingMode.HALF_UP).doubleValue();
    }

    //Get available balance with WSO2 API - frontend-account-controller
    @BeforeAll
    void getAvailableBalanceWso2Api(Harness harness) {
        String tokenCbl = harness.tasks().wso2().authorization().getToken("DigitalchannelsSB_CBL");
        accountNumberClient = harness.getConfig("payments_cashless.availableBalances.accounts").split(",")[0];
        creditorAccountNumber = harness.getConfig("payments_cashless.availableBalances.accounts").split(",")[1];

        dispositionBalanceApi = harness.withWso2()
                .prepareRequest()
                .withPath("/t/payments.cluster/frontend-account-controller/v1/frontend/account/balance/" + accountNumberClient)
                .withHeader("Authorization", tokenCbl)
                .get()
                .andAssertStatus(200)
                .extract("dispositionBalance")
                .get("amount")
                .doubleValue();

        harness.log("Disposition balance value from WSO2 API is " + dispositionBalanceApi);

        double generatedNumber = ThreadLocalRandom.current().nextDouble(1.00, 5.00);
        BigDecimal decimal = new BigDecimal(Double.toString(generatedNumber));
        payment = decimal.setScale(2, RoundingMode.HALF_UP).doubleValue();
        harness.log("Generated payment is " + payment);
    }

    @Disabled("Not currently maintained")
    @JiraTestCase(id = "PAY-T1657")
    @Order(1)
    @TestCase(name = "Check disposition balance in IB", environments = {Environment.TST1, Environment.PPE})
    void checkIbAvailable(Harness harness) {
        String clientBirthNum = harness.getConfig("payments_cashless.availableBalances.rc").split(",")[0];

        Credentials ibCredentials = harness.withUdebs()
                .ib()
                .usernameAndPassword()
                .getUsernameAndPasswordByRc(clientBirthNum);

        harness.withNewIb()
                .openLoginPage()
                .closeCookiesPanelIfVisible()
                .clickLoginWithIdAndPassword()
                .typeUsername(ibCredentials.getUsername())
                .clickContinue()
                .typePassword(ibCredentials.getPassword())
                .clickToLogin()
                .onAuthMobileKeyPage()
                .typeSmsKey("12341234")
                .clickSend()
                .onDashboardPage()
                .getAvailableBalance();

        dispositionBalance = parseValueFromStoreElementContentIb(harness, AVAILABLE_BALANCE_VALUE_KEY);
        harness.log("Disposition balance after logging to IB is " + dispositionBalance);
        Assertions.assertEquals(dispositionBalanceApi, dispositionBalance, 0, "Disposition balance is not same in API and IB");
    }

    @Disabled("Not currently maintained")
    @JiraTestCase(id = "PAY-T1663")
    @Order(2)
    @TestCase(name = "Check new disposition balance (after payment) in IB", environments = {Environment.TST1, Environment.PPE})
    void checkIbAvailableBalanceAfterPayment(Harness harness) throws InterruptedException {
        sendInstantPayment(harness);
        expectedAvailableBalance = calculateExpectedAvailableBalance(dispositionBalanceApi, payment);
        harness.log("Expected disposition balance is " + expectedAvailableBalance);
        String clientBirthNum = harness.getConfig("payments_cashless.availableBalances.rc").split(",")[0];
        String transactionIb = "-" + String.format("%.2f", payment).replace(".", ",");
        transactionIb = transactionIb.length() == 4 ? transactionIb + "0" : transactionIb;

        Credentials ibCredentials = harness.withUdebs()
                .ib()
                .usernameAndPassword()
                .getUsernameAndPasswordByRc(clientBirthNum);

        DashboardPage dashboardPage = harness.withNewIb()
                .openLoginPage()
                .then(harness.tasks().newIb().loginWithUsernamePasswordAndDefaultMobKey(ibCredentials.getUsername(), ibCredentials.getPassword()))
                .getAvailableBalance();

        double newDispositionBalanceIb = parseValueFromStoreElementContentIb(harness, AVAILABLE_BALANCE_VALUE_KEY);
        harness.log("New IB disposition balance after payment is " + newDispositionBalanceIb);
        Assertions.assertEquals(expectedAvailableBalance, newDispositionBalanceIb, 0, "The disposition balance does not equal the expected one after the payment in IB");


        AccountsPage accountsTabs = dashboardPage.goToAccounts()
                .getAvailableBalance();

        newDispositionBalanceIb = parseValueFromStoreElementContentIb(harness, AVAILABLE_BALANCE_VALUE_KEY);
        harness.log("New IB disposition balance after payment is " + newDispositionBalanceIb);
        Assertions.assertEquals(expectedAvailableBalance, newDispositionBalanceIb, 0, "The disposition balance does not equal the expected one after the payment in IB");

        accountsTabs
                .clickAdvancedSearch()
                .fillDateFrom(TestUtils.getTodayDate())
                .clickFilterTransactions()
                .checkPaymentValue(transactionIb);
    }

    @Disabled("Not currently maintained")
    @JiraTestCase(id = "PAY-T1660")
    @Order(3)
    @TestCase(name = "Check new disposition balance in PaE", environments = {Environment.TST1, Environment.PPE})
    void checkDispositionInPymentEngineAfterPayment(Harness harness, @Key("payment.engine") Credentials credentials) {
        harness.withPaymentEngine()
                .openLoginPage()
                .login(credentials.getUsername(), credentials.getPassword())
                .clickOkButton()
                .clickOnCache24_7()
                .clickOnAccountAndReservation()
                .typeInternalAccountNumberAndClickApply(accountNumberClient)
                .clickOnAccountDetail()
                .getDispoBalance();

        double newDispoBalancePaymentEngine = parseValueGlobalStorePaymentEngine(harness, DISPO_BALANCE_VALUE_KEY);
        harness.log("New dispo balance in PaymentEngine " + newDispoBalancePaymentEngine);
        Assertions.assertEquals(expectedAvailableBalance, newDispoBalancePaymentEngine, 0, "The disposition balance does not equal the expected one after the payment in UFO");
    }


    private String createRequestForTransaction(String debtorAccountId, String creditorAccountId, Double amount) {
        JsonObject json = new JsonObject();
        Random ran = new Random();
        json.addProperty("instructionId", ran.nextInt());
        json.addProperty("debetorAccountId", debtorAccountId);
        json.addProperty("creditorAccountId", creditorAccountId);
        json.addProperty("creditorBankNumber", "0600");
        json.addProperty("amount", amount);
        json.addProperty("constantSymbol", "12");
        json.addProperty("specificSymbol", "34");
        json.addProperty("variableSymbol", "56");
        json.addProperty("AVMessage", "Automated test");
        json.addProperty("channelId", "IB");
        json.addProperty("userId", "GEMTEST");
        return json.toString();
    }

    void sendInstantPayment(Harness harness) throws InterruptedException {
        String TOKEN = harness.tasks().wso2().authorization().getToken("ATM");
        String request = createRequestForTransaction(accountNumberClient, creditorAccountNumber, payment);
        harness.log("Instant payment amount: " + payment);

        harness.withWso2()
                .prepareRequest()
                .withPath("/t/payments.cluster/CRT_INST_PAYM/001")
                .withHeader("Content-Type", "application/json")
                .withHeader("Accept", "application/json")
                .withHeader("Authorization", TOKEN)
                .withHeader("cache-control", "no-cache,no-cache")
                .withPayload(request)
                .post()
                .andAssertStatus(201)
                .andAssertFieldValue("rejectionReason", "");

        TimeUnit.SECONDS.sleep(5L);
    }
}