package ee.tuleva.onboarding.mandate.command;

import ee.tuleva.onboarding.account.AccountStatementService;
import ee.tuleva.onboarding.account.FundBalance;
import ee.tuleva.onboarding.fund.FundRepository;
import ee.tuleva.onboarding.mandate.FundTransferExchange;
import ee.tuleva.onboarding.mandate.Mandate;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

import static java.util.stream.Collectors.toList;

@Component
@RequiredArgsConstructor
public class CreateMandateCommandToMandateConverter implements Converter<CreateMandateCommandWithUser, Mandate> {

    private final AccountStatementService accountStatementService;
    private final FundRepository fundRepository;

    @Override
    @NonNull
    public Mandate convert(CreateMandateCommandWithUser createMandateCommandWithUser) {
        Mandate mandate = new Mandate();
        mandate.setUser(createMandateCommandWithUser.getUser());

        val createMandateCommand = createMandateCommandWithUser.getCreateMandateCommand();

        mandate.setPillar(getPillar(createMandateCommand));

        List<FundTransferExchange> fundTransferExchanges =
            createMandateCommand.getFundTransferExchanges()
                .stream()
                .map(exchange -> FundTransferExchange.builder()
                    .sourceFundIsin(exchange.getSourceFundIsin())
                    .targetFundIsin(exchange.getTargetFundIsin())
                    .amount(getAmount(exchange, mandate))
                    .mandate(mandate)
                    .build())
                .collect(toList());

        mandate.setFundTransferExchanges(fundTransferExchanges);
        mandate.setFutureContributionFundIsin(createMandateCommand.getFutureContributionFundIsin());

        return mandate;
    }

    private Integer getPillar(CreateMandateCommand createMandateCommand) {
        val sourceIsin = getIsin(createMandateCommand);

        if (sourceIsin == null) {
            throw new IllegalArgumentException("Isin not found");
        }

        val fund = fundRepository.findByIsin(sourceIsin);

        if (fund == null) {
            throw new IllegalArgumentException(
                "Provided fund isin not found in the database: " + createMandateCommand.getFutureContributionFundIsin());
        }
        return fund.getPillar();
    }

    private String getIsin(CreateMandateCommand createMandateCommand) {
        if (createMandateCommand.getFutureContributionFundIsin() != null) {
            return createMandateCommand.getFutureContributionFundIsin();
        }
        return createMandateCommand.getFundTransferExchanges().stream()
            .map(MandateFundTransferExchangeCommand::getSourceFundIsin).findFirst().orElse(null);
    }

    private BigDecimal getAmount(MandateFundTransferExchangeCommand exchange, Mandate mandate) {
        val pillar = mandate.getPillar();
        if (pillar == 2) {
            return exchange.getAmount();
        } else if (pillar == 3) {
            val statement = accountStatementService.getAccountStatement(mandate.getUser());
            val balance = getFundBalance(statement, exchange.getSourceFundIsin());
            val exchangeAmount = balance.getUnits().multiply(exchange.getAmount());
            return exchangeAmount.setScale(3, RoundingMode.HALF_UP);
        } else {
            throw new IllegalStateException("Unknown pillar " + pillar);
        }
    }

    private FundBalance getFundBalance(List<FundBalance> accountStatement, String isin) {
        return accountStatement.stream()
            .filter(fundBalance -> fundBalance.getFund().getIsin().equals(isin))
            .findFirst()
            .orElseThrow(() -> new IllegalStateException("Fund not found: " + isin));
    }
}

@@ -2206,17 +2206,24 @@
 eption(%22
-I
+Source i
 sin not 
@@ -2227,17 +2227,42 @@
 ot found
-%22
+: %22 + createMandateCommand
 );%0A     
@@ -2401,33 +2401,16 @@
 ception(
-%0A                
 %22Provide
@@ -2456,60 +2456,18 @@
 %22 + 
-createMandateCommand.getFutureContributionFund
+source
 Isin
-()
 );%0A 

Add better logging