package de.accso.flexinale.besucherportal.api_in.web;

import de.accso.flexinale.backoffice.api_contract.event.model.FilmTO;
import de.accso.flexinale.backoffice.api_contract.event.model.KinoSaalTO;
import de.accso.flexinale.backoffice.api_contract.event.model.KinoTO;
import de.accso.flexinale.backoffice.api_contract.event.model.VorfuehrungTO;
import de.accso.flexinale.besucherportal.application.services.FKKsVTInMemoryCache;
import de.accso.flexinale.besucherportal.application.services.FilmService;
import de.accso.flexinale.besucherportal.application.services.KinoService;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.common.shared_kernel.RawWrapper;
import de.accso.flexinale.security.api_contract.BesucherRetriever;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.server.ResponseStatusException;

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

import static de.accso.flexinale.common.shared_kernel.RawWrapper.getRawOrNull;

@Controller
@Profile({ "!testdata-backoffice & !testdata-ticketing" })
public class FilmWebController {

    private record VorfuehrungMitRestkontingentTO(Identifiable.Id vorfuehrungId, FilmTO film, Zeit zeit,
                                          KinoSaalTO kinoSaal, KinoTO kino, RestKontingent restkontingentOnline) {
        public record Zeit(LocalDateTime raw) implements RawWrapper<LocalDateTime> {
            public Zeit(LocalDateTime raw) {
                this.raw = raw.withNano(0); // precision is second
            }
        }

        public record RestKontingent(Integer raw) implements RawWrapper<Integer> {}
    }

    @Autowired
    private FilmService filmService;

    @Autowired
    private KinoService kinoService;

    @Autowired
    private BesucherRetriever besucherRetriever;

    @Autowired
    private FKKsVTInMemoryCache cache;

    @GetMapping(value="/filme")
    @PreAuthorize("hasRole('ROLE_BESUCHER')")
    @SuppressWarnings("SameReturnValue")
    public String filme(final Model model) {
        List<FilmTO> filme = filmService.filme();
        model.addAttribute("filme", filme);
        return "filme";
    }

    @GetMapping(value="/film/{id}")
    @PreAuthorize("hasRole('ROLE_BESUCHER')")
    @SuppressWarnings("SameReturnValue")
    public String film(@PathVariable("id") final String id, final Model model) {
        Identifiable.Id filmId = Identifiable.Id.of(id);

        FilmTO film = filmService.film(filmId);
        if (film == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "film %s not available".formatted(id));
        }
        else {
            model.addAttribute("film", film);
            List<VorfuehrungTO> vorfuehrungenOfFilm = filmService.vorfuehrungenFuer(filmId);

            List<VorfuehrungMitRestkontingentTO> vorfuehrungenMitRestkontingent = vorfuehrungenOfFilm
                    .stream()
                    .map(v -> getRestkontingentOnlineAndMap(v, film))
                    .collect(Collectors.toList());

            model.addAttribute("vorfuehrungenMitRestkontingent", vorfuehrungenMitRestkontingent);

            if (!vorfuehrungenOfFilm.isEmpty()) {
                Identifiable.Id idOfLoggedInBesucher = besucherRetriever.getIdOfLoggedInBesucher();
                List<String> vorfuehrungenMitUeberlapp =
                        filmService.vorfuehrungenMitUeberlapp(vorfuehrungenOfFilm, idOfLoggedInBesucher);
                model.addAttribute("vorfuehrungenMitUeberlapp", vorfuehrungenMitUeberlapp);
                List<String> vorfuehrungenMitTicket =
                        filmService.vorfuehrungenFuerDieDerBenutzerEinTicketHat(vorfuehrungenOfFilm, idOfLoggedInBesucher);
                model.addAttribute("vorfuehrungenMitTicket", vorfuehrungenMitTicket);
            }
        }

        return "film";
    }

    private VorfuehrungMitRestkontingentTO getRestkontingentOnlineAndMap(final VorfuehrungTO vorfuehrung,
                                                                         final FilmTO film) {
        VorfuehrungMitRestkontingentTO.RestKontingent restKontingentOnline =
                new VorfuehrungMitRestkontingentTO.RestKontingent(cache.getRestkontingentOnline(vorfuehrung.id()));
        KinoTO kino = kinoService.kino(vorfuehrung.kinoId());

        return new VorfuehrungMitRestkontingentTO(vorfuehrung.id(), film,
                new VorfuehrungMitRestkontingentTO.Zeit(getRawOrNull(vorfuehrung.zeit())),
                vorfuehrung.kinoSaal(), kino, restKontingentOnline);
    }
}
