package ru.nbch.credit_tracker.service;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ru.nbch.credit_tracker.service.indic.FlagsService;
import ru.nbch.credit_tracker.service.indic.FlagsService2;

import javax.sql.DataSource;
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;

public class FlagsService2Test {

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

    private static final String RU_PASTDUEAREAR_CREATE = """
            create table RU_PASTDUEARREAR
            (
                FID                    INTEGER      not null,
                SERIAL_NUM             BIGINT       not null primary key,
                ACC_SERIAL_NUM         BIGINT       not null,
                REPORTED_DT            DATE         not null,
                AMT_PAST_DUE           DECIMAL(17, 2),
                DAYS_PAST_DUE          BIGINT
            );
            """;

    private static final String RU_PASTDUEAREAR_DROP = """
            DROP TABLE IF EXISTS RU_PASTDUEARREAR;
            """;

    private static final String RU_PAYMT_DROP = """
                        DROP TABLE IF EXISTS RU_PAYMT;
            """;

    private static final String RU_PAYMT_CREATE = """
            create table RU_PAYMT
            (
                FID                 INTEGER      not null,
                SERIAL_NUM          BIGINT       not null primary key,
                DAYS_PAST_DUE       INTEGER,
                REPORTED_DT         DATE         not null
            );
            """;

    private static final String FILL_RU_PASTDUEARREAR = String.format("""
                            INSERT INTO RU_PASTDUEARREAR VALUES
                                (1, 1,  1, '%s', 36000.00,        159), -- просрочка 5000 и 90_days по этой записи
                                (2, 2,  2, '%s', 3200.00,         null), -- не макс reported_date для того же acc_serial_num
                                (2, 3,  2, '%s', 6700.00,         null), -- не макс reported_date для того же acc_serial_num
                                (2, 4,  2, '%s', 5700.00,         null), -- просрочка 5000 по этой записи
                                (3, 5,  3, '%s', 300000000000.00, null),
                                (2, 6,  2, '%s', 0.00,            null), -- не макс reported_date для того же acc_serial_num
                                (4, 7,  4, '%s', 4000.00,         null), -- нет просрочек
                                (5, 8,  5, '%s', 2000.31,         182), -- не макс reported_date для того же acc_serial_num
                                (5, 9,  5, '%s', 2000.31,         212), -- не макс reported_date для того же acc_serial_num
                                (5, 10, 5, '%s', 2000.31,         null), -- не макс reported_date для того же acc_serial_num
                                (5, 11, 5, '%s', 2000.31,         273), -- не макс reported_date для того же acc_serial_num
                                (5, 12, 5, '%s', 2000.31,         null), -- не макс reported_date для того же acc_serial_num
                                (5, 13, 5, '%s', 7000.31,         307), -- не макс reported_date для того же acc_serial_num
                                (5, 14, 5, '%s', 2000.31,         338), -- не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи
                                (5, 15, 5, '%s', 7000.31,         368), -- Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt
                                (5, 16, 5, '%s', 2000.31,         399), -- не макс reported_date для того же acc_serial_num
                                (5, 17, 5, '%s', 5000.31,         5), -- не макс reported_date для того же acc_serial_num
                                (6, 18, 6, '%s', 3000,            null), -- Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT)
                                (7, 19, 7, '%s', 5500,            90), -- не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда
                                (7, 20, 7, '%s', 3000,            null) -- Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt
                    """,
            calcLocalDateFromInitialDate("2025-05-31"),
            calcLocalDateFromInitialDate("2021-12-29"),
            calcLocalDateFromInitialDate("2022-01-28"),
            calcLocalDateFromInitialDate("2025-04-06"),
            calcLocalDateFromInitialDate("2025-01-14"),
            calcLocalDateFromInitialDate("2021-11-29"),
            calcLocalDateFromInitialDate("2025-12-06"),
            calcLocalDateFromInitialDate("2021-04-01"),
            calcLocalDateFromInitialDate("2021-05-01"),
            calcLocalDateFromInitialDate("2021-05-31"),
            calcLocalDateFromInitialDate("2021-07-01"),
            calcLocalDateFromInitialDate("2021-07-31"),
            calcLocalDateFromInitialDate("2021-08-04"),
            calcLocalDateFromInitialDate("2025-09-08"),
            calcLocalDateFromInitialDate("2025-09-09"),
            calcLocalDateFromInitialDate("2021-11-04"),
            calcLocalDateFromInitialDate("2022-01-04"),
            calcLocalDateFromInitialDate("2025-09-09"),
            calcLocalDateFromInitialDate("2025-09-09"),
            calcLocalDateFromInitialDate("2025-09-10"));

    private static final String FILL_RU_PAYMT = String.format("""
                    INSERT INTO RU_PAYMT VALUES
                    (1, 1,  null,  '%s'),
                    (2, 2,  30,    '%s'),
                    (3, 3,  null,  '%s'),
                    (3, 4,  100,   '%s'),
                    (4, 5,  10,    '%s'),
                    (5, 6,  10,    '%s'),
                    (6, 7,  90,    '%s'),
                    (6, 8,  80,    '%s'),
                    (7, 9,  null,  '%s'),
                    (8, 10, null,  '%s'),
                    (8, 11, 83,    '%s'),
                    (8, 12, 91,    '%s'),
                    (9, 13, 100,   '%s')
                    """,
            calcLocalDateFromInitialDate("2024-11-09"),
            calcLocalDateFromInitialDate("2024-11-09"),
            calcLocalDateFromInitialDate("2024-11-10"),
            calcLocalDateFromInitialDate("2024-11-15"),
            calcLocalDateFromInitialDate("2024-11-15"),
            calcLocalDateFromInitialDate("2023-11-15"),
            calcLocalDateFromInitialDate("2024-11-09"),
            calcLocalDateFromInitialDate("2024-10-09"),
            calcLocalDateFromInitialDate("2024-10-09"),
            calcLocalDateFromInitialDate("2024-10-09"),
            calcLocalDateFromInitialDate("2024-10-09"),
            calcLocalDateFromInitialDate("2024-10-09"),
            calcLocalDateFromInitialDate("2024-10-09"));


    private DataSource dataSource;
    private NamedParameterJdbcTemplate jdbcTemplate;
    private FlagsService2 flagsService;

    @BeforeEach
    void setUp() {
        dataSource = DataSourceBuilder.create()
                .url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=DB2;DATABASE_TO_LOWER=TRUE")
                .driverClassName("org.h2.Driver")
                .username("sa")
                .password("")
                .build();

        jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);

        jdbcTemplate.getJdbcTemplate().execute(RU_PASTDUEAREAR_DROP);
        jdbcTemplate.getJdbcTemplate().execute(RU_PAYMT_DROP);

        jdbcTemplate.getJdbcTemplate().execute(RU_PASTDUEAREAR_CREATE);
        jdbcTemplate.getJdbcTemplate().execute(RU_PAYMT_CREATE);

        flagsService = new FlagsService2(jdbcTemplate);
    }

    @Test
    public void testfindFlags() {
        jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PASTDUEARREAR);
        jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PAYMT);

        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() {
        jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PASTDUEARREAR);

        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() {
        jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PAYMT);

        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 static LocalDate calcLocalDateFromInitialDate(String date) {
        LocalDateTime now = LocalDateTime.now();
        long daysPassed = ChronoUnit.DAYS.between(INITIAL_DATE, now);
        return LocalDate.parse(date).plusDays(daysPassed);
    }
}


