package cz.moneta.test.dsl.udebs;

import cz.moneta.test.dsl.util.PollingUtils;
import cz.moneta.test.harness.context.StoreAccessor;
import cz.moneta.test.harness.endpoints.udebs.UdebsEndpoint;
import org.jooq.Record;
import org.jooq.Result;

import java.util.concurrent.TimeUnit;

public class TransactionsClient {
    private final StoreAccessor harness;

    public TransactionsClient(StoreAccessor harness) {
        this.harness = harness;
    }

    private static final String FIRST_TRANSACTION_IN_CLIENTTRANSACTIONPACKAGES = "SELECT * FROM CLIENTTRANSACTIONPACKAGES " +
            "WHERE CLIENTID='%s' " +
            "AND OPERID='%s' " +
            "AND CHANNELID='%s' " +
            "ORDER BY BORNDATE DESC, PACKAGEID DESC " +
            "FETCH FIRST ROW ONLY";

    private static final String FIRST_TRANSACTION_BETWEEN_TIME = "SELECT * FROM CLIENTTRANSACTIONPACKAGES " +
            "WHERE CLIENTID='%s' " +
            "AND OPERID='%s' " +
            "AND CHANNELID='%s' " +
            "AND BORNDATE BETWEEN TO_DATE('%s', 'dd.MM.rrrr hh24:mi:ss') AND TO_DATE('%s', 'dd.MM.rrrr hh24:mi:ss') " +
            "ORDER BY BORNDATE DESC, PACKAGEID DESC " +
            "FETCH FIRST ROW ONLY";

    private static final String TRANSACTIONS_FROM_TIME = "SELECT * FROM CLIENTTRANSACTIONPACKAGES " +
            "WHERE CLIENTID='%s' " +
            "AND OPERID='%s' " +
            "AND CHANNELID='%s' " +
            "AND BORNDATE >= TO_DATE('%s', 'dd.MM.rrrr hh24:mi:ss') " +
            "ORDER BY BORNDATE DESC, PACKAGEID DESC";

    private static final String NUMBER_OF_TRANSACTIONS_CREATED_DOCUMENTS = "SELECT * FROM CLIENTTRANSACTIONPACKAGES " +
            "WHERE CLIENTID='%s' " +
            "AND OPERID='%s' " +
            "AND CHANNELID='%s' " +
            "AND STATUSID='60' " +
            "AND BORNDATE >= TO_DATE('%s', 'dd.MM.rrrr hh24:mi:ss') " +
            "ORDER BY BORNDATE DESC, PACKAGEID DESC";

    private static final String ACCOUNT_BALANCE = "SELECT BALANCE FROM accountbalances where BALTYPEID=50 and ACCOUNTID=%s";

    private Result<Record> executeSql(String sqlQuery, String... args) {
        return harness.getEndpoint(UdebsEndpoint.class)
                .executeSql(String.format(sqlQuery, args));
    }

    private String delayToGetStatusId(String sqlQuery, long waitForResponseInSeconds, String... args) {
        try {
            TimeUnit.SECONDS.sleep(waitForResponseInSeconds);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
        return executeSql(sqlQuery, args)
                .getValues("STATUSID")
                .get(0)
                .toString();
    }

    private int getNumberOfDocumentsTransactions(String sqlQuery, long waitForResponseInSeconds, String clientId, String operId, String channelId, String fromDateTime) {
        try {
            TimeUnit.SECONDS.sleep(waitForResponseInSeconds);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
        return executeSql(sqlQuery, clientId, operId, channelId, fromDateTime)
                .size();
    }

    public String getLastTransactionStatus(String clientId, String operId, String channelId,
                                           long waitForResponseInSeconds, String expectedStatus) {
        return PollingUtils.waitForExpectedResultForSeconds(expectedStatus, waitForResponseInSeconds,
                () -> delayToGetStatusId(FIRST_TRANSACTION_IN_CLIENTTRANSACTIONPACKAGES, waitForResponseInSeconds,
                        clientId, operId, channelId));
    }

    //fromDateTime and toDateTime must have format - dd.MM.rrrr hh24:mi:ss
    public String getLastTransactionStatus(String clientId, String operId, String channelId, String fromDateTime,
                                           String toDateTime, long waitForResponseInSeconds, String expectedStatus) {
        return PollingUtils.waitForExpectedResultForSeconds(expectedStatus, waitForResponseInSeconds,
                () -> delayToGetStatusId(FIRST_TRANSACTION_BETWEEN_TIME, waitForResponseInSeconds,
                        clientId, operId, channelId, fromDateTime, toDateTime));
    }

    //fromDateTime must have format - dd.MM.rrrr hh24:mi:ss
    public String getLastTransactionStatus(String clientId, String operId, String channelId,
                                           long waitForResponseInSeconds, String fromDateTime, String expectedStatus) {
        return PollingUtils.waitForExpectedResultForSeconds(expectedStatus, waitForResponseInSeconds,
                () -> delayToGetStatusId(FIRST_TRANSACTION_IN_CLIENTTRANSACTIONPACKAGES, waitForResponseInSeconds,
                        clientId, operId, channelId, fromDateTime));
    }

    //fromDateTime must have format - dd.MM.rrrr hh24:mi:ss
    public int delayAndGetNumberOfTransactionsCreatedDocuments(String clientId, String operId, String channelId, String fromDateTime, long waitForResponseInSeconds) {
        return getNumberOfDocumentsTransactions(NUMBER_OF_TRANSACTIONS_CREATED_DOCUMENTS, waitForResponseInSeconds, clientId, operId, channelId, fromDateTime);
    }

    public Result<Record> getAccountBalance(String account) {
        String sqlQuery = String.format(ACCOUNT_BALANCE, account);
        return harness.getEndpoint(UdebsEndpoint.class).executeSql(sqlQuery);
    }

    public String getLastTransactionPackageId(String clientId, String operId, String channelId,
                                              long waitForResponseInSeconds) {
        return harness.getEndpoint(UdebsEndpoint.class)
                .executeSql(String.format(FIRST_TRANSACTION_IN_CLIENTTRANSACTIONPACKAGES,
                        clientId, operId, channelId, waitForResponseInSeconds))
                .getValues("PACKAGEID")
                .get(0)
                .toString();
    }
}