package ru.nbch.credit_tracker.service.indic;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.util.*;

@Service
public class FlagsService {

    private static final Logger log = LoggerFactory.getLogger(FlagsService.class);
    private final FlagsMapper mapper;

    public FlagsService(FlagsMapper mapper) {
        this.mapper = mapper;
    }

    public Map<Integer, FlagsData> findFlags(List<Integer> fids) {
        if (fids.isEmpty()) {
            return new HashMap<>();
        }
        List<Integer> distinctFids = fids.stream()
                .distinct()
                .sorted(Comparator.naturalOrder())
                .toList();

        log.debug("Fids count: {}", distinctFids.size());
        Map<Integer, Integer> flags5000 = new HashMap<>();
        Map<Integer, Integer> flags90DaysByPastDueArrear = new HashMap<>();
        Map<Integer, Integer> flags90DaysByRuPaymt = new HashMap<>();
        findFlagsRuPastDueArrear(distinctFids, flags5000, flags90DaysByPastDueArrear);
        findFlagsRuPaymt(distinctFids, flags90DaysByRuPaymt);

        Map<Integer, FlagsData> result = new HashMap<>();
        fids.forEach(fid -> {
            Integer flagDebt5000 = flags5000.get(fid);
            if (flagDebt5000 == null) {
                flagDebt5000 = 0;
            }

            Integer flag90days = flags90DaysByPastDueArrear.get(fid);
            if (flag90days == null || flag90days == 0) {
                flag90days = flags90DaysByRuPaymt.get(fid);
                if (flag90days == null) {
                    flag90days = 0;
                }
            }
            result.put(fid, new FlagsData(flagDebt5000, flag90days));
        });

        return result;
    }

    /**
     * Выбирает данные из RU_PASTDUEARREAR и ищет по ним флаги просрочек
     * По FID из  RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT.
     * Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то flag500 устанавливается в 1.
     * Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0.
     *
     * flag90Days = 1, если выполняется хотя бы одно из условий:
     * В INDIC.RU_PASTDUEARREAR у данного FID есть запись с DAYS_PAST_DUE ≥ 90 и REPORTED_DT >= текущая дата – 12 месяцев.
     * Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
     * @param fids        - отсортирован по возрастанию фидов
     * @param flag5000Map - рещультирующая мапа флагов просрочек debt5000
     * @param flag90Days  - рещультирующая мапа флагов просрочек 90_days
     */
    private void findFlagsRuPastDueArrear(List<Integer> fids, Map<Integer, Integer> flag5000Map, Map<Integer, Integer> flag90Days) {
        log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
        List<RuPastDuarearData> ruPastDuarearDataList = mapper.findRuPastDuarearData(fids);

        ruPastDuarearDataList.sort(Comparator.comparing(FlagsService.RuPastDuarearData::getFid) // сортируем сначала по фиду
                .thenComparing(FlagsService.RuPastDuarearData::getReportedDate, Comparator.reverseOrder())); // Затем сортируем по REPORTED_DT по убыванию

        LocalDate yearAgo = LocalDate.now().minusYears(1);

        for (Integer fid : fids) {
            Iterator<RuPastDuarearData> iterator = ruPastDuarearDataList.iterator();
            BigInteger prevAccSerialNum = null;
            while (iterator.hasNext()) {
                FlagsService.RuPastDuarearData data = iterator.next();
                if (fid.equals(data.getFid())) {
                    if (flag5000Map.get(fid) == null) { // Первая запись в списке с макс REPORTED_DT. Нас интересует только она для флага 5000
                        if (prevAccSerialNum == null || !prevAccSerialNum.equals(data.getAccSerialNum())) {
                            if (data.getAmtPastDue() != null && data.getAmtPastDue().compareTo(BigDecimal.valueOf(5000)) >= 0) {
                                flag5000Map.put(fid, 1);
                            }
                            prevAccSerialNum = data.getAccSerialNum();
                        }
                    }

                    Integer flag90 = flag90Days.get(fid);
                    if (flag90 != null && flag90 == 1) {
                        iterator.remove();
                        break;
                    }

                    if (flag90 == null || flag90 == 0) {
                        if (data.getDaysPastDue() != null) {
                            boolean has90daysFlag = data.getDaysPastDue().compareTo(BigInteger.valueOf(90)) >= 0 && !data.getReportedDate().isBefore(yearAgo);
                            if (has90daysFlag) {
                                flag90Days.put(fid, 1);
                                iterator.remove();
                                break;
                            } else {
                                flag90Days.put(fid, 0);
                                iterator.remove();
                            }
                        } else {
                            iterator.remove();
                        }
                    }
                }
            }
            // если мы так и не нашли флаги для этих фидов - выставляем их в 0
            flag5000Map.putIfAbsent(fid, 0);
            flag90Days.putIfAbsent(fid, 0);
        }
        log.debug("Complete calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
    }

    /**
     * Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days
     *
     * flag90Days = 1, если выполняется хотя бы одно из условий:
     * В INDIC.RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE ≥ 90 и REPORTED_DT >= текущая дата – 12 месяцев.
     * Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
     * @param fids - отсортирован по возрастанию фидов
     * @param flag90Days -  рещультирующая мапа флагов просрочек 90_days
     */
    private void findFlagsRuPaymt(List<Integer> fids, Map<Integer, Integer> flag90Days) {
        log.debug("Start calculating flag 90 days by RU_PAPAYMT");
        LocalDate yearAgo = LocalDate.now().minusYears(1);
        List<RuPaymtData> ruPaymtDataList = mapper.findRuPaymtData(fids, yearAgo);
        ruPaymtDataList.sort(Comparator.comparing(RuPaymtData::getFid, Comparator.naturalOrder()));

        for (Integer fid : fids) {
            Iterator<RuPaymtData> iterator = ruPaymtDataList.iterator();
            while (iterator.hasNext()) {
                RuPaymtData data = iterator.next();
                if (fid.equals(data.getFid())) {
                    Integer flag90 = flag90Days.get(fid);
                    if (flag90 != null && flag90 == 1) {
                        iterator.remove();
                        break;
                    }

                    if (flag90 == null || flag90 == 0) {
                        if (data.getDaysPastDue() != null) {
                            boolean has90daysFlag = data.getDaysPastDue().compareTo(BigInteger.valueOf(90)) >= 0 && !data.getReportedDate().isBefore(yearAgo);
                            if (has90daysFlag) {
                                flag90Days.put(fid, 1);
                                iterator.remove();
                                break;
                            } else {
                                flag90Days.put(fid, 0);
                                iterator.remove();
                            }
                        } else {
                            iterator.remove();
                        }
                    }
                }
            }
            flag90Days.putIfAbsent(fid, 0);
        }
        log.debug("Complete calculating flag 90 days by RU_PAPAYMT");
    }

    @Getter
    @Setter
    @AllArgsConstructor
    public static class FlagsData {
        private Integer flagDebt5000;
        private Integer flag90days;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public static class RuPastDuarearData {
        private Integer fid;
        private BigInteger accSerialNum;
        private LocalDate reportedDate;
        private BigDecimal amtPastDue;
        private BigInteger daysPastDue;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public static class RuPaymtData {
        private Integer fid;
        private BigInteger daysPastDue;
        private LocalDate reportedDate;
    }
}
