package de.accso.flexinale.api_in.rest;

import de.accso.flexinale.domain.dao.FilmDao;
import de.accso.flexinale.domain.dao.KinoDao;
import de.accso.flexinale.domain.dao.KontingentDao;
import de.accso.flexinale.domain.dao.VorfuehrungDao;
import de.accso.flexinale.domain.model.*;
import de.accso.flexinale.infrastructure.security.BenutzerDao;
import de.accso.flexinale.infrastructure.security.BenutzerDetailsService;
import de.accso.flexinale.infrastructure.security.BenutzerEntity;
import de.accso.flexinale.shared_kernel.DoNotCheckInArchitectureTests;
import de.accso.flexinale.shared_code_for_test.TestDataBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.annotation.DirtiesContext;

import java.time.LocalDateTime;

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

@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
@DoNotCheckInArchitectureTests
class RestControllerTest {

    @Autowired
    BenutzerDao benutzerDao;

    @Autowired
    BenutzerDetailsService benutzerDetailsService;

    @Autowired
    FilmDao filmDao;

    @Autowired
    KinoDao kinoDao;

    @Autowired
    VorfuehrungDao vorfuehrungDao;

    @Autowired
    KontingentDao kontingentDao;

    @Autowired
    FilmRestController filmRestController;

    @Autowired
    KinoKinoSaalRestController kinoKinoSaalRestController;

    @Autowired
    VorfuehrungRestController vorfuehrungRestController;

    @Test
    void testSmokeIntegrationPersistenceAndRestForFilmTest() {
        // arrange
        createBenutzerCredentialsAndSetAuthenticationToken();

        Film film = TestDataBuilder.createFilm("my film", "my url", 99);

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

        // assert
        assertThat(savedFilm.equalsByContent(film)).isTrue();

        // act
        Film searchedFilm = filmRestController.film(film.id().id());

        // assert
        assertThat(searchedFilm.equalsByContent(film)).isTrue();
    }

    @Test
    void testSmokeIntegrationPersistenceAndRestForKinoKinoSaalTest() {
        // arrange
        createBenutzerCredentialsAndSetAuthenticationToken();

        Kino kino = TestDataBuilder.createKinoWithKinosaele(999);
        KinoSaal kinoSaal = kino.kinoSaele.iterator().next();

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

        // assert
        assertThat(savedKino.equalsByContent(kino)).isTrue();

        // act
        Kino searchedKino = kinoKinoSaalRestController.kino(kino.id().id());

        // assert
        assertThat(searchedKino.equalsByContent(kino)).isTrue();
        assertThat(searchedKino.kinoSaele).hasSize(1);
        for (KinoSaal searchedKinoSaal : searchedKino.kinoSaele) {
            assertThat(searchedKinoSaal.equalsByContent(kinoSaal)).isTrue();
        }
    }

    @Test
    void testSmokeIntegrationPersistenceAndRestForVorfuehrungAndKontingentTest() {
        // arrange
        createBenutzerCredentialsAndSetAuthenticationToken();

        Film film = TestDataBuilder.createFilm("my film", "my url", 99);
        Kino kino = TestDataBuilder.createKinoWithKinosaele(999);
        KinoSaal kinoSaal = kino.kinoSaele.iterator().next();

        Kontingent kontingent = TestDataBuilder.createKontingent(33, 33, 33);
        Vorfuehrung vorfuehrung = TestDataBuilder.createVorfuehrung(LocalDateTime.now(), film, kinoSaal, kontingent);

        // act
        Film savedFilm = filmDao.save(film);
        Kino savedKino = kinoDao.save(kino);
        Vorfuehrung savedVorfuehrung = vorfuehrungDao.save(vorfuehrung); // saves also Kontingent because of Cascade

        // assert
        assertThat(savedFilm.equalsByContent(film)).isTrue();
        assertThat(savedKino.equalsByContent(kino)).isTrue();
        assertThat(savedVorfuehrung.equalsByContent(vorfuehrung)).isTrue();
        assertThat(savedVorfuehrung.getKontingent().equalsByContent(kontingent)).isTrue();

        // act
        Vorfuehrung searchedVorfuehrung = vorfuehrungRestController.vorfuehrung(vorfuehrung.id().id());

        // assert
        assertThat(searchedVorfuehrung.equalsByContent(vorfuehrung)).isTrue();
    }

    // ------------------------------------------------------------------------------------------------------------

    private void createBenutzerCredentialsAndSetAuthenticationToken() {
        // arrange
        BenutzerEntity benutzer = TestDataBuilder.createBenutzerEntity();
        benutzer.encryptAndSetPassword("test");

        // act
        benutzerDao.save(benutzer);

        UserDetails userDetails = benutzerDetailsService.loadUserByUsername (benutzer.login);
        Authentication authToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(),
                userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authToken);
    }
}