/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.velocitycore.commands;

import de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import de.steamwar.command.TypeValidator;
import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.PlayerChatter;
import de.steamwar.persistent.Subserver;
import de.steamwar.sql.*;
import de.steamwar.velocitycore.EventStarter;
import de.steamwar.velocitycore.SubserverSystem;

import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Linked
public class EventCommand extends SWCommand {

    public EventCommand() {
        super("event");
    }

    @Validator("noEvent")
    public TypeValidator<Chatter> noEventValidator() {
        return (sender, value, messageSender) -> Event.get() == null;
    }

    @Register
    public void noCurrentEvent(@Validator("noEvent") Chatter sender) {
        sender.system("EVENT_NO_CURRENT");

        List<Event> coming = Event.getComing();
        if (coming.isEmpty()) return;

        sender.system("EVENT_COMING");

        Instant now = Instant.now();
        DateTimeFormatter format = DateTimeFormatter.ofPattern(sender.parseToPlain("EVENT_DATE_FORMAT"));
        for (Event e : coming) {
            sender.prefixless("EVENT_COMING_EVENT", e.getStart().toLocalDateTime().format(format), e.getEnd().toLocalDateTime().format(format), e.getEventName());

            if (now.isBefore(e.getDeadline().toInstant())) {
                sender.prefixless("EVENT_COMING_DEADLINE", e.getDeadline());
            }

            SchematicType schemType = e.getSchematicType();
            if (schemType != null && schemType.getDeadline() != null && now.isBefore(schemType.getDeadline().toInstant())) {
                sender.prefixless("EVENT_COMING_SCHEM_DEADLINE", schemType.getDeadline());
            }

            Set<Team> teams = TeamTeilnahme.getTeams(e.getEventID());
            if (!teams.isEmpty()) {
                sender.prefixless("EVENT_COMING_TEAMS", teams.stream().map(team -> sender.parseToLegacy("EVENT_COMING_TEAM", team.getTeamColor(), team.getTeamKuerzel())).collect(Collectors.joining(" ")));
            }
        }
    }

    @Register
    public void eventOverview(@Validator(value = "noEvent", invert = true) Chatter sender) {
        sender.system("EVENT_USAGE");

        Event currentEvent = Event.get();
        sender.system("EVENT_CURRENT_EVENT", currentEvent.getEventName());

        DateTimeFormatter format = DateTimeFormatter.ofPattern(sender.parseToPlain("EVENT_TIME_FORMAT"));
        for (EventFight fight : EventFight.getEvent(currentEvent.getEventID())) {
            Team blue = Team.byId(fight.getTeamBlue());
            Team red = Team.byId(fight.getTeamRed());
            StringBuilder fline = new StringBuilder(sender.parseToLegacy("EVENT_CURRENT_FIGHT", fight.getStartTime().toLocalDateTime().format(format), blue.getTeamColor(), blue.getTeamKuerzel(), red.getTeamColor(), red.getTeamKuerzel()));

            if (fight.hasFinished()) {
                switch (fight.getErgebnis()) {
                    case 1:
                        fline.append(sender.parseToLegacy("EVENT_CURRENT_FIGHT_WIN", blue.getTeamColor(), blue.getTeamKuerzel()));
                        break;
                    case 2:
                        fline.append(sender.parseToLegacy("EVENT_CURRENT_FIGHT_WIN", red.getTeamColor(), red.getTeamKuerzel()));
                        break;
                    default:
                        fline.append(sender.parseToLegacy("EVENT_CURRENT_FIGHT_DRAW"));
                }
            }

            sender.prefixless("PLAIN_STRING", fline.toString());
        }
    }

    @Register
    public void eventWithTeam(@Validator(value = "noEvent", invert = true) PlayerChatter sender, @ErrorMessage("EVENT_NO_TEAM") Team team) {
        Subserver eventArena = EventStarter.getEventServer().get(team.getTeamId());
        if (eventArena == null || !Subserver.getServerList().contains(eventArena)) {
            sender.system("EVENT_NO_FIGHT_TEAM");
            return;
        }
        if (!PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer)) {
            SubserverSystem.sendPlayer(eventArena, sender.getPlayer());
        }
    }

    protected static TypeMapper<Team> eventTeam(Function<EventFight, List<Integer>> teamMapper) {
        return new TypeMapper<>() {
            @Override
            public Team map(Chatter sender, PreviousArguments previousArguments, String s) {
                if ("PUB".equalsIgnoreCase(s) || "public".equalsIgnoreCase(s)) {
                    return Team.byId(0);
                }
                return Team.get(s);
            }

            @Override
            public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
                Set<String> teams = new HashSet<>();
                Event currentEvent = Event.get();
                for (EventFight fight : EventFight.getEvent(currentEvent.getEventID())) {
                    if (fight.hasFinished()) continue;
                    teamMapper.apply(fight).forEach(integer -> {
                        Team team = Team.byId(integer);
                        teams.add(team.getTeamName());
                        teams.add(team.getTeamKuerzel());
                    });
                }
                return teams;
            }
        };
    }

    @ClassMapper(value = Team.class, local = true)
    public TypeMapper<Team> eventTeams() {
        return eventTeam(eventFight -> {
            if (eventFight.getStartTime().after(new Timestamp(System.currentTimeMillis()))) {
                return Collections.emptyList();
            }
            return List.of(eventFight.getTeamBlue(), eventFight.getTeamRed());
        });
    }
}