package de.accso.flexinale.besucherportal.api_in.event;

import de.accso.flexinale.FlexinaleDistributedApplicationTestIntegrated;
import de.accso.flexinale.backoffice.api_out.event.FilmPublisher;
import de.accso.flexinale.backoffice.api_out.event.KinoPublisher;
import de.accso.flexinale.backoffice.api_out.event.VorfuehrungPublisher;
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.backoffice.domain.model.Vorfuehrung;
import de.accso.flexinale.besucherportal.api_out.event.GutscheinEinloesenBeauftragtPublisher;
import de.accso.flexinale.besucherportal.api_contract.event.GutscheinEinloesenBeauftragtEvent;
import de.accso.flexinale.besucherportal.api_contract.event.model.GutscheinEinloesenAuftragTO;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
import de.accso.flexinale.common.shared_kernel.Versionable;
import org.junit.jupiter.api.BeforeEach;
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.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import static de.accso.flexinale.shared_code_for_test.TestDataBuilder.createFilm;
import static de.accso.flexinale.shared_code_for_test.TestDataBuilder.createKinoWithKinosaele;
import static org.assertj.core.api.Assertions.assertThat;

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

    @Autowired
    KinoPublisher kinoPublisher;

    @Autowired
    VorfuehrungPublisher vorfuehrungPublisher;

    @Autowired
    FilmPublisher filmPublisher;

    @Autowired
    KontingentSubscriber kontingentSubscriber;

    @Autowired
    private GutscheinEinloesenBeauftragtPublisher gutscheinEinloesenBeauftragtPublisher;

    Vorfuehrung vorfuehrung1;
    Vorfuehrung vorfuehrung2;

    @BeforeEach
    void prepareVorfuehrungenAnKontingente() {
        // arrange for all tests
        Film film = createFilm();
        Collection<Film> newFilms = new ArrayList<>();
        newFilms.add(film);
        filmPublisher.publishNewFilms(newFilms);

        Kino kino = createKinoWithKinosaele();

        // kino has 3 kinosaele
        Iterator<KinoSaal> kinoSaalIterator = kino.kinoSaele.iterator();

        KinoSaal kinoSaal1 = kinoSaalIterator.next();
        kinoSaal1.anzahlPlaetze = new KinoSaal.AnzahlPlaetze(120);
        KinoSaal kinoSaal2 = kinoSaalIterator.next();
        kinoSaal2.anzahlPlaetze = new KinoSaal.AnzahlPlaetze(99);

        Collection<Kino> newKinos = new ArrayList<>();
        newKinos.add(kino);
        kinoPublisher.publishNewKinos(newKinos);

        vorfuehrung1 = new Vorfuehrung(Identifiable.Id.of(), new Vorfuehrung.Zeit(LocalDateTime.now()), film, kinoSaal1);
        vorfuehrung2 = new Vorfuehrung(Identifiable.Id.of(), new Vorfuehrung.Zeit(LocalDateTime.now()), film, kinoSaal2);

        Collection<Vorfuehrung> newVorfuehrungen = List.of(vorfuehrung1, vorfuehrung2);

        vorfuehrungPublisher.publishNewVorfuehrungen(newVorfuehrungen);
    }

    @Test
    void testRestKontingentOnlineWhenVorfuehrungInVerkaufGeben() {
        // act
        int restKontingentOnlineForVorfuehrung1 = kontingentSubscriber.getCache().getRestkontingentOnline(vorfuehrung1.id());
        int restKontingentOnlineForVorfuehrung2 = kontingentSubscriber.getCache().getRestkontingentOnline(vorfuehrung2.id());

        // assert
        assertThat(restKontingentOnlineForVorfuehrung1).isEqualTo(120/3);
        assertThat(restKontingentOnlineForVorfuehrung2).isEqualTo(99/3);
    }

    @Test
    void testRestKontingentOnlineWhenTicketsKaufen() {
        // arrange
        GutscheinEinloesenAuftragTO gutschein2 = new GutscheinEinloesenAuftragTO(Identifiable.Id.of(), Versionable.initialVersion(),
                vorfuehrung1.film.id(), vorfuehrung1.id(), Identifiable.Id.of(),
                new GutscheinEinloesenAuftragTO.AnzahlTickets(2));
        gutscheinEinloesenBeauftragtPublisher.post(GutscheinEinloesenBeauftragtEvent.class,
                new GutscheinEinloesenBeauftragtEvent(gutschein2));

        GutscheinEinloesenAuftragTO gutschein5 = new GutscheinEinloesenAuftragTO(Identifiable.Id.of(), Versionable.initialVersion(),
                vorfuehrung2.film.id(), vorfuehrung2.id(), Identifiable.Id.of(),
                new GutscheinEinloesenAuftragTO.AnzahlTickets(5));
        gutscheinEinloesenBeauftragtPublisher.post(GutscheinEinloesenBeauftragtEvent.class,
                new GutscheinEinloesenBeauftragtEvent(gutschein5));

        // act
        int restKontingentOnlineForVorfuehrung1 = kontingentSubscriber.getCache().getRestkontingentOnline(vorfuehrung1.id());
        int restKontingentOnlineForVorfuehrung2 = kontingentSubscriber.getCache().getRestkontingentOnline(vorfuehrung2.id());

        // assert
        assertThat(restKontingentOnlineForVorfuehrung1).isEqualTo(120/3 - 2);
        assertThat(restKontingentOnlineForVorfuehrung2).isEqualTo(99/3 - 5);
    }
}
