package org.mte.numecoeval.calculs;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mte.numecoeval.calculs.domain.data.entree.Application;
import org.mte.numecoeval.calculs.domain.data.entree.EquipementVirtuel;
import org.mte.numecoeval.calculs.domain.data.indicateurs.*;
import org.mte.numecoeval.calculs.domain.model.CalculEquipementPhysique;
import org.mte.numecoeval.calculs.domain.model.CalculMessagerie;
import org.mte.numecoeval.calculs.infrastructure.repository.*;
import org.mte.numecoeval.calculs.infrastructure.service.calcul.IntegrationCalculEquipementsService;
import org.mte.numecoeval.calculs.infrastructure.service.calcul.IntegrationCalculMessagerieService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;

import javax.sql.DataSource;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;

@SpringBootTest
@ActiveProfiles(profiles = {"test"})
@Slf4j
@EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class, DataSourceAutoConfiguration.class, KafkaAutoConfiguration.class})
class ApiEventCalculsApplicationTests {


    @Autowired
    IntegrationCalculEquipementsService integrationCalculEquipementsService;
    @Autowired
    IntegrationCalculMessagerieService integrationCalculMessagerieService;

    @MockBean
    EquipementPhysiqueRepository equipementPhysiqueRepository;
    @MockBean
    EquipementVirtuelRepository equipementVirtuelRepository;
    @MockBean
    ApplicationRepository applicationRepository;
    @MockBean
    MessagerieRepository messagerieRepository;
    @MockBean
    DataSource dataSource;

    private static final String RESOURCES = "src/test/resources";
    private static final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());

    @MockBean
    IndicateurRepository indicateurRepository;

    ArgumentCaptor<List<ImpactEquipementPhysique>> captorImpactEqPh = ArgumentCaptor.forClass((Class) List.class);
    ArgumentCaptor<List<ImpactEquipementVirtuel>> captorImpactEqV = ArgumentCaptor.forClass((Class) List.class);
    ArgumentCaptor<List<ImpactApplication>> captorImpactApp = ArgumentCaptor.forClass((Class) List.class);
    ArgumentCaptor<List<ImpactReseau>> captorImpactReseau = ArgumentCaptor.forClass((Class) List.class);

    ArgumentCaptor<List<ImpactMessagerie>> captorImpactMsg = ArgumentCaptor.forClass((Class) List.class);

    @Test
    void testIntegrationCalculEquipementsService() throws IOException {

        assertNotNull(ApiEventCalculsApplication.class);

        // check null case
        integrationCalculEquipementsService.calculImpactEquipementPhysique(null);

        /* DEFINE INPUT DATA */
        var calculEquipementPhysique = mapper.readValue(
                Files.readString(Path.of(RESOURCES).resolve("input").resolve("equipementPhysique.json")),
                CalculEquipementPhysique.class
        );

        // mock equipements virtuels
        List<EquipementVirtuel> equipementVirtuelList = List.of(EquipementVirtuel.builder()
                .id(79302L)
                .nomEquipementVirtuel("virtual-eq-1001-1")
                .nomEquipementPhysique("physical-eq-srv-1001")
                .cluster("PY1ORA01")
                .nomLot("lot1")
                .dateLot(LocalDate.of(2023, 10, 26))
                .nomOrganisation("org")
                .typeEqv("calcul")
                .vCPU(4)
                .build());
        Mockito.when(equipementVirtuelRepository.findEquipementVirtuels(any(), any(), any())).thenReturn(equipementVirtuelList);

        // mock applications
        List<Application> applicationList = List.of(Application.builder()
                .nomApplication("application-1001-1-1")
                .typeEnvironnement("Production")
                .nomEquipementVirtuel("virtual-eq-1001-1")
                .nomEquipementPhysique("physical-eq-srv-1001")
                .domaine("Domain 1")
                .sousDomaine("Sub Domain 1")
                .nomLot("lot1")
                .dateLot(LocalDate.of(2023, 10, 26))
                .nomOrganisation("org")
                .nomEntite("Entite de test")
                .build());
        Mockito.when(applicationRepository.findApplications(any(), any(), any(), any())).thenReturn(applicationList);

        /* EXECUTE */
        integrationCalculEquipementsService.calculImpactEquipementPhysique(calculEquipementPhysique);

        /* CAPTURE Impacts lists */
        Mockito.verify(indicateurRepository, times(1)).saveIndicateursEquipements(captorImpactEqPh.capture(), captorImpactEqV.capture(), captorImpactApp.capture(), captorImpactReseau.capture());

        List<ImpactEquipementPhysique> actualImpactEqPh = captorImpactEqPh.getValue();
        List<ImpactEquipementVirtuel> actualImpactEqV = captorImpactEqV.getValue();
        List<ImpactApplication> actualImpactApp = captorImpactApp.getValue();
        List<ImpactReseau> actualImpactReseau = captorImpactReseau.getValue();

        /* Check EXPECTED */
        Assertions.assertEquals(4, actualImpactEqPh.size());
        Assertions.assertEquals(4, actualImpactEqV.size());
        Assertions.assertEquals(4, actualImpactApp.size());
        Assertions.assertEquals(4, actualImpactReseau.size());

        /* Check all statutIndicateur = OK */
        // Assertions.assertTrue(actualImpactEqPh.stream().allMatch(i -> "OK".equals(i.getStatutIndicateur())));
        // Assertions.assertTrue(actualImpactEqV.stream().allMatch(i -> "OK".equals(i.getStatutIndicateur())));
        // Assertions.assertTrue(actualImpactApp.stream().allMatch(i -> "OK".equals(i.getStatutIndicateur())));
        // Assertions.assertTrue(actualImpactReseau.stream().allMatch(i -> "OK".equals(i.getStatutIndicateur())));
    }


    @Test
    void testIntegrationCalculMessagerieService() throws IOException {
        // check null case
        integrationCalculMessagerieService.calculImpactMessagerie(null);

        /* DEFINE INPUT DATA */
        var calculMessagerie = mapper.readValue(
                Files.readString(Path.of(RESOURCES).resolve("input").resolve("messagerie.json")),
                CalculMessagerie.class
        );

        integrationCalculMessagerieService.calculImpactMessagerie(calculMessagerie);

        /* CAPTURE Impacts lists */
        Mockito.verify(indicateurRepository, times(1)).saveIndicateursMessagerie(captorImpactMsg.capture());
        List<ImpactMessagerie> actualImpactMsg = captorImpactMsg.getValue();

        /* Check EXPECTED */
        Assertions.assertEquals(2, actualImpactMsg.size());

        /* Check all statutIndicateur = ERREUR */
        Assertions.assertTrue(actualImpactMsg.stream().allMatch(i -> "ERREUR".equals(i.getStatutIndicateur())));
    }
}
