package ru.nbch.credit_tracker.service;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;
import ru.nbch.credit_tracker.service.indic.FlagsService;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class FlagsServiceTest {

    private static final LocalDate INITIAL_DATE = LocalDate.parse("2025-09-25");

    @Mock
    FlagsMapper flagsMapper;

    @Test
    public void testfindFlags() {
        FlagsService flagsService = new FlagsService(flagsMapper);

        when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
        when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(ruPaymtDataList);

        List<Integer> fids = new ArrayList<>(List.of(
                1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days  в RU_PAYMT
                3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR.  Есть просрочка 90_days  в RU_PAYMT
                4, // нет никаких просрочек
                7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR.  Есть просрочка 90_days  в RU_PAYMT
                8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
                9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
        );

        Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);

        Assertions.assertEquals(9, resultMap.size());
        Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
        Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
        Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
        Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());

        Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
    }

    @Test
    public void testfindFlagsWithoutRuPaymt() {
        FlagsService flagsService = new FlagsService(flagsMapper);

        when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
        when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(new ArrayList<>());

        List<Integer> fids = new ArrayList<>(List.of(
                1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days  в RU_PAYMT
                3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR.  Есть просрочка 90_days  в RU_PAYMT
                4, // нет никаких просрочек
                7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR.  Есть просрочка 90_days  в RU_PAYMT
                8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
                9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
        );

        Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);

        Assertions.assertEquals(9, resultMap.size());
        Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
        Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
        Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
        Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());

        Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(3).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(6).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(8).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(9).getFlag90days());
    }


    @Test
    public void testfindFlagsWithoutRuPastduearreae() {
        FlagsService flagsService = new FlagsService(flagsMapper);

        when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(new ArrayList<>());
        when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(ruPaymtDataList);

        List<Integer> fids = new ArrayList<>(List.of(
                1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days  в RU_PAYMT
                3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR.  Есть просрочка 90_days  в RU_PAYMT
                4, // нет никаких просрочек
                7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
                6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR.  Есть просрочка 90_days  в RU_PAYMT
                8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
                9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
        );

        Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);

        Assertions.assertEquals(9, resultMap.size());
        Assertions.assertEquals(0, resultMap.get(1).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(2).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(3).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(5).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
        Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());

        Assertions.assertEquals(0, resultMap.get(1).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(5).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
        Assertions.assertEquals(0, resultMap.get(7).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
        Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
    }


    private final List<FlagsService.RuPastDuarearData> ruPastDuarearDataList = new ArrayList<>(
            List.of(
                    new FlagsService.RuPastDuarearData(1, BigInteger.valueOf(1), calcLocalDateFromInitialDate("2025-05-31"), BigDecimal.valueOf(36000.00), BigInteger.valueOf(159)), // просрочка 5000 и 90_days по этой записи

                    new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2021-12-29"), BigDecimal.valueOf(3200.00), null), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2022-01-28"), BigDecimal.valueOf(6700.00), null), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2025-04-06"), BigDecimal.valueOf(5700.00), null), // просрочка 5000 по этой записи

                    new FlagsService.RuPastDuarearData(3, BigInteger.valueOf(3), calcLocalDateFromInitialDate("2025-01-14"), BigDecimal.valueOf(300000000000.00), null),

                    new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2021-11-29"), BigDecimal.valueOf(0.00), null), // не макс reported_date для того же acc_serial_num

                    new FlagsService.RuPastDuarearData(4, BigInteger.valueOf(4), calcLocalDateFromInitialDate("2025-12-06"), BigDecimal.valueOf(4000.00), null), // нет просрочек

                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-04-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(182)), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-05-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(212)), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-05-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-07-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(273)), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-07-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-08-04"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(307)), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2025-09-08"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(338)), // не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(51), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(368)), // Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-11-04"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(399)), // не макс reported_date для того же acc_serial_num
                    new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2022-01-04"), BigDecimal.valueOf(5000.31), BigInteger.valueOf(5)), // не макс reported_date для того же acc_serial_num

                    new FlagsService.RuPastDuarearData(6, BigInteger.valueOf(6), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(3000), null), // Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT)

                    new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(5500), BigInteger.valueOf(90)), // не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда
                    new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), calcLocalDateFromInitialDate("2025-09-10"), BigDecimal.valueOf(3000), null) // Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt

            )
    );

    private final List<FlagsService.RuPaymtData> ruPaymtDataList = new ArrayList<>(
            List.of(
                    new FlagsService.RuPaymtData(1, null, calcLocalDateFromInitialDate("2024-11-09")),
                    new FlagsService.RuPaymtData(2, BigInteger.valueOf(30), calcLocalDateFromInitialDate("2024-11-09")),
                    new FlagsService.RuPaymtData(3, null, calcLocalDateFromInitialDate("2024-11-10")),
                    new FlagsService.RuPaymtData(3, BigInteger.valueOf(100), calcLocalDateFromInitialDate("2024-11-15")),
                    new FlagsService.RuPaymtData(4, BigInteger.valueOf(10), calcLocalDateFromInitialDate("2024-11-15")),
                    new FlagsService.RuPaymtData(5, BigInteger.valueOf(10), calcLocalDateFromInitialDate("2023-11-15")),
                    new FlagsService.RuPaymtData(6, BigInteger.valueOf(90), calcLocalDateFromInitialDate("2024-11-09")),
                    new FlagsService.RuPaymtData(6, BigInteger.valueOf(80), calcLocalDateFromInitialDate("2024-10-09")),
                    new FlagsService.RuPaymtData(7, null, calcLocalDateFromInitialDate("2024-10-09")),
                    new FlagsService.RuPaymtData(8, null, calcLocalDateFromInitialDate("2024-10-09")),
                    new FlagsService.RuPaymtData(8, BigInteger.valueOf(83), calcLocalDateFromInitialDate("2024-10-09")),
                    new FlagsService.RuPaymtData(8, BigInteger.valueOf(91), calcLocalDateFromInitialDate("2024-10-09")),
                    new FlagsService.RuPaymtData(9, BigInteger.valueOf(100), calcLocalDateFromInitialDate("2024-10-09"))
            )
    );

    private LocalDate calcLocalDateFromInitialDate(String date) {
        LocalDateTime now = LocalDateTime.now();
        long daysPassed = ChronoUnit.DAYS.between(INITIAL_DATE, now);
        return LocalDate.parse(date).plusDays(daysPassed);
    }
}
