package de.accso.flexinale.besucherportal.application.services;

import de.accso.flexinale.backoffice.api_contract.event.model.FilmTO;
import de.accso.flexinale.backoffice.api_contract.event.model.KinoTO;
import de.accso.flexinale.backoffice.api_contract.event.model.VorfuehrungTO;
import de.accso.flexinale.common.application.caching.InMemoryCache;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.ticketing.api_contract.event.model.TicketTO;

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

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

public final class FKKsVTInMemoryCache {

    // caches are currently never cleared - might want to use Cache implementation with automatic time-to-live
    private final InMemoryCache<Identifiable.Id, FilmTO> filmCache = new InMemoryCache<>();
    private final InMemoryCache<Identifiable.Id, KinoTO> kinoCache = new InMemoryCache<>();
    private final InMemoryCache<Identifiable.Id, VorfuehrungTO> vorfuehrungCache = new InMemoryCache<>();
    private final InMemoryCache<Identifiable.Id, TicketTO> ticketCache = new InMemoryCache<>();

    private final InMemoryCache<Identifiable.Id, Integer> vorfuehrung2RestKontingentOnlineCache = new InMemoryCache<>();

    public void addOrUpdate(final FilmTO film) {
        filmCache.put(film.id(), film);
    }

    public void delete(final FilmTO film) {
        filmCache.remove(film.id());
    }

    public FilmTO film(final Identifiable.Id id) {
        return filmCache.get(id);
    }

    public List<FilmTO> filme() {
        return filmCache.values().stream().toList();
    }

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

    public void addOrUpdate(final KinoTO kino) {
        kinoCache.put(kino.id(), kino);
    }

    public void delete(final KinoTO kino) {
        kinoCache.remove(kino.id());
    }


    public KinoTO kino(final Identifiable.Id id) {
        return kinoCache.get(id);
    }

    public List<KinoTO> kinos() {
        return kinoCache.values().stream().toList();
    }

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

    public void addOrUpdate(final VorfuehrungTO vorfuehrung) {
        vorfuehrungCache.put(vorfuehrung.id(), vorfuehrung);
    }

    public void delete(final VorfuehrungTO vorfuehrung) {
        vorfuehrungCache.remove(vorfuehrung.id());
    }

    public List<VorfuehrungTO> vorfuehrungen() {
        return vorfuehrungCache.values().stream().toList();
    }

    public VorfuehrungTO vorfuehrung(final Identifiable.Id id) {
        return vorfuehrungCache.get(id);
    }

    public List<VorfuehrungTO> vorfuehrungenByFilmId(final Identifiable.Id filmId) {
        return vorfuehrungCache.values()
                .stream()
                .filter(v -> v.film().id().equals(filmId))
                .toList();
    }

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

    public void addOrUpdate(final TicketTO ticket) {
        ticketCache.put(ticket.id(), ticket);
    }

    public List<TicketTO> tickets() {
        return ticketCache.values().stream().toList();
    }

    public List<TicketTO> ticketsByBesucher(final Identifiable.Id besucherId) {
        return ticketCache.values()
                .stream()
                .filter(t -> t.besucherId().equals(besucherId))
                .collect(Collectors.toList()); // don't use .toList() here as we need to sort the list later
    }

    public List<TicketTO> gueltigeTicketsByBesucher(final Identifiable.Id besucherId) {
        return ticketsByBesucher(besucherId)
                .stream()
                .filter((t -> getRawOrNull(t.gueltig())))
                .collect(Collectors.toList());
    }

    public int gesamtZahlDerTicketsFuer(final Identifiable.Id besucherId) {
        return (int) ticketCache.values()
                .stream()
                .filter(t -> t.besucherId().equals(besucherId))
                .filter((t -> getRawOrNull(t.gueltig())))
                .count();
    }

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

    public void addOrUpdate(final Identifiable.Id vorfuehrungId, final Integer onlineRestKontingent) {
        vorfuehrung2RestKontingentOnlineCache.put(vorfuehrungId, onlineRestKontingent);
    }

    public Integer getRestkontingentOnline(final Identifiable.Id vorfuehrungId) {
        Integer restKontingentFromCache = vorfuehrung2RestKontingentOnlineCache.get(vorfuehrungId);
        return (restKontingentFromCache != null) ? restKontingentFromCache : 0;
    }
}
