package org.mte.numecoeval.calculs.steps;

import io.cucumber.java.fr.Alors;
import io.cucumber.java.fr.Soit;
import org.mte.numecoeval.calculs.domain.data.demande.DemandeCalculImpactEquipementPhysique;
import org.mte.numecoeval.calculs.domain.data.demande.OptionsCalcul;
import org.mte.numecoeval.calculs.domain.data.entree.EquipementPhysique;
import org.mte.numecoeval.calculs.domain.data.trace.DureeDeVie;
import org.mte.numecoeval.calculs.domain.exception.CalculImpactException;
import org.mte.numecoeval.calculs.domain.port.input.service.DureeDeVieEquipementPhysiqueService;
import org.mte.numecoeval.calculs.domain.port.input.service.impl.DureeDeVieEquipementPhysiqueServiceImpl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

import static org.junit.jupiter.api.Assertions.*;
import static org.mte.numecoeval.calculs.CucumberIntegrationTest.FORMATTER_FRENCH_FORMAT;

public class DureeDeVieStepDefinitions {

    DemandeCalculImpactEquipementPhysique demandeCalculCourante;

    @Soit("Un équipement physique {string} avec la date d'achat {string} et la date de retrait {string}")
    public void soitEquipementPhysiqueAvecDateAchatEtDateRetrait(String nomEquipementPhysique, String dateAchat, String dateRetrait) {
        demandeCalculCourante = DemandeCalculImpactEquipementPhysique.builder()
                .dateCalcul(LocalDateTime.now())
                .equipementPhysique(
                        EquipementPhysique.builder()
                                .nomEquipementPhysique(nomEquipementPhysique)
                                .dateAchat(LocalDate.parse(dateAchat, FORMATTER_FRENCH_FORMAT))
                                .dateRetrait(LocalDate.parse(dateRetrait, FORMATTER_FRENCH_FORMAT))
                                .build()
                )
                .optionsCalcul(new OptionsCalcul("REEL"))
                .build();
    }

    @Soit("Un équipement physique {string} avec la date d'achat {string}")
    public void soitEquipementPhysiqueAvecDateAchat(String nomEquipementPhysique, String dateAchat) {
        demandeCalculCourante = DemandeCalculImpactEquipementPhysique.builder()
                .dateCalcul(LocalDateTime.now())
                .equipementPhysique(
                        EquipementPhysique.builder()
                                .nomEquipementPhysique(nomEquipementPhysique)
                                .dateAchat(LocalDate.parse(dateAchat, FORMATTER_FRENCH_FORMAT))
                                .dateRetrait(null)
                                .build()
                )
                .optionsCalcul(new OptionsCalcul("REEL"))
                .build();
    }

    @Alors("la durée de vie de l'équipement est {double}")
    public void checkDureeDeVie(Double dureeDeVieAttendue) {

        DureeDeVieEquipementPhysiqueService service = new DureeDeVieEquipementPhysiqueServiceImpl();
        DureeDeVie result = assertDoesNotThrow(() -> service.calculerDureeVie(demandeCalculCourante));

        assertNotNull(result);
        assertEquals(dureeDeVieAttendue, result.getValeurRetenue());
    }

    @Alors("la durée de vie correspond à la différence entre la date d'achat et la date du jour")
    public void checkDureeDeVieDateDuJour() {

        DureeDeVieEquipementPhysiqueService service = new DureeDeVieEquipementPhysiqueServiceImpl();
        DureeDeVie result = assertDoesNotThrow(() -> service.calculerDureeVie(demandeCalculCourante));

        assertNotNull(result);
        double differenceAvecDateDuJour = ChronoUnit.DAYS.between(demandeCalculCourante.getEquipementPhysique().getDateAchat(), LocalDate.now()) / 365d;
        assertEquals(differenceAvecDateDuJour, result.getValeurRetenue());
    }

    @Soit("Une demande de calcule de durée de vie tel que")
    public void initDemandeCalculDureeDeVie(DemandeCalculImpactEquipementPhysique demande) {
        demandeCalculCourante = demande;
    }

    @Alors("le calcul de durée de vie lève une erreur de type {string} avec le message {string}")
    public void checkErreurCalculDureeDeVie(String typeErreur, String messageErreur) {
        DureeDeVieEquipementPhysiqueService service = new DureeDeVieEquipementPhysiqueServiceImpl();
        CalculImpactException result = assertThrows(CalculImpactException.class, () -> service.calculerDureeVie(demandeCalculCourante));

        assertNotNull(result);
        assertEquals(typeErreur, result.getErrorType());
        assertEquals(messageErreur, result.getMessage());
    }
}
