package gr.piraeusbank.max.parser.parser.ergo;

import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import gr.piraeusbank.max.parser.domain.intranet.csv.ergo.ErgoDetailImportMappingStrategy;
import gr.piraeusbank.max.parser.domain.intranet.csv.ergo.ErgoHeaderImportMappingStrategy;
import gr.piraeusbank.max.parser.exception.MaxParserException;
import gr.piraeusbank.max.parser.parser.MaxParser;
import gr.piraeusbank.max.parser.parser.ParserErrors;
import gr.piraeusbank.max.parser.service.MaxWrapperDb;
import gr.piraeusbank.max.parser.util.Cache;
import gr.piraeusbank.max.parser.util.Transform;
import io.lsn.meru.importmodel.domain.holder.ImportRecord;
import io.lsn.meru.importmodel.domain.invoice.Invoice;
import io.lsn.meru.importmodel.domain.policy.Policy;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class ErgoAutoParser extends ErgoParser implements MaxParser {
    private final String marketLob;
    private final Integer year;
    private final Integer month;
    private final String daysRange;
    private final MaxWrapperDb maxWrapperDb;
    private List<String> headerPathList;
    private List<String> detailPathList;
    private List<ImportRecord> parsedHeaderEntries = new ArrayList<>();
    private List<ImportRecord> parsedDetailEntries = new ArrayList<>();
    private final ParserErrors parserErrors = new ParserErrors();

    public ErgoAutoParser(String marketLob, Integer year, Integer month, String daysRange, MaxWrapperDb maxWrapperDb) {
        this.marketLob = marketLob;
        this.year = year;
        this.month = month;
        this.daysRange = daysRange;
        this.maxWrapperDb = maxWrapperDb;
    }

    private String getPath() {
        return "//2869s3/ReportsBlueByte/001 BACK OFFICE ΗΛΕΚΤΡΟΝΙΚΑ ΑΡΧΕΙΑ/ERGO/" + year + "/" +
                Transform.transformIntegerToGreekMonth(month) + '/' +
                daysRange + "/";
    }

    @Override public MaxParser readInput() throws Exception {
        log.info("Reading Header Data TXT files from path: {}", getPath());
        List<String> filePaths = getFilePathsList();
        getHeaderAndDetailPathList(filePaths);

/* 	//    Perhaps do the read input for all the processes of parsing the headers and details
//          Do the magic for this batch
        final CompletableFuture<Boolean> future = new CompletableFuture<>();
        futures.add(future);
        threadPool.submit(() -> {*/

        // load the headers .txt
        for (String hp : headerPathList) {
//            print current file path
            log.info(hp);

//            final FileReader reader = new FileReader(hp); READ WITH THAT for UTF-8 ONLY
            final BufferedReader bufferedReader =
                    new BufferedReader(new InputStreamReader(Files.newInputStream(Paths.get(hp)),
                            Charset.forName("windows-1253")));

            final CsvToBean<ImportRecord> csvToBean = new CsvToBeanBuilder<ImportRecord>(bufferedReader)
                    .withSeparator(',')
                    .withThrowExceptions(false)
                    .withIgnoreEmptyLine(true)
                    .withIgnoreLeadingWhiteSpace(true)
                    .withFilter(line -> line.length > 0 && !(line[0].startsWith("//") || line[0].startsWith("#")))
                    .withMappingStrategy(new ErgoHeaderImportMappingStrategy(maxWrapperDb))
                    .build();
            parsedHeaderEntries.addAll(csvToBean.parse());

        }

        //Map with invoice number as a key to remove invoice number duplicates
        final Map<String, ImportRecord> entriesByInvoiceNumber = new HashMap<>();
        for (ImportRecord ir : parsedHeaderEntries) entriesByInvoiceNumber.put(ir.getInvoice().getNumber(), ir);


        log.info("Ready to read Detail Data from files ");
        for (String dp : detailPathList) {
//            print current file path
            log.info(dp);

//            final FileReader reader = new FileReader(dp); READ WITH THAT for UTF-8 ONLY
            final BufferedReader bufferedReader =
                    new BufferedReader(new InputStreamReader(Files.newInputStream(Paths.get(dp)),
                            Charset.forName("windows-1253")));

            final CsvToBean<ImportRecord> csvToBean2 = new CsvToBeanBuilder<ImportRecord>(bufferedReader)
                    .withSeparator(',')
                    .withThrowExceptions(false)
                    .withIgnoreEmptyLine(true)
                    .withIgnoreLeadingWhiteSpace(true)
                    .withFilter(line -> line.length > 0 && !(line[0].startsWith("//") || line[0].startsWith("#")))
                    .withMappingStrategy(new ErgoDetailImportMappingStrategy(entriesByInvoiceNumber))
                    .build();
            parsedDetailEntries.addAll(csvToBean2.parse());
        }


        /*  1. done -set commision pa and pb to policy
            2. Check if exists another policy cover so add ONLY THE COMMISIONS and keep the rest as it is
            3. done - set commision pa and pb to invoice
            4.  Check if exists another invoice cover so add ONLY THE COMMISIONS and keep the rest as it is.
            Perhaps in the end?
             */


        return this;
    }

    private void getHeaderAndDetailPathList(List<String> filePaths) {
        if (!filePaths.isEmpty()) {
            this.headerPathList = filePaths.stream()
                    .filter(p -> p.contains("HEADER"))
                    .collect(Collectors.toList());

            this.detailPathList = filePaths.stream()
                    .filter(p -> p.contains("DETAIL"))
                    .collect(Collectors.toList());
        } else throw new MaxParserException("We don't have any files to parse in this folder. Give another one.");
    }

    @Override public MaxParser transform() {
        log.info("ErgoAutoParser in transform. They were transformed and mapped regularly inside population process");

        // remove null invoice-type entries from the header list
        parsedHeaderEntries.removeIf(ir -> ir.getInvoice().getInvoiceType() == null);
//        parsedHeaderEntries.forEach(System.out::println);

        for (ImportRecord parsedDetailEntry : parsedDetailEntries) {
            Invoice invoice = parsedDetailEntry.getInvoice();
            log.info(invoice.getCommissionPa() + " " + invoice.getCommissionPb());
        }

//    do similar impl like ibsparser method----> writeParserKnownErrors();


        //implement this
        return this;
    }

    @Override public MaxParser generateJson() throws IOException {
        log.info("ErgoAutoParser in generateJson");
        //implement this
        return this;
    }

    private List<String> getFilePathsList() throws IOException {
        List<String> filePaths;
        try (Stream<Path> paths = Files.walk(Paths.get(getPath()))) {
            filePaths = paths.filter(Files::isRegularFile)
                    .map(Path::toString)
                    .filter(p -> p.contains("AUTO"))
                    .collect(Collectors.toList()); // Collect into a List
        }
//        log.info(filePaths.toString());
        return filePaths;
    }

    public static boolean checkValueExistsInMeru(ImportRecord importRecord, String checkValue,
                                                 int valuePositionFromMeruQuery) {
        // log.info("nExisting {}",retrieveUniquenessAsOneField(entry));
        return getBusinessValueChecksSet(valuePositionFromMeruQuery).contains(
                retrieveBusinessUniquenessChecksFromFile(importRecord, checkValue));
    }

    public static Set<String> getBusinessValueChecksSet(int lineNumValue) {
        final Set<String> comparisonSet = new HashSet<>();
        for (String e : Cache.getSkipSetAll()) {
            String[] parts = e.split("_");
            comparisonSet.add(
                    String.join("_", parts[1], parts[2], parts[3],
                            parts[4], parts[lineNumValue]));
        }
        return comparisonSet;
    }

    public static synchronized String retrieveBusinessUniquenessChecksFromFile(ImportRecord importRecord,
                                                                               String checkValue) {
        final Policy policy = importRecord.getPolicy();
        return String.format("%s_%s_%s_%s_%s",
                policy.getInsuranceCompany().getCode(),
                policy.getAgent().getCode(),
                Cache.getPackageLobMarketingMapping().get(policy.getInsurancePackage().getCode()),
                policy.getNumber(),
                checkValue);
    }

    @Override public MaxParser validate() {
        return this; // No-Op
    }

    @Override public MaxParser persistToDb() {
        return this; // No-Op
    }

    @Override public MaxParser publishToBroker() {
        return this; // No-Op
    }
}

package gr.piraeusbank.max.parser.domain.intranet.csv.ergo;

import com.opencsv.CSVReader;
import com.opencsv.bean.MappingStrategy;
import com.opencsv.exceptions.*;
import gr.piraeusbank.max.parser.exception.MaxParserException;
import gr.piraeusbank.max.parser.util.InvoiceType;
import io.lsn.meru.importmodel.domain.holder.ImportRecord;
import io.lsn.meru.importmodel.domain.invoice.Invoice;
import io.lsn.meru.importmodel.domain.invoice.InvoiceCover;
import io.lsn.meru.importmodel.domain.policy.PolicyObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;

import static gr.piraeusbank.max.parser.exception.MaxParserException.unsupported;
import static gr.piraeusbank.max.parser.util.Factory.replaceDoubleQuotes;
import static gr.piraeusbank.max.parser.util.Factory.replaceDoubleQuotesAndCommas;

@Slf4j
public class ErgoDetailImportMappingStrategy implements MappingStrategy<ImportRecord> {
    private final Map<String, ImportRecord> entriesByInvoiceNumber;

    public ErgoDetailImportMappingStrategy(Map<String, ImportRecord> entriesByInvoiceNumber) {
        this.entriesByInvoiceNumber = entriesByInvoiceNumber;
    }

    @Override public ImportRecord populateNewBean(String[] line)
            throws CsvBeanIntrospectionException, CsvValidationException {

        ImportRecord importRecord = new ImportRecord();
        try {
            final String policyNumber = replaceDoubleQuotes(line[0]);
            final String invoiceNumber = replaceDoubleQuotes(line[1]);

//          Policy and Invoice number checkings
            if (StringUtils.isBlank(policyNumber))
                throw new MaxParserException(
                        String.format("Policy with number %s doesn't have value %n", policyNumber));
            if (StringUtils.isBlank(invoiceNumber))
                throw new MaxParserException(
                        String.format("Invoice with number %s doesn't have value! %n", invoiceNumber));

            // The line that is processed, should have already been loaded, thus make sure it exists
            if (!entriesByInvoiceNumber.containsKey(invoiceNumber))
                throw unsupported(String.format("InvoiceNumber %s from commissions cannot be " +
                        "matched with any parsed invoices from issues", invoiceNumber));

            importRecord = entriesByInvoiceNumber.get(invoiceNumber);
            if (!importRecord.getPolicy().getNumber().equals(policyNumber))
                throw unsupported(String.format("The InvoiceNumber %s from commissions does not match the related " +
                                "policyNumber. policyNumber (issue): %s, policyNumber (commissions): %s",
                        invoiceNumber, importRecord.getPolicy().getNumber(), policyNumber));

            InvoiceType invoiceType = InvoiceType.of(importRecord.getInvoice().getInvoiceType().getCode());
            switch (invoiceType) {
                case NEW_POLICY:
                    throw new MaxParserException(
                            "New Policy type found and should have been already registered, so should be skipped!");
                case RENEWAL:
                case AMENDMENT:
                case CANCELLATION:
                    injectCommissionFields(importRecord, line);
                    return importRecord;
                default:
                    throw new CsvException("Not implemented invoiceType " + invoiceType);
            }
        } catch (Exception e) {
            final String errMsg = String.format("Error {Invoice: %s, Policy: %s} Msg: %s ",
                    importRecord.getInvoice().getNumber(), importRecord.getPolicy().getNumber(), e.getMessage());
            log.error(errMsg/*,e*/); //uncomment e in log to provide full stacktrace info for MaxParser

//        Add the error below and write known-error files in json type, in the end
//         addError(new ParserErrorWithStackTrace(errMsg, e));

/*//          Remove if not used in the end all the errors(If we dont want getCaptured() exceptions for csv),
            only write in json then
            throw new CsvValidationException(e.getMessage());*/
        }
        throw new CsvValidationException("Not implemented invoiceType ");
    }


    private void injectCommissionFields(ImportRecord entity, String[] line) {
        Invoice invoice = entity.getInvoice();
        enrichPolicyCover(line, entity);
        enrichInvoiceCover(line, entity);

        invoice.setCommissionPa(new BigDecimal(replaceDoubleQuotesAndCommas(line[6])));
        invoice.setCommissionPb(new BigDecimal(replaceDoubleQuotesAndCommas(line[8])));
    }

    private void enrichPolicyCover(String[] line, ImportRecord entity) {
        PolicyObject policyObject = entity.getPolicy().getInsuredPerson().getPolicyObjectList().get(0);
        policyObject.getCoverList().get(0)
                .setCommissionAmount(new BigDecimal(replaceDoubleQuotesAndCommas(line[6])).add(
                        new BigDecimal(replaceDoubleQuotesAndCommas(line[8]))));
    }

    private void enrichInvoiceCover(String[] line, ImportRecord entity) {
        entity.getInvoice().getInvoiceCoverList().get(0)
                .setCommission(new BigDecimal(replaceDoubleQuotesAndCommas(line[6])).add(
                        new BigDecimal(replaceDoubleQuotesAndCommas(line[8]))));
    }

    // no-op below methods
    @Override public void captureHeader(CSVReader reader) throws IOException, CsvRequiredFieldEmptyException {

    }

    @Override public String[] transmuteBean(ImportRecord bean)
            throws CsvDataTypeMismatchException, CsvRequiredFieldEmptyException {
        return new String[0];
    }

    @Override public void setType(Class<? extends ImportRecord> type) throws CsvBadConverterException {
        // no-op
    }

    @Override public String[] generateHeader(ImportRecord bean) throws CsvRequiredFieldEmptyException {
        return new String[0];
    }
}

with this code why commissions pa and commissions pb in the end when i parse the csv tajke the same values? Any ideas? there are differnt values in csv file.