package ru.nbch.indicsearchservice.aggregator;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.mutable.MutableLong;
import org.springframework.stereotype.Component;
import ru.nbch.indicsearchservice.util.IndicSearchCommon;
import ru.nbch.indicsearchservice.util.MovableBufferedReader;

@Component
public class FileAggregatorCommon {
    private static final List<String> RU_UID_CHILD_TABLES = List.of(
        "RU_TRADE",
        "RU_ACCOUNT_AMT",
        "RU_COBORROWER",
        "RU_PAYMT_CONDITION",
        "RU_OVERALLVAL",
        "RU_AMENDMENT",
        "RU_FUNDDATE",
        "RU_ARREAR",
        "RU_DUEARREAR",
        "RU_PASTDUEARREAR",
        "RU_PAYMT",
        "RU_MONTHAVERPAYMT",
        "RU_SOURCENONMONETOBLIG",
        "RU_SUBJECTNONMONETOBLIG",
        "RU_COLLATERAL",
        "RU_GUARANTOR",
        "RU_INDEPGUARANTOR",
        "RU_COLLATINSURED",
        "RU_COLLATREPAY",
        "RU_GUARANTEEREPAY",
        "RU_OBLIGTERMINATION",
        "RU_SUBMITHOLD",
        "RU_ACQUIRERLEGAL",
        "RU_ACQUIRERINDIVID",
        "RU_SERVICECO",
        "RU_OBLIGACCOUNT",
        "RU_LEGALRECORD",
        "RU_OBLIGSRCBANKRUPT",
        "RU_OBLIGSRCLIQUID",
        "RU_AMOUNT_INFO",
        "RU_CONTRACT_TERMS_CHANGES",
        "RU_ALL_ARREARS"
    );

    void aggregateRuUidFiles(BufferedWriter aggregatedFileWriter,
                             Map<String, Path> tableFileMap,
                             String memberCode,
                             String acctNum,
                             String uuid,
                             MutableLong rowCounter) {
        try (BufferedReader ruUidFileReader = Files.newBufferedReader(tableFileMap.get("RU_UID"))) {
            Map<String, MovableBufferedReader> childTablesReadersMap = new LinkedHashMap<>();
            for (String tableName : RU_UID_CHILD_TABLES) {
                if (!tableFileMap.containsKey(tableName)) {
                    continue;
                }

                MovableBufferedReader childTablesReader = new MovableBufferedReader(Files.newBufferedReader(tableFileMap.get(tableName)));
                if (childTablesReader.getCurrentLine() != null) {
                    childTablesReadersMap.put(tableName, childTablesReader);
                }
            }

            String ruUidLine;
            while ((ruUidLine = ruUidFileReader.readLine()) != null) {
                List<String> lineList = Arrays.asList(ruUidLine.split("%%%"));
                Optional<String> foundMemberCode = IndicSearchCommon.getRowValue(lineList, "MEMBER_CODE");

                if (foundMemberCode.isPresent() && !foundMemberCode.get().startsWith(memberCode)) {
                    continue;
                }

                if (acctNum != null) {
                    Optional<String> foundAcctNum = IndicSearchCommon.getRowValue(lineList, "ACCT_NUM");

                    if (foundAcctNum.isPresent() && !foundAcctNum.get().equals(acctNum)) {
                        continue;
                    }
                }
                if (uuid != null) {
                    Optional<String> foundUuid = IndicSearchCommon.getRowValue(lineList, "UUID");

                    if (foundUuid.isPresent() && !foundUuid.get().equals(uuid)) {
                        continue;
                    }
                }

                aggregatedFileWriter.write(ruUidLine);
                aggregatedFileWriter.newLine();
                rowCounter.increment();

                Optional<String> foundRuUidSerialNum = IndicSearchCommon.getRowValue(lineList, "SERIAL_NUM");
                if (foundRuUidSerialNum.isEmpty()) {
                    throw new RuntimeException("SERIAL_NUM was not found!");
                }
                Long ruUidSerialNum = Long.parseLong(foundRuUidSerialNum.get());

                for (Map.Entry<String, MovableBufferedReader> entry : childTablesReadersMap.entrySet()) {
                    String childTableName = entry.getKey();
                    MovableBufferedReader childReader = entry.getValue();

                    String childLine = childReader.getCurrentLine();
                    if (childLine == null) {
                        continue;
                    }

                    List<String> childLineList = Arrays.asList(childLine.split("%%%"));
                    Optional<String> foundChildAccSerialNum = IndicSearchCommon.getRowValue(childLineList, "ACC_SERIAL_NUM");
                    if (foundChildAccSerialNum.isEmpty()) {
                        throw new RuntimeException("ACC_SERIAL_NUM was not found!");
                    }
                    Long childAccSerialNum = Long.parseLong(foundChildAccSerialNum.get());

                    if (ruUidSerialNum > childAccSerialNum) {
                        while ((childLine = childReader.nextLine()) != null) {
                            childLineList = Arrays.asList(childLine.split("%%%"));
                            foundChildAccSerialNum = IndicSearchCommon.getRowValue(childLineList, "ACC_SERIAL_NUM");
                            if (foundChildAccSerialNum.isEmpty()) {
                                throw new RuntimeException("ACC_SERIAL_NUM was not found!");
                            }
                            childAccSerialNum = Long.parseLong(foundChildAccSerialNum.get());

                            if (childAccSerialNum.equals(ruUidSerialNum)) {
                                aggregatedFileWriter.write(childLine);
                                aggregatedFileWriter.newLine();
                                rowCounter.increment();

                                if (childTableName.equals("RU_COLLATERAL")) {
                                    aggregateBlocksTableFile(childTableName, aggregatedFileWriter, tableFileMap, memberCode, rowCounter);
                                }
                            }

                            if (childAccSerialNum > ruUidSerialNum) {
                                break;
                            }
                        }
                    } else if (ruUidSerialNum.equals(childAccSerialNum)) {
                        aggregatedFileWriter.write(childLine);
                        aggregatedFileWriter.newLine();
                        rowCounter.increment();

                        if (childTableName.equals("RU_COLLATERAL")) {
                            aggregateBlocksTableFile(childTableName, aggregatedFileWriter, tableFileMap, memberCode, rowCounter);
                        }

                        while ((childLine = childReader.nextLine()) != null) {
                            childLineList = Arrays.asList(childLine.split("%%%"));
                            foundChildAccSerialNum = IndicSearchCommon.getRowValue(childLineList, "ACC_SERIAL_NUM");
                            if (foundChildAccSerialNum.isEmpty()) {
                                throw new RuntimeException("ACC_SERIAL_NUM was not found!");
                            }
                            childAccSerialNum = Long.parseLong(foundChildAccSerialNum.get());

                            if (childAccSerialNum.equals(ruUidSerialNum)) {
                                aggregatedFileWriter.write(childLine);
                                aggregatedFileWriter.newLine();
                                rowCounter.increment();

                                if (childTableName.equals("RU_COLLATERAL")) {
                                    aggregateBlocksTableFile(childTableName, aggregatedFileWriter, tableFileMap, memberCode, rowCounter);
                                }
                            }

                            if (childAccSerialNum > ruUidSerialNum) {
                                break;
                            }
                        }
                    }
                }
            }

            for (MovableBufferedReader br : childTablesReadersMap.values()) {
                br.getReader().close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    void aggregateBlocksTableFile(String parentTableName, BufferedWriter aggregatedFileWriter, Map<String, Path> tableFileMap, String memberCode, MutableLong rowCounter) {
        try (BufferedReader parentFileReader = Files.newBufferedReader(tableFileMap.get(parentTableName));
             MovableBufferedReader blocksFileReader = new MovableBufferedReader(Files.newBufferedReader(tableFileMap.get(parentTableName + "_BLOCKS")))) {
            String parentLine;
            while ((parentLine = parentFileReader.readLine()) != null) {
                List<String> parentLineList = Arrays.asList(parentLine.split("%%%"));
                Optional<String> foundMemberCode = IndicSearchCommon.getRowValue(parentLineList, "MEMBER_CODE");

                if (foundMemberCode.isPresent() && !foundMemberCode.get().startsWith(memberCode)) {
                    continue;
                }

                aggregatedFileWriter.write(parentLine);
                aggregatedFileWriter.newLine();
                rowCounter.increment();

                Optional<String> foundParentSerialNum = IndicSearchCommon.getRowValue(parentLineList, "SERIAL_NUM");
                if (foundParentSerialNum.isEmpty()) {
                    throw new RuntimeException("SERIAL_NUM was not found!");
                }
                Long parentSerialNum = Long.parseLong(foundParentSerialNum.get());

                String blocksLine = blocksFileReader.getCurrentLine();
                if (blocksLine == null) {
                    continue;
                }

                List<String> childLineList = Arrays.asList(blocksLine.split("%%%"));
                Optional<String> foundChildParentSerialNum = IndicSearchCommon.getRowValue(childLineList, "PARENT_SERIAL_NUM");
                if (foundChildParentSerialNum.isEmpty()) {
                    throw new RuntimeException("PARENT_SERIAL_NUM was not found!");
                }
                Long blocksParentSerialNum = Long.parseLong(foundChildParentSerialNum.get());

                if (parentSerialNum > blocksParentSerialNum) {
                    while ((blocksLine = blocksFileReader.nextLine()) != null) {
                        childLineList = Arrays.asList(blocksLine.split("%%%"));
                        foundChildParentSerialNum = IndicSearchCommon.getRowValue(childLineList, "PARENT_SERIAL_NUM");
                        if (foundChildParentSerialNum.isEmpty()) {
                            throw new RuntimeException("PARENT_SERIAL_NUM was not found!");
                        }
                        blocksParentSerialNum = Long.parseLong(foundChildParentSerialNum.get());

                        if (blocksParentSerialNum.equals(parentSerialNum)) {
                            aggregatedFileWriter.write(blocksLine);
                            aggregatedFileWriter.newLine();
                            rowCounter.increment();
                        }

                        if (blocksParentSerialNum > parentSerialNum) {
                            break;
                        }
                    }
                } else if (parentSerialNum.equals(blocksParentSerialNum)) {
                    aggregatedFileWriter.write(blocksLine);
                    aggregatedFileWriter.newLine();
                    rowCounter.increment();

                    while ((blocksLine = blocksFileReader.nextLine()) != null) {
                        childLineList = Arrays.asList(blocksLine.split("%%%"));
                        foundChildParentSerialNum = IndicSearchCommon.getRowValue(childLineList, "PARENT_SERIAL_NUM");
                        if (foundChildParentSerialNum.isEmpty()) {
                            throw new RuntimeException("PARENT_SERIAL_NUM was not found!");
                        }
                        blocksParentSerialNum = Long.parseLong(foundChildParentSerialNum.get());

                        if (blocksParentSerialNum.equals(parentSerialNum)) {
                            aggregatedFileWriter.write(blocksLine);
                            aggregatedFileWriter.newLine();
                            rowCounter.increment();
                        }

                        if (blocksParentSerialNum > parentSerialNum) {
                            break;
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
