package testdata;

import de.accso.flexinale.FlexinaleModulith2ComponentsApplication;
import de.accso.flexinale.backoffice.application.services.FilmUploadService;
import de.accso.flexinale.backoffice.application.services.KinoUploadService;
import de.accso.flexinale.backoffice.application.services.KinoSaalUploadService;
import de.accso.flexinale.backoffice.application.services.VorfuehrungUploadService;
import de.accso.flexinale.backoffice.domain.dao.FilmDao;
import de.accso.flexinale.backoffice.domain.dao.KinoDao;
import de.accso.flexinale.backoffice.domain.dao.VorfuehrungDao;
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.application.PersistMode;
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.ticketing_api_contract.api_contract.Ticketing;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

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

@SpringBootTest(classes = {FlexinaleModulith2ComponentsApplication.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@ExtendWith(SpringExtension.class)
@DoNotCheckInArchitectureTests
@SuppressWarnings("NewClassNamingConvention")
public class FKKsVTestDataLoader {
    private static final String xlsDataFile = "/testdata/TestDataFilmKinoKinoSaalVorfuehrung.xlsx";

    @Autowired
    private KinoUploadService kinoUploadService;

    @Autowired
    private KinoSaalUploadService kinoSaalUploadService;

    @Autowired
    private FilmUploadService filmUploadService;

    @Autowired
    private VorfuehrungUploadService vorfuehrungUploadService;

    @Autowired
    private KinoDao kinoDao;

    @Autowired
    private FilmDao filmDao;

    @Autowired
    private VorfuehrungDao vorfuehrungDao;

    @Autowired
    private Ticketing ticketing;

    @Value("${spring.jpa.hibernate.ddl-auto}")
    private String springJpaHibernateDdlAuto;

    @Test
    void loadFilmeAndKinosAndVorfuehrungenTestDataAndPersistToDatabase() throws IOException {
        // All data in the database is automatically deleted because of the property
        //     spring.jpa.hibernate.ddl-auto=create
        // in the test application.properties
        assertThat(springJpaHibernateDdlAuto).isEqualTo("create");

        // Loading: order is important because of foreign key relations
        internal_loadFilmeTestDataAndPersistToDatabase(filmUploadService, filmDao);
        internal_loadKinosAndKinoSaeleTestDataAndPersistToDatabase(kinoSaalUploadService, kinoUploadService, kinoDao);
        internal_loadVorfuehrungenTestDataAndPersistToDatabase(vorfuehrungUploadService, vorfuehrungDao, ticketing);
    }

    static void internal_loadFilmeTestDataAndPersistToDatabase(final FilmUploadService filmUploadService,
                                                               final FilmDao filmDao) throws IOException {
        // arrange
        // act
        int numPersistedFilms = filmUploadService.
                loadDataFromExcelSheetAndPersist(xlsDataFile, PersistMode.UPDATE);

        // assert
        assertThat(filmDao.findAll()).hasSize(26);
        assertThat(numPersistedFilms).isEqualTo(26);
    }

    static void internal_loadKinosAndKinoSaeleTestDataAndPersistToDatabase(final KinoSaalUploadService kinoSaalUploadService,
                                                                           final KinoUploadService kinoUploadService,
                                                                           final KinoDao kinoDao) throws IOException {
        // arrange
        // act
        Collection<KinoSaal> kinoSaele = kinoSaalUploadService.loadDataFromExcelSheet(xlsDataFile);
        Map<Identifiable.Id, Collection<KinoSaal>> alleKinosUndIhreSaele =
                kinoSaalUploadService.mapKinoSaeleToKino(kinoSaele);

        // assert
        assertThat(alleKinosUndIhreSaele.keySet()).hasSize(10);
        alleKinosUndIhreSaele.keySet().forEach(kinoId -> {
            Collection<KinoSaal> saeleImKino = alleKinosUndIhreSaele.get(kinoId);
            assertThat(saeleImKino).hasSize(2);
            saeleImKino.forEach(kinoSaal -> {
                Integer anzahlPlaetze = getRawOrNull(kinoSaal.anzahlPlaetze);
                assertThat(anzahlPlaetze).isBetween(10, 100);
            });
        });


        // arrange
        // act
        kinoUploadService.beforeLoad(alleKinosUndIhreSaele);
        int numPersistedKinos = kinoUploadService.
                loadDataFromExcelSheetAndPersist(xlsDataFile, PersistMode.UPDATE);
        kinoUploadService.afterLoad();

        // assert
        assertThat(numPersistedKinos).isEqualTo(10);

        List<Kino> alleKinos = kinoDao.findAll();
        assertThat(alleKinos).hasSizeGreaterThanOrEqualTo(10);
        assertThat(alleKinos.size()).isGreaterThanOrEqualTo(numPersistedKinos);

        for (Identifiable.Id kinoId: alleKinosUndIhreSaele.keySet()) {
            Optional<Kino> optionalKino = kinoDao.findById(kinoId);
            assertThat(optionalKino.isEmpty()).isFalse();
            assertThat(optionalKino.get().kinoSaele).hasSize(2);
        }
    }

    static void internal_loadVorfuehrungenTestDataAndPersistToDatabase(final VorfuehrungUploadService vorfuehrungUploadService,
                                                                       final VorfuehrungDao vorfuehrungDao,
                                                                       final Ticketing ticketing) throws IOException {
        // arrange
        // act

        // first load all vorfuehrungen
        Collection<Vorfuehrung> vorfuehrungen = vorfuehrungUploadService.loadDataFromExcelSheet(xlsDataFile);
        // and advice ticketing (results in kontingente)

        for (Vorfuehrung vorfuehrung: vorfuehrungen) {
            try {
                ticketing.gebeVorfuehrungInDenVerkauf(vorfuehrung.id(), getRawOrNull(vorfuehrung.kinoSaal.anzahlPlaetze));
            }
            catch (IllegalStateException ex) {
                // do nothing... Kontingent already exists. We do not support any update of entites here
            }
        }

        int numPersistedVorfuehrungen = vorfuehrungUploadService.
                loadDataFromExcelSheetAndPersist(xlsDataFile, PersistMode.UPDATE);

        // assert
        assertThat(vorfuehrungDao.findAll()).hasSize(52);
        assertThat(numPersistedVorfuehrungen).isEqualTo(52);
    }
}
