package eu.jonahbauer.wizard.client.cli.commands;

import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.state.AwaitingJoinSession;
import eu.jonahbauer.wizard.client.cli.state.Lobby;
import eu.jonahbauer.wizard.client.cli.state.Menu;
import eu.jonahbauer.wizard.common.messages.client.CreateSessionMessage;
import eu.jonahbauer.wizard.common.messages.client.JoinSessionMessage;
import eu.jonahbauer.wizard.common.messages.client.RejoinMessage;
import eu.jonahbauer.wizard.common.messages.data.SessionData;
import eu.jonahbauer.wizard.common.model.Configuration;
import lombok.Getter;
import org.ajbrown.namemachine.NameGenerator;
import org.java_websocket.framing.CloseFrame;
import org.jetbrains.annotations.NotNull;

import java.util.Iterator;
import java.util.UUID;

import static picocli.CommandLine.*;

@Command(name = "\b", subcommands = {HelpCommand.class, QuitCommand.class})
public class LobbyCommand {
    @Getter(lazy = true)
    private static final NameGenerator nameGenerator = new NameGenerator();

    private final Client client;
    private final Lobby lobby;

    public LobbyCommand(Client client, Lobby lobby) {
        this.client = client;
        this.lobby = lobby;
    }

    @Command(name = "list", description = "Shows a list of available sessions")
    public void list() {
        var sessions = lobby.getSessions();
        var count = sessions.size();
        if (count > 0) {
            StringBuilder builder = new StringBuilder();

            if (count == 1) builder.append("There is one open session:\n");
            else builder.append("There are ").append(count).append(" open sessions:\n");

            sessions.forEach((session) -> builder.append(session).append('\n'));
            client.println(builder);
        } else {
            client.println("No sessions.");
        }
    }

    @Command(name = "disconnect", description = "Disconnects from the server")
    public Menu disconnect() {
        client.getSocket().close(CloseFrame.GOING_AWAY);
        client.setSocket(null);
        return new Menu();
    }

    @Command(name = "join", description = "Joins the specified session")
    public AwaitingJoinSession join(
            @Parameters(index = "0", paramLabel = "<session>", description = "session uuid", completionCandidates = SessionCompleter.class) UUID session,
            @Option(names = "--name", description = "user name") String userName
    ) {
        if (userName == null) {
            userName = getNameGenerator().generateName().getFirstName();
            client.println("Your name will be " + userName + ".");
        }

        client.send(new JoinSessionMessage(session, userName));
        return new AwaitingJoinSession();
    }

    @Command(name = "create")
    public AwaitingJoinSession create(
            @Parameters(index = "0", paramLabel = "<session>", description = "human readable session name") String sessionName,
            @Option(names = "--name", description = "user name") String userName,
            @Option(names = "--configuration", description = "game configuration", defaultValue = "DEFAULT") Configuration configuration,
            @Option(names = "--timeout", description = "interaction timeout", defaultValue = "60000") long timeout
    ) {
        if (userName == null) {
            userName = getNameGenerator().generateName().getFirstName();
            client.println("Your name will be " + userName + ".");
        }

        client.send(new CreateSessionMessage(sessionName, userName, timeout, configuration));
        return new AwaitingJoinSession();
    }

    @Command(name = "rejoin")
    public AwaitingJoinSession rejoin(
            @Parameters(index = "0", paramLabel = "<session>", description = "session uuid", completionCandidates = SessionCompleter.class) UUID session,
            @Parameters(index = "1", paramLabel = "<player>", description = "player uuid") UUID player,
            @Parameters(index = "2", paramLabel = "<secret>", description = "player secret") String secret
    ) {
        client.send(new RejoinMessage(session, player, secret));
        return new AwaitingJoinSession();
    }

    public static class SessionCompleter implements Iterable<String> {
        private final Lobby lobby;

        private SessionCompleter(Lobby lobby) {
            this.lobby = lobby;
        }

        @NotNull
        @Override
        public Iterator<String> iterator() {
            return lobby.getSessions().stream().map(SessionData::getUuid).map(UUID::toString).iterator();
        }
    }
}
