package de.accso.flexinale.api_in.web;

import de.accso.flexinale.domain.model.Ticket;
import de.accso.flexinale.domain.model.Vorfuehrung;

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

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

class TicketSorter {

    /**
     * return a list of list of tickets, sorted by time (inner list) and date (outer list).
     * each list contains the tickets for a day
     */
    static List<List<VorfuehrungMitAnzahlTicketsTO>> sortAndMapTicketsPerDay(final List<Ticket> allTickets) {
        if (allTickets == null || allTickets.isEmpty()) {
            return new ArrayList<>();
        }

        // 1) tickets by day
        List<List<Ticket>> ticketsProTag = new ArrayList<>();

        LocalDate previousDate = LocalDate.MIN; // far past :-)
        for (Ticket ticket : allTickets) {
            LocalDate currentDate = ((LocalDateTime) getRawOrNull(ticket.vorfuehrung.zeit)).toLocalDate();

            if (currentDate.equals(previousDate)) {
                ticketsProTag.getLast().add(ticket);
            }
            else {
                List<Ticket> tickets = new ArrayList<>();
                tickets.add(ticket);
                ticketsProTag.add(tickets);
            }
            previousDate = currentDate;
        }

        // 2) inner structure: Vorfuehrung and tickets
        List<List<VorfuehrungMitAnzahlTicketsTO>> vorfuehrungUndAnzahlTicketsFuerVorfuehrungProTag = new ArrayList<>();

        for (List<Ticket> ticketsForOneDay : ticketsProTag) {
            List<VorfuehrungMitAnzahlTicketsTO> vorfuehrungenUndAnzahlTicketsFuerEinenTag = new ArrayList<>();
            int anzahlTicketsFuerVorfuehrung = 0;
            Vorfuehrung previousVorfuehrung = ticketsForOneDay.getFirst().vorfuehrung;

            for (Ticket ticket : ticketsForOneDay) {
                // inside each day, vorfuehrungen are ordered by zeit.
                // There is never more than one vorfuehrung at a zeit for a user.
                // So tickets for the same vorfuehrung are all in a row - that's why the following works.
                Vorfuehrung currentVorfuehrung = ticket.vorfuehrung;

                if (currentVorfuehrung.equals(previousVorfuehrung)) {
                    anzahlTicketsFuerVorfuehrung++;
                }
                else {
                    // add the information for previous Vorfuehrung
                    VorfuehrungMitAnzahlTicketsTO to = mapVorfuehrungToTO(
                            anzahlTicketsFuerVorfuehrung, previousVorfuehrung);
                    vorfuehrungenUndAnzahlTicketsFuerEinenTag.add(to);
                    anzahlTicketsFuerVorfuehrung = 1; // ... we also have a new ticket for the next Vorfuehrung
                }

                previousVorfuehrung = currentVorfuehrung;
            }

            // Also add the last Vorfuehrung an anzahlTickets when day is over
            VorfuehrungMitAnzahlTicketsTO to = mapVorfuehrungToTO(
                    anzahlTicketsFuerVorfuehrung, previousVorfuehrung);
            vorfuehrungenUndAnzahlTicketsFuerEinenTag.add(to);
            vorfuehrungUndAnzahlTicketsFuerVorfuehrungProTag.add(vorfuehrungenUndAnzahlTicketsFuerEinenTag);
        }
        return vorfuehrungUndAnzahlTicketsFuerVorfuehrungProTag;
    }

    private static VorfuehrungMitAnzahlTicketsTO mapVorfuehrungToTO(final int anzahlTicketsFuerVorfuehrung,
                                                                    final Vorfuehrung previousVorfuehrung) {
        return new VorfuehrungMitAnzahlTicketsTO(
                new VorfuehrungMitAnzahlTicketsTO.Zeit(getRawOrNull(previousVorfuehrung.zeit)),
                previousVorfuehrung.film,
                previousVorfuehrung.kinoSaal,
                new VorfuehrungMitAnzahlTicketsTO.AnzahlTickets(anzahlTicketsFuerVorfuehrung)
        );
    }
}
