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

import eu.jonahbauer.wizard.client.cli.Client;
import eu.jonahbauer.wizard.client.cli.commands.SessionCommand;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.server.*;
import lombok.Getter;
import lombok.Setter;
import picocli.CommandLine.Model.CommandSpec;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

@Getter
public final class Session extends BaseState {
    private static final CommandSpec COMMAND_SPEC = CommandSpec.forAnnotatedObject(new SessionCommand(null, null));

    private final UUID self;
    private final UUID session;
    private final String secret;
    private final Map<UUID, PlayerData> players = new HashMap<>();

    private boolean ready;
    @Setter
    private Boolean nextReady;

    public Session(SessionJoinedMessage joined) {
        this.self = joined.getPlayer();
        this.session = joined.getSession();
        this.ready = false;
        this.secret = joined.getSecret();
        joined.getPlayers().forEach(player -> players.put(player.getUuid(), player));
    }

    public Session(UUID self, UUID session, String secret, boolean ready, Map<UUID, PlayerData> players) {
        this.self = self;
        this.session = session;
        this.secret = secret;
        this.players.putAll(players);
        this.ready = ready;
    }

    @Override
    public Optional<ClientState> onEnter(Client client) {
        if (players.size() - 1 == 1) {
            client.printfln("Successfully joined session %s. There is one other player.", session);
        } else {
            client.printfln("Successfully joined session %s. There are %d other players.", session, players.size() - 1);
        }
        client.printfln("You are %s. Your secret is %s", self, secret);
        return super.onEnter(client);
    }

    @Override
    public Optional<ClientState> onMessage(Client client, ServerMessage message) {
        if (message instanceof PlayerJoinedMessage join) {
            var player = join.getPlayer();
            players.put(player.getUuid(), player);
            client.printfln("Player \"%s\" joined the session.", player.getName());
            return Optional.empty();
        } else if (message instanceof PlayerLeftMessage leave) {
            var player = players.remove(leave.getPlayer());
            client.printfln("Player \"%s\" left the session.", player.getName());
            return Optional.empty();
        } else if (message instanceof PlayerModifiedMessage modified) {
            var player = modified.getPlayer();
            players.put(player.getUuid(), player);
            client.printfln("Player \"%s\" was modified.", player.getName());
            return Optional.empty();
        } else if (message instanceof StartingGameMessage) {
            return Optional.of(new Game(
                    self,
                    session,
                    secret,
                    players.values().stream().collect(Collectors.toMap(PlayerData::getUuid, PlayerData::getName))
            ));
        } else if (nextReady != null && message instanceof NackMessage nack) {
            client.println("Error: " + nack.getMessage());
            nextReady = null;
            client.ready();
            return Optional.empty();
        } else if (nextReady != null && message instanceof AckMessage) {
            ready = nextReady;
            nextReady = null;
            client.ready();
            return Optional.empty();
        } else {
            return unexpectedMessage(client, message);
        }
    }

    @Override
    public Object getCommand() {
        return SessionCommand.class;
    }
}
