package de.accso.flexinale.api_in.web;

import de.accso.flexinale.FlexinaleModulith1OnionApplication;
import de.accso.flexinale.domain.dao.FilmDao;
import de.accso.flexinale.domain.dao.KinoDao;
import de.accso.flexinale.domain.dao.VorfuehrungDao;
import de.accso.flexinale.domain.model.*;
import de.accso.flexinale.domain.services.TicketService;
import de.accso.flexinale.infrastructure.security.BenutzerDao;
import de.accso.flexinale.infrastructure.security.mapper.BenutzerEntity2BesucherMapper;
import de.accso.flexinale.shared_kernel.DoNotCheckInArchitectureTests;
import de.accso.flexinale.shared_code_for_test.TestDataBuilder;
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.junit.jupiter.SpringExtension;

import java.time.LocalDateTime;
import java.util.List;

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

@SpringBootTest(classes = {FlexinaleModulith1OnionApplication.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@ExtendWith(SpringExtension.class)
@DoNotCheckInArchitectureTests
class TicketSortingTest {

    @Autowired
    TicketService ticketService;

    @Autowired
    private BenutzerDao benutzerDao;

    @Autowired
    private FilmDao filmDao;

    @Autowired
    private KinoDao kinoDao;

    @Autowired
    private VorfuehrungDao vorfuehrungDao;

    private Besucher besucher;

    @BeforeEach
    void prepareTickets() {
        // arrange for all tests

        Film film = TestDataBuilder.createFilm();
        filmDao.save(film);

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

        KinoSaal kinoSaal = kino.kinoSaele.iterator().next();
        LocalDateTime beginnVorfuehrung1 = LocalDateTime.of(2022, 11, 29, 16, 0);

        Vorfuehrung vorfuehrung1 = TestDataBuilder.createVorfuehrung(beginnVorfuehrung1, film, kinoSaal, 33);
        Vorfuehrung vorfuehrung2 = TestDataBuilder.createVorfuehrung(beginnVorfuehrung1.plusHours( 3), film, kinoSaal,33);
        Vorfuehrung vorfuehrung3 = TestDataBuilder.createVorfuehrung(beginnVorfuehrung1.plusHours(24), film, kinoSaal,33);
        vorfuehrungDao.save(vorfuehrung1);
        vorfuehrungDao.save(vorfuehrung2);
        vorfuehrungDao.save(vorfuehrung3);

        besucher = TestDataBuilder.createBesucher();
        benutzerDao.save(BenutzerEntity2BesucherMapper.map(besucher));

        ticketService.loeseGutscheineOnlineFuerVorfuehrungEin(vorfuehrung1.id(), besucher, 2);
        ticketService.loeseGutscheineOnlineFuerVorfuehrungEin(vorfuehrung2.id(), besucher, 3);
        ticketService.loeseGutscheineOnlineFuerVorfuehrungEin(vorfuehrung3.id(), besucher, 4);
    }

    @Test
    void testSortAndMapTicketsPerDay() {
        List<Ticket> tickets = ticketService.findByBesucherOrderByZeit(besucher);
        assertThat(tickets.size()).isEqualTo(9);

        List<List<VorfuehrungMitAnzahlTicketsTO>> vorfuehrungenTicketMap
                = TicketSorter.sortAndMapTicketsPerDay(tickets);

        // The outer list contains one list for each day. There are tickets for 2 different days
        assertThat(vorfuehrungenTicketMap.size()).isEqualTo(2);

        // Day 1: 2 Vorfuehrungen a 2 resp 3 tickets
        List<VorfuehrungMitAnzahlTicketsTO> vorfuehrungenOfDay1 = vorfuehrungenTicketMap.getFirst();

        assertThat(vorfuehrungenOfDay1.size()).isEqualTo(2);
        assertThat(vorfuehrungenOfDay1.get(0).zeit().raw()).isBefore(vorfuehrungenOfDay1.get(1).zeit().raw());

        assertThat(vorfuehrungenOfDay1.get(0).anzahlTickets().raw()).isEqualTo(2);
        assertThat(vorfuehrungenOfDay1.get(1).anzahlTickets().raw()).isEqualTo(3);

        // Day 2: 1 Vorfuehrung
        List<VorfuehrungMitAnzahlTicketsTO> vorfuehrungenOfDay2 = vorfuehrungenTicketMap.get(1);
        assertThat(vorfuehrungenOfDay2.size()).isEqualTo(1);
        assertThat(vorfuehrungenOfDay2.getFirst().anzahlTickets().raw()).isEqualTo(4);
    }
}