package de.accso.flexinale.backoffice.infrastructure.persistence;

import de.accso.flexinale.FlexinaleModulith2ComponentsApplication;
import de.accso.flexinale.backoffice.domain.dao.FilmDao;
import de.accso.flexinale.backoffice.domain.dao.KinoDao;
import de.accso.flexinale.backoffice.domain.dao.KinoSaalDao;
import de.accso.flexinale.backoffice.domain.dao.VorfuehrungDao;
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.common.shared_kernel.DoNotCheckInArchitectureTests;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import org.assertj.core.api.Assertions;
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.junit.jupiter.SpringExtension;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Optional;
import java.util.stream.Collectors;

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

@SpringBootTest(classes = {FlexinaleModulith2ComponentsApplication.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@ExtendWith(SpringExtension.class)
@DoNotCheckInArchitectureTests
public class FKKsVPersistenceTest {

    @Autowired
    private FilmDao filmDao;

    @Autowired
    private KinoDao kinoDao;

    @Autowired
    private KinoSaalDao kinoSaalDao;

    @Autowired
    private VorfuehrungDao vorfuehrungDao;

    @Test
    void saveAndDeleteFilm() {
        // arrange
        Film film = createFilm();

        // act
        filmDao.save(film);

        // assert
        Assertions.assertThat(filmDao.findById(film.id())).isNotEmpty();

        // cleanup
        filmDao.deleteById(film.id());

        // assert
        Assertions.assertThat(filmDao.findById(film.id())).isEmpty();
    }

    @Test
    void saveAndDeleteKinoAndKinoSaele() {
        // arrange
        Kino kino = createKinoWithKinosaele();
        ArrayList<String> kinoSaeleIds = kino.kinoSaele.stream().map(it -> it.id().id()).collect(
                Collectors.toCollection(ArrayList::new));

        // act
        kinoDao.save(kino);

        // assert
        Assertions.assertThat(kinoDao.findById(kino.id())).isNotEmpty();

        // cleanup
        kinoDao.deleteById(kino.id());

        // assert
        Assertions.assertThat(kinoDao.findById(kino.id())).isEmpty();
        for (String kinoSaeleId: kinoSaeleIds) {
            Assertions.assertThat(kinoSaalDao.findById(Identifiable.Id.of(kinoSaeleId))).isEmpty();
        }
    }

    @Test
    void saveAndDeleteVorfuehrungAndKontingent() {
        // arrange
        Film film = createFilm();
        filmDao.save(film);

        Kino kino = createKinoWithKinosaele();
        kino = kinoDao.save(kino);

        LocalDateTime now = LocalDateTime.now();
        KinoSaal kinoSaal = kino.kinoSaele.stream().findFirst().get();
        Vorfuehrung vorfuehrung = createVorfuehrung(now, film, kinoSaal);

        // act
        vorfuehrungDao.save(vorfuehrung);

        // assert
        Optional<Vorfuehrung> optionalSearchedVorfuehrung = vorfuehrungDao.findById(vorfuehrung.id());
        assertThat(optionalSearchedVorfuehrung).isNotEmpty();

        Vorfuehrung searchedVorfuehrung = optionalSearchedVorfuehrung.get();
        LocalDateTime searchedVorfuehrungZeit = getRawOrNull(searchedVorfuehrung.zeit);
        assertThat(searchedVorfuehrungZeit.getDayOfYear()).isEqualTo(now.getDayOfYear());
        assertThat(searchedVorfuehrungZeit.getMinute()).isEqualTo(now.getMinute());

        // cleanup
        vorfuehrungDao.deleteById(vorfuehrung.id());
        kinoDao.deleteById(kino.id());
        filmDao.deleteById(film.id());

        // assert
        assertThat(vorfuehrungDao.findById(vorfuehrung.id())).isEmpty();
    }
}
