package de.accso.flexinale.ticketing.application.services;


import de.accso.flexinale.FlexinaleDistributedApplicationTestIntegrated;
import de.accso.flexinale.backoffice.api_contract.event.model.KinoTO;
import de.accso.flexinale.backoffice.api_contract.event.model.VorfuehrungTO;
import de.accso.flexinale.backoffice.api_out.event.mapper.KinoKinoSaal2KinoKinoSaalTOMapper;
import de.accso.flexinale.backoffice.api_out.event.mapper.Vorfuehrung2VorfuehrungTOMapper;
import de.accso.flexinale.backoffice.domain.model.Film;
import de.accso.flexinale.backoffice.domain.model.Kino;
import de.accso.flexinale.backoffice.domain.model.KinoSaal;
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.shared_code_for_test.TestDataBuilder;
import de.accso.flexinale.ticketing.domain.dao.KontingentDao;
import de.accso.flexinale.ticketing.domain.dao.TicketDao;
import de.accso.flexinale.ticketing.domain.model.Kontingent;
import de.accso.flexinale.ticketing.domain.model.Ticket;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static de.accso.flexinale.common.shared_kernel.RawWrapper.getRawOrNull;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(classes = {FlexinaleDistributedApplicationTestIntegrated.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@ActiveProfiles("test-integrated")
@ExtendWith(SpringExtension.class)
@DoNotCheckInArchitectureTests
public class VorfuehrungUpdatedTest {

    @Autowired
    TicketService ticketService;

    @Autowired
    private TicketDao ticketDao;

    @Autowired
    private KontingentDao kontingentDao;

    @AfterEach
    void cleanupDatabase() {
        ticketDao.deleteAll();
        kontingentDao.deleteAll();
    }

    @Test
    void testAktualisiereVorfuehrung(){
        // arrange
        Film film = TestDataBuilder.createFilm();
        Kino kino = TestDataBuilder.createKinoWithKinosaele();
        KinoSaal kinoSaal = kino.kinoSaele.iterator().next();
        kinoSaal.anzahlPlaetze = new KinoSaal.AnzahlPlaetze(120);

        VorfuehrungTO vorfuehrung = Vorfuehrung2VorfuehrungTOMapper.map(
                TestDataBuilder.createVorfuehrung(LocalDateTime.now(), film, kinoSaal));

        // act
        ticketService.gebeVorfuehrungInDenVerkauf(vorfuehrung.id(), getRawOrNull(vorfuehrung.kinoSaal().anzahlPlaetze()));
        Identifiable.Id besucherId = Identifiable.Id.of();
        ticketService.loeseGutscheineOnlineFuerVorfuehrungEin(vorfuehrung.id(), vorfuehrung.film().id(),
                besucherId, 10);
        Optional<Kontingent> kontingentOptional = kontingentDao.findByVorfuehrung(vorfuehrung.id());
        List<Ticket> tickets = ticketService.findByBesucher(besucherId);

        // assert
        assertThat(tickets.size()).isEqualTo(10);
        assertThat(kontingentOptional.get().getRestKontingentOnline().raw()).isEqualTo(120/3 -10);

        // arrange - aktualisiere Vorfuehrung
        Kino newKino = TestDataBuilder.createKinoWithKinosaele();
        KinoSaal newKinoSaal = kino.kinoSaele.iterator().next();
        newKinoSaal.anzahlPlaetze = new KinoSaal.AnzahlPlaetze(30);

        newKino.kinoSaele = Set.of(newKinoSaal);
        KinoTO mappedKino = KinoKinoSaal2KinoKinoSaalTOMapper.mapKino(newKino);

        VorfuehrungTO changedVorfuehrung = new VorfuehrungTO(vorfuehrung.id(), vorfuehrung.version(), vorfuehrung.zeit(),
                vorfuehrung.film(), mappedKino.kinoSaele().iterator().next(), mappedKino.id());

        // act
        ticketService.aktualisiereVorfuehrung(changedVorfuehrung.id(), getRawOrNull(changedVorfuehrung.kinoSaal().anzahlPlaetze()));

        Optional<Kontingent> changedKontingentOptional = kontingentDao.findByVorfuehrung(vorfuehrung.id());
        List<Ticket> changedTickets = ticketService.findByBesucher(besucherId);

        // assert
        assertThat(changedKontingentOptional.get().getRestKontingentOnline().raw()).isEqualTo(30/3);

        assertThat(changedTickets.size()).isEqualTo(10);
        // ... but all tickets not gueltig anymore
        assertThat(changedTickets.stream().filter(t -> getRawOrNull(t.gueltig)).collect(Collectors.toList())).isEmpty();
     }
}
