package de.accso.flexinale.data.persistence;

import de.accso.flexinale.FlexinaleMonolithApplication;
import de.accso.flexinale.data.model.*;
import de.accso.flexinale.shared_code_for_test.TestDataBuilder;
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.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(classes = {FlexinaleMonolithApplication.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@ExtendWith(SpringExtension.class)
public class BasicPersistenceTest {

    @Autowired
    private BenutzerDao besucherDao;

    @Autowired
    private KinoDao kinoDao;

    @Autowired
    private KinoSaalDao kinoSaalDao;

    @Autowired
    private FilmDao filmDao;

    @Autowired
    private VorfuehrungDao vorfuehrungDao;

    @Autowired
    private TicketDao ticketDao;

    @Test
    void saveAndDeleteBesucher() {
        // arrange
        Benutzer besucher = TestDataBuilder.createBenutzer();

        // act
        besucher = besucherDao.save(besucher);

        // assert
        assertThat(besucherDao.findById(besucher.id)).isNotEmpty();

        // cleanup
        besucherDao.deleteById(besucher.id);

        // assert
        assertThat(besucherDao.findById(besucher.id)).isEmpty();
    }

    @Test
    void findBesucherByLogin() {
        // arrange
        Benutzer besucher = TestDataBuilder.createBenutzer();

        // act
        besucher = besucherDao.save(besucher);

        // assert
        assertThat(besucherDao.findByLogin(besucher.login)).isNotEmpty();

        // cleanup
        besucherDao.deleteById(besucher.id);

        // assert
        assertThat(besucherDao.findById(besucher.id)).isEmpty();
    }

    @Test
    void findBesucherByName() {
        // arrange
        Benutzer besucher = TestDataBuilder.createBenutzer();

        // act
        besucher = besucherDao.save(besucher);

        // assert
        assertThat(besucherDao.findByName(besucher.name)).isNotEmpty();

        // cleanup
        besucherDao.deleteById(besucher.id);

        // assert
        assertThat(besucherDao.findById(besucher.id)).isEmpty();
    }

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

        // act
        film = filmDao.save(film);

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

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

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

    @Test
    void saveAndDeleteKinoAndKinoSaele() {
        // arrange
        Kino kino = TestDataBuilder.createKinoWithKinosaele();
        List<String> kinoSaeleIds = kino.kinoSaele.stream().map(it -> it.id).toList();

        // act
        kino = kinoDao.save(kino);

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

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

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

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

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

        LocalDateTime now = LocalDateTime.now();
        Vorfuehrung vorfuehrung = TestDataBuilder.createVorfuehrung(now, film, kino.kinoSaele.iterator().next());

        // act
        vorfuehrung = vorfuehrungDao.save(vorfuehrung);

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

        Vorfuehrung vorfuehrung1 = vorfuehrungOptional.get();
        assertThat(vorfuehrung1.zeit.getDayOfYear()).isEqualTo(now.getDayOfYear());
        assertThat(vorfuehrung1.zeit.getMinute()).isEqualTo(now.getMinute());

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

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

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

        Kino kino = TestDataBuilder.createKinoWithKinosaele(100, 110, 120);
        kino = kinoDao.save(kino);

        Vorfuehrung vorfuehrung = TestDataBuilder.createVorfuehrung(LocalDateTime.now(), film, kino.kinoSaele.iterator().next());
        vorfuehrungDao.save(vorfuehrung);

        Benutzer besucher = TestDataBuilder.createBenutzer();
        besucherDao.save(besucher);

        Ticket ticket = TestDataBuilder.createTicket(film, vorfuehrung, besucher, Ticket.VerkaufsKanal.KINOKASSE);

        // act
        ticket = ticketDao.save(ticket);

        // assert
        Optional<Ticket> ticketOptional = ticketDao.findById(ticket.id);
        assertThat(ticketOptional).isNotEmpty();

        // cleanup
        ticketDao.deleteById(ticket.id);
        vorfuehrungDao.deleteById(vorfuehrung.id);
        kinoDao.deleteById(kino.id);
        filmDao.deleteById(film.id);
        besucherDao.deleteById(besucher.id);

        // assert
        assertThat(ticketDao.findById(ticket.id)).isEmpty();
    }
}
