package de.accso.flexinale.backoffice.api_in.rest;

import de.accso.flexinale.backoffice.api_out.event.FilmPublisher;
import de.accso.flexinale.backoffice.api_out.event.VorfuehrungPublisher;
import de.accso.flexinale.backoffice.application.services.FilmUploadService;
import de.accso.flexinale.backoffice.domain.model.Film;
import de.accso.flexinale.backoffice.domain.model.Vorfuehrung;
import de.accso.flexinale.backoffice.application.services.FilmService;
import de.accso.flexinale.backoffice.application.services.VorfuehrungService;
import de.accso.flexinale.common.application.PersistMode;
import de.accso.flexinale.common.application.PersistedEntityAndResult;
import de.accso.flexinale.common.shared_kernel.FlexinaleIllegalStateException;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

import static de.accso.flexinale.common.application.PersistedEntityAndResult.PersistedResult.ADDED;
import static de.accso.flexinale.common.application.PersistedEntityAndResult.PersistedResult.UPDATED;

@RestController
@Profile({ "!testdata-besucherportal & !testdata-ticketing" })
public class FilmRestController {
    private static final Logger LOGGER = LoggerFactory.getLogger(FilmRestController.class);

    @Autowired
    private FilmService filmService;

    @Autowired
    private FilmUploadService filmUploadService;

    @Autowired
    private FilmPublisher filmPublisher;

    @Autowired
    private VorfuehrungService vorfuehrungService;

    @Autowired
    private VorfuehrungPublisher vorfuehrungPublisher;

    @GetMapping(value = "/rest/filme", produces = "application/json")
    @PreAuthorize("hasRole('ROLE_BESUCHER')")
    public List<Film> filme() {
        return filmService.filme();
    }

    @GetMapping(value = "/rest/film/{id}", produces = "application/json")
    @PreAuthorize("hasRole('ROLE_BESUCHER')")
    public Film film(@PathVariable final String id) {
        Identifiable.Id filmId = Identifiable.Id.of(id);
        Film film = filmService.film(filmId);
        if (film == null) {
            throw new RestResourceNotFoundException("no Film %s found.".formatted(id));
        }

        return film;
    }

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

    ResponseEntity<RestResponseMessage> loadAndPersistAndPublishNewFilme(
            final BufferedInputStream stream, final String originalFilename) throws IOException
    {
        // load and persist
        Collection<PersistedEntityAndResult<Film>> addedAndUpdatedFilms =
                filmUploadService.loadDataFromExcelSheetAndPersistAndReturn(stream, PersistMode.UPDATE);

        List<Film> addedFilms = addedAndUpdatedFilms.stream()
                .filter(filmWithResult -> filmWithResult.result().equals(ADDED))
                .map(PersistedEntityAndResult::entity)
                .toList();
        List<Film> updatedFilms = addedAndUpdatedFilms.stream()
                .filter(filmWithResult -> filmWithResult.result().equals(UPDATED))
                .map(PersistedEntityAndResult::entity)
                .toList();

        publishFilmsAndVorfuehrungen(addedFilms, updatedFilms);

        String message = "Uploaded the Excel file %s adding %d new Film entities and updating %d existing Film entities."
                .formatted(originalFilename, addedFilms.size(), updatedFilms.size());

        RestUploadResult restUploadResult = new RestUploadResult(HttpStatus.OK, message);
        return ResponseEntity.status(restUploadResult.status())
                .body(new RestResponseMessage(restUploadResult.message()));
    }

    private void publishFilmsAndVorfuehrungen(final List<Film> addedFilms, final List<Film> updatedFilms) {
        filmPublisher.publishNewFilms(addedFilms);
        filmPublisher.publishUpdatedFilms(updatedFilms);

        List<Vorfuehrung> vorfuehrungen = vorfuehrungService.vorfuehrungen();
        for (Film updatedFilm: updatedFilms) {
            List<Vorfuehrung> vorfuehrungenToPublishAsUpdated = vorfuehrungen.stream()
                    .filter(vorfuehrung -> vorfuehrung.film.id.equals(updatedFilm.id))
                    .toList();
            vorfuehrungPublisher.publishUpdatedVorfuehrungen(vorfuehrungenToPublishAsUpdated);
        }
    }

    @PostMapping("/rest/filme")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public ResponseEntity<RestResponseMessage> uploadFilme(@RequestParam("file") final MultipartFile file) {
        try (BufferedInputStream stream = new BufferedInputStream(file.getInputStream())) {
            if (!ExcelHelper.isExcelFile(stream)) {
                String message = "Please upload a valid Excel file!";
                throw new FlexinaleIllegalStateException(message);
            }

            return loadAndPersistAndPublishNewFilme(stream, file.getOriginalFilename());
        }
        catch (Exception ex) {
            String message = "Could not upload the Excel file: %s: %s".formatted(file.getOriginalFilename(), ex.getMessage());
            RestBadRequestException exception = new RestBadRequestException(message, ex);
            LOGGER.error(message, exception);

            throw exception;
        }
    }
}
