package de.brembs.kickplanner.controller;

import de.brembs.kickplanner.AppConfig;
import de.brembs.kickplanner.Kickplanner;
import de.brembs.kickplanner.controller.registration.PlayerRegistrationHandler;
import de.brembs.kickplanner.domain.Attendee;
import de.brembs.kickplanner.domain.Event;
import de.brembs.kickplanner.domain.Player;
import de.brembs.kickplanner.repository.KickRepository;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.UrlEncoded;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.ModelAndView;
import static spark.Spark.get;
import static spark.Spark.post;
import spark.template.freemarker.FreeMarkerEngine;

/**
 *
 * @author rmetzger
 */
public class WebsiteController {

    private static final Logger LOG = LoggerFactory.getLogger(WebsiteController.class);
    private final PlayerRegistrationHandler playerRegistrationHandler;

    public WebsiteController(KickRepository repository) {
        FreeMarkerEngine freeMarkerEngine = new FreeMarkerEngine();
        Configuration freeMarkerConfiguration = new Configuration();
        freeMarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(Kickplanner.class, "/static/templates/"));
        freeMarkerEngine.setConfiguration(freeMarkerConfiguration);
        this.playerRegistrationHandler = new PlayerRegistrationHandler(repository);

        get("/", (req, resp) -> {
            Event upcomingEvent = repository.getUpcomingEvent();
            if (upcomingEvent.getDate() == null) {
                // there is no upcoming event with a terminated date
                resp.status(301);
                Map<String, Object> attributes = new HashMap<>();
                attributes.put("app_version", repository.getAppVersionInfo());
                return getStatusModelAndView(attributes, "noevent.html");
            }

            upcomingEvent = repository.getEvent(upcomingEvent.getUuid().toString());
            Map<String, String> cookies = req.cookies();
            String registeredPlayerUuids = cookies.get(upcomingEvent.getUuid().toString());
            if (registeredPlayerUuids != null) {
                List<String> playerUuidList = Pattern.compile(":").splitAsStream(registeredPlayerUuids).collect(Collectors.toList());
                List<Attendee> allAttendees = new ArrayList<>(upcomingEvent.getAttendingPlayers());
                allAttendees.addAll(upcomingEvent.getNonAttendingPlayers());
                allAttendees.addAll(upcomingEvent.getUnsurePlayers());
                allAttendees.stream().filter(a -> playerUuidList.contains(a.getUuid().toString())).forEach(a -> a.setDeletable(Boolean.TRUE));
            }
            LOG.info("Got upcoming Event " + upcomingEvent.getUuid() + " for date " + upcomingEvent.getDateAsString());

            Map<String, Object> attributes = new HashMap<>();
            attributes.put("app_version", repository.getAppVersionInfo());
            attributes.put("location_render", AppConfig.getConfig().getRenderLocation());
            attributes = getEventAttributes(attributes, upcomingEvent, repository.getEvents());
            resp.status(200);
            return getStatusModelAndView(attributes, "index.html");
        }, freeMarkerEngine);

        post("/register", (req, resp) -> {
            MultiMap<String> params = new MultiMap<>();
            UrlEncoded.decodeTo(req.body(), params, "UTF-8");

            String playerName = params.getString("player");
            if (playerName != null) {
                playerName = playerName.trim();
                if (playerName.isEmpty()) {
                    resp.status(301);
                    resp.redirect("/");
                    return "Redirect";
                }
            }

            UUID playerUuid = null;
            Player player = repository.getPlayerByName(playerName);
            if (player == null) {
                playerUuid = repository.createPlayer(playerName);
                player = repository.getPlayer(playerUuid.toString());
            } else {
                playerUuid = player.getUuid();
            }

            Boolean option = null;
            if (params.containsKey("optIn")) {
                option = Boolean.TRUE;
            } else if (params.containsKey("optOut")) {
                option = Boolean.FALSE;
            }

            /*
            repository.setPlayerStatus(playerUuid.toString(), params.getString("eventId"), option, params.getString("comment"));
            Event event = repository.getEvent(repository.getUpcomingEvent().getUuid().toString());
            PushHandler pushHandler = new PushHandler(repository);
            pushHandler.sendPushMessage(new PushMessage("Kickplanner Sofortnachricht", playerName + (option ? " hat sich eingetragen." : " hat sich abgemeldet.") + "\nZwischenstand " + event.getDayAsString() + "\nZusagen " + event.getAttendingPlayersCount() + " Absagen " + event.getNonAttendingPlayersCount(), "../img/" + (option ? "thumbs-up.png" : "thumbs-down.png"), null));
             */
            this.playerRegistrationHandler.registerPlayerForEvent(player, params.getString("eventId"), option, params.getString("comment"));

            Map<String, String> cookies = req.cookies();
            String registeredPlayerUuids = cookies.get(params.getString("eventId"));
            if (registeredPlayerUuids == null) {
                registeredPlayerUuids = playerUuid.toString();
            } else {
                List<String> playerUuidList = Pattern.compile(":").splitAsStream(registeredPlayerUuids).collect(Collectors.toList());
                if (!playerUuidList.contains(playerUuid.toString())) {
                    registeredPlayerUuids = registeredPlayerUuids + ":" + playerUuid.toString();
                }
            }
            resp.cookie(params.getString("eventId"), registeredPlayerUuids, 691200);
            resp.status(301);
            resp.redirect("/");
            return "Redirect";
        });

        post("/remove", (req, resp) -> {
            MultiMap<String> params = new MultiMap<>();
            UrlEncoded.decodeTo(req.body(), params, "UTF-8");

            String playerUuid = params.getString("delplayer");
            String eventUuid = params.getString("delevent");

            repository.deletePlayerStatus(playerUuid, eventUuid);

            resp.status(301);
            resp.redirect("/#attendees");
            return "Redirect";
        });
    }

    private static Map<String, Object> getEventAttributes(Map<String, Object> attributes, Event event, List<Event> closedEvents) {
        attributes.put("event", event);
        attributes.put("average_attendee_count",
                Math.round(closedEvents.stream()
                        .filter(e -> !Objects.equals(e, event))
                        .mapToInt(e -> e.getAttendingPlayersCount())
                        .average().orElse(0.0)));
        if (closedEvents != null && closedEvents.size() > 5) {
            closedEvents = closedEvents.subList(0, 5);
        }
        attributes.put("closed_events", closedEvents);
        return attributes;
    }

    private static ModelAndView getStatusModelAndView(Map<String, Object> attributes, String template) {
        LOG.info("Return new ModelAndView ");
        if (template == null) {
            template = "index.html";
        }
        return new ModelAndView(attributes, template);
    }
}
