package cz.moneta.test.dsl.hypos.contractPrepare.process;

import cz.moneta.test.dsl.Harness;
import cz.moneta.test.dsl.hypos.enums.*;
import cz.moneta.test.dsl.hypos.pages.contract.ContractHistoryPage;
import cz.moneta.test.dsl.hypos.pages.hypokalkulacka.HypokalkulackaPage;
import cz.moneta.test.dsl.hypos.tasks.HyposDbTasks;
import cz.moneta.test.dsl.hypos.tasks.HyposTasks;
import cz.moneta.test.dsl.util.DateUtils;
import cz.moneta.test.dsl.util.data.hypos.HyposContract;
import cz.moneta.test.harness.endpoints.hypos.HyposEndpoint;
import cz.moneta.test.harness.support.auth.Credentials;
import cz.moneta.test.harness.endpoints.testautomationdb.TestAutomationDbEndpoint;
import org.junit.jupiter.api.Assertions;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import static cz.moneta.test.dsl.hypos.enums.Client.FIRST_CLIENT;
import static cz.moneta.test.dsl.hypos.enums.HyposLogType.PROCESS;
import static cz.moneta.test.dsl.hypos.pages.hypokalkulacka.HypokalkulackaPage.HYPOKALKULACKA_ID;

public class ContractProcessPrepare {

    private final Harness harness;
    private final HyposTasks tasks;
    private static final String CONTRACT_STATE_SIGNED = "Úvěr podepsán";

    public ContractProcessPrepare(Harness harness) {
        this.harness = harness;
        this.tasks = harness.tasks().hypos();
    }

    /**
     * Get hypos profile for fill client income in hypokalkulacka based on loan action.
     * @param loanAction - mortgages loan action
     * @return HyposProfile
     */
    private HyposProfile getProfileForIncome(String loanAction) {
        if (HyposAction.isActionEmployee(loanAction)) {
            return HyposProfile.PAYROLL_ACCOUNTANT;
        } else {
            return HyposProfile.EMERGENCY;
        }
    }

    /**
     * Creating new hypokalkulacka for Hypos tests.
     * @param hyposContract - mortgages data
     */
    public void createNewHypokalkulacka(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        HypokalkulackaPage hypokalkulackaPage = harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .switchToHypokalkulacka()
                .then(tasks.hypokalkulackaTasks().addClientData(hyposContract, FIRST_CLIENT.getValue()))
                .then(tasks.hypokalkulackaTasks().addClientAddress(hyposContract, FIRST_CLIENT.getValue()))
                .then(tasks.hypokalkulackaTasks().addClientIncomeEmployee(hyposContract, FIRST_CLIENT.getValue()))
                .then(tasks.hypokalkulackaTasks().addClientIncomeBusiness(hyposContract, FIRST_CLIENT.getValue()))
                .then(tasks.hypokalkulackaTasks().addClientIncomeOther(hyposContract, FIRST_CLIENT.getValue()))
                .then(tasks.hypokalkulackaTasks().addClientExpenditure(hyposContract, FIRST_CLIENT.getValue()));

        // Set other client data
        for (int client = 1; client < hyposContract.getClients().size(); client++) {
            hypokalkulackaPage.then(tasks.hypokalkulackaTasks().addOtherClientData(hyposContract, client))
                .then(tasks.hypokalkulackaTasks().addClientAddress(hyposContract, client))
                .then(tasks.hypokalkulackaTasks().addClientIncomeEmployee(hyposContract, client))
                .then(tasks.hypokalkulackaTasks().addClientIncomeBusiness(hyposContract, client))
                .then(tasks.hypokalkulackaTasks().addClientIncomeOther(hyposContract, client))
                .then(tasks.hypokalkulackaTasks().addClientExpenditure(hyposContract, client));
        }

        hypokalkulackaPage.then(tasks.hypokalkulackaTasks().addHouseholds(hyposContract))
                .then(tasks.hypokalkulackaTasks().addRealtyData(hyposContract))
                .then(tasks.hypokalkulackaTasks().addLoanData(hyposContract));

        hyposContract.getLoanPurposes().stream()
                .filter(purpose -> purpose.getType().equals(LoanPurposeType.REFINANCING.getValue()))
                .findAny()
                .ifPresent(purpose -> hypokalkulackaPage.fillFirstExpectedDrawDate(LocalDate.now().plusMonths(15).format(DateTimeFormatter.ofPattern("d.M.yyyy"))));

        hypokalkulackaPage.then(tasks.hypokalkulackaTasks().addApplicationData(hyposContract))
                .clickSaveHypokalkulackaButtonAndCheckAlertText()
                .then(tasks.hypokalkulackaTasks().generateAndSignAgreements101())
                .then(tasks.hypokalkulackaTasks().generateAndSignMortgageApplication())
                .getHypokalkulackaVersion(harness);

        String hypokalkulackaId = harness.get(HYPOKALKULACKA_ID).toString();
        hypokalkulackaPage.then(tasks.hypokalkulackaTasks().saveHypokalkulacka());
        Assertions.assertNotNull(hypokalkulackaId, "Hypokalkulačka ID is empty. Stopping test.");
        String contractId = new HyposDbTasks(harness).getContractId(hypokalkulackaId);
        Assertions.assertNotNull(contractId, "Contract ID is empty. Stopping test.");
        hyposContract.setContractId(contractId);
    }

    /**
     * Filling in mandatory data and start prescoring.
     * @param hyposContract - mortgages data
     */
    public void fillDataAndStartPrescoring(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .switchToHypokom()
                .then(tasks.hypokomTasks().searchClientByContractId(hyposContract.getContractId()))
                .then(tasks.hypokomTasks().setClientAppointmentDate())
                .then(tasks.hypokomTasks().createNewContract(hyposContract.getContractId(), hyposContract));

        Assertions.assertEquals(true, new HyposDbTasks(harness).contractPrescoringFinished(hyposContract.getContractId()), "Prescoring was not finished in 180 seconds interval.");
        harness.withHypos().closeHypos();
    }

    /**
     * Filling in mandatory detail income data in hypokalkulacka.
     * @param hyposContract - mortgages data
     */
    public void fillDetailIncomeInHypokalkulacka(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        HypokalkulackaPage hypokalkulackaPage = harness.withHypos().openLoginPage()
                .then(tasks.loginToHyposWithProfile(getProfileForIncome(hyposContract.getLoanAction())))
                .then(tasks.searchAndOpenContract(hyposContract.getContractId()))
                .clickHypokalkulacka();

        for (int client = 0; client < hyposContract.getClients().size(); client++) {
            if (!hyposContract.getClients().get(client).getEmployeeIncomes().isEmpty())
                for (int income = 0; income < hyposContract.getClients().get(client).getEmployeeIncomes().size(); income++) {
                    hypokalkulackaPage.then(tasks.hypokalkulackaTasks().fillClientDetailIncomeEmployee(hyposContract, client, income));
                }
            hypokalkulackaPage.then(tasks.hypokalkulackaTasks().fillClientDetailIncomeBusiness(hyposContract, client));
        }
        hypokalkulackaPage.clickSaveNewHypokalkulackaVersionAndAcceptAlertText()
                .closeHypokalkulackaAndContinueWithContract(hyposContract);
    }

    /**
     * Filling in mandatory data and start scoring.
     * @param hyposContract - mortgages data
     */
    public void fillDataAndStartScoringManual(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        ContractHistoryPage contractHistoryPage = harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Obchod založen - kontrola OK"))
                .then(tasks.contractTasks().setNoCifForClient(hyposContract))
                .then(tasks.contractTasks().setLoanAndDrawingConditions(hyposContract))
                .then(tasks.contractTasks().setNewRealtyAndSupervision(hyposContract))
                .then(tasks.contractTasks().importChecklistUwDocument());

        hyposContract.getClients().stream()
                .filter(cardif -> !cardif.getCardifType().equals(CardifType.WITHOUT_CARDIF.getValue()))
                .findAny()
                .ifPresent(cardif -> contractHistoryPage.then(tasks.contractTasks().setLifeInsurance()));

        this.fillDetailIncomeInHypokalkulacka(hyposContract);

        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContract(hyposContract.getContractId()))
                .then(tasks.contractTasks().setScoringManual());

        Assertions.assertEquals(true, new HyposDbTasks(harness).contractScoringFinished(hyposContract.getContractId()), "Contract checking was not finished in 180 seconds interval.");
    }

    /**
     * Filling in mandatory life insirance data in contract detail
     * @param hyposContract - mortgages data
     */
    public void fillLifeInsuranceData(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Obchod založen - kontrola OK"))
                .then(tasks.contractTasks().setLifeInsurance())
                .clickCloseContract();
        }

    /**
     * Filling in mandatory data and approve contract.
     * @param hyposContract - mortgages data
     */
    public void approveContract(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Ověřování"))
                .then(tasks.contractTasks().setEventsFlagsUw1());

        ContractHistoryPage contractHistoryPage = harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Ověřování"))
                .then(tasks.hypokalkulackaTasks().createNewHypokalkulackaVersion(hyposContract))
                .then(tasks.contractTasks().finishContractApproval(hyposContract))
                .checkContractState("Úvěr schválen");

        hyposContract.getClients().stream()
                .filter(cardif -> !cardif.getCardifType().equals(CardifType.WITHOUT_CARDIF.getValue()))
                .findAny()
                .ifPresent(cardif -> contractHistoryPage.then(tasks.contractTasks().setLifeInsurance()));
    }

    /**
     * Generating contract documents and sign contract.
     * @param hyposContract - mortgages data
     */
    public void generateAndSignLoanContractDocuments(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Úvěr schválen"))
                .then(tasks.contractTasks().setEventsFlagsUw2());

        Assertions.assertEquals(true, new HyposDbTasks(harness).loanAccountOpeningFinished(hyposContract.getContractId()), "Loan account was not opened in 420 seconds interval.");

        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Příprava smluv"))
                .then(tasks.contractTasks().generateLoanContractDocuments())
                .then(tasks.contractTasks().setEventsFlagsDocumentsReady());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Příprava smluv"))
                .then(tasks.contractTasks().signLoanContractDocuments())
                .checkContractState(CONTRACT_STATE_SIGNED)
                .clickCloseContract();
    }

    /**
     * Creating and settings current account.
     * @param hyposContract - mortgages data
     */
    public void createCurrentAccount(HyposContract hyposContract, Credentials banker) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        String cif = harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), CONTRACT_STATE_SIGNED))
                .then(tasks.contractTasks().getClientCifInHypos(hyposContract));
        Assertions.assertNotNull(cif, "Client's CIF is empty. Stopping test.");
        String currentAccountNumber = tasks.createCurrentAccountForExistingClient(banker.getUsername(), hyposContract, cif);
        String nodeHost = harness.getEndpoint(HyposEndpoint.class).getNodeHost();
        String createdDateTime = DateUtils.getFormattedTodayDateTime("yyyy-MM-dd hh:mm:ss");
        harness.getEndpoint(TestAutomationDbEndpoint.class)
                .insertCreatedClientInfo(cif, hyposContract.getClients().get(FIRST_CLIENT.getValue()).getNonClient().birthNumber(), null, createdDateTime, banker.getUsername(), "Hypos", nodeHost);
        hyposContract.setCurrentAccountNumber(currentAccountNumber);
    }

    /**
     * Filling in mandatory data and create draw request.
     * @param hyposContract - mortgages data
     */
    public void fillDataAndCreateDrawRequest(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), CONTRACT_STATE_SIGNED))
                .then(tasks.contractTasks().setLandAreaRegistrationDates())
                .then(tasks.contractTasks().setClientCurrentAccount(hyposContract.getCurrentAccountNumber()));
        harness.withHypos()
                .openLoginPage()
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), CONTRACT_STATE_SIGNED))
                .then(tasks.contractTasks().approveDrawingConditions())
                .then(tasks.contractTasks().createNewDrawRequest(hyposContract));
        harness.withHypos()
                .openLoginPage()
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), CONTRACT_STATE_SIGNED))
                .then(tasks.contractTasks().processAndApproveNewDraw());
    }

    /**
     * Verify draw request.
     * @param hyposContract - mortgages data
     */
    public void verifyDrawRequest(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .switchToTestDrawVerificationPage()
                .then(tasks.searchAndVerifyDraw(hyposContract.getContractId()));
    }

    /**
     * Drawing contract.
     * @param hyposContract - mortgages data
     */
    public void doDrawInIcbs(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Úvěr dočerpán"))
                .then(tasks.contractTasks().doDrawInIcbs());
    }

    /**
     * Checking contract after drawing.
     * @param hyposContract - mortgages data
     */
    public void checkContractDrawing(HyposContract hyposContract) {
        tasks.hyposCommonTasks().hyposLog(PROCESS, new Object(){}.getClass().getEnclosingMethod().getName());
        harness.withHypos()
                .openLoginPage()
                .then(tasks.loginToHyposWithProfile(HyposProfile.EMERGENCY))
                .then(tasks.searchAndOpenContractAndCheckState(hyposContract.getContractId(), "Úvěr splácen"));
    }
}