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

import eu.jonahbauer.wizard.client.libgdx.Client;
import eu.jonahbauer.wizard.client.libgdx.screens.*;
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.messages.server.*;
import eu.jonahbauer.wizard.common.model.Configuration;
import org.jetbrains.annotations.NotNull;

import java.util.*;

import static eu.jonahbauer.wizard.client.libgdx.state.AwaitingJoinSession.Source.*;

public final class Lobby extends BaseState {
    private final Map<UUID, SessionData> sessions = new HashMap<>();

    private LobbyScreen lobbyScreen;
    
    public Lobby(Collection<SessionData> list) {
        list.forEach(s -> sessions.put(s.getUuid(), s));
    }
    
    @Override
    public Optional<ClientState> onEnter(Client client) {
        if (!client.isError()) showListScreen(client);
        return super.onEnter(client);
    }

    @Override
    public Optional<ClientState> onErrorDismissed(Client client) {
        showListScreen(client);
        return Optional.empty();
    }

    @Override
    public Optional<ClientState> onMessage(Client client, ServerMessage message) {
        if (message instanceof SessionCreatedMessage created) {
            var session = created.getSession();
            sessions.put(session.getUuid(), session);
            if (lobbyScreen != null) {
                lobbyScreen.addSession(session);
            }
            return Optional.empty();
        } else if (message instanceof SessionRemovedMessage removed) {
            var session = removed.getSession();
            sessions.remove(session);
            if (lobbyScreen != null) {
                lobbyScreen.removeSession(session);
            }
            return Optional.empty();
        } else if (message instanceof SessionModifiedMessage modified) {
            var session = modified.getSession();
            sessions.put(session.getUuid(), session);
            if (lobbyScreen != null) {
                lobbyScreen.modifySession(session);
            }
            return Optional.empty();
        } else if (message instanceof SessionListMessage list) {
            list.getSessions().forEach(s -> sessions.put(s.getUuid(), s));
            if (lobbyScreen != null) {
                lobbyScreen.setSessions(list.getSessions().toArray(new SessionData[0]));
            }
            return Optional.empty();
        } else {
            return unexpectedMessage(client, message);
        }
    }

    public Optional<ClientState> disconnect(@SuppressWarnings("unused") Client client) {
        return Optional.of(new Menu());
    }

    public Optional<ClientState> createSession(Client client, @NotNull String sessionName, @NotNull Configuration config, long timeout, @NotNull String playerName) {
        client.send(new CreateSessionMessage(sessionName, playerName, timeout, config));
        return Optional.of(new AwaitingJoinSession(null, sessionName, config, CREATE));
    }

    public Optional<ClientState> joinSession(Client client, UUID sessionUUID, String playerName) {
        var session = sessions.get(sessionUUID);
        if (session != null) {
            client.send(new JoinSessionMessage(sessionUUID, playerName));
            return Optional.of(new AwaitingJoinSession(session.getUuid(), session.getName(), session.getConfiguration(), JOIN));
        } else {
            throw new IllegalArgumentException("Session does not exist.");
        }
    }

    public Optional<ClientState> rejoinSession(Client client, @NotNull UUID sessionUUID, @NotNull UUID playerUUID, @NotNull String secret) {
        var session = sessions.get(sessionUUID);
        if (session != null) {
            client.send(new RejoinMessage(sessionUUID, playerUUID, secret));
            return Optional.of(new AwaitingJoinSession(sessionUUID, session.getName(), session.getConfiguration(), REJOIN));
        } else {
            throw new IllegalArgumentException("Session does not exist.");
        }
    }

    public Optional<ClientState> showCreateScreen(Client client) {
        var game = client.getGame();
        lobbyScreen = null;
        game.setScreen(new CreateGameScreen(game));
        return Optional.empty();
    }

    public Optional<ClientState> showListScreen(Client client) {
        var game = client.getGame();
        lobbyScreen = new LobbyScreen(game);
        game.setScreen(lobbyScreen);
        lobbyScreen.setSessions(sessions.values().toArray(SessionData[]::new));
        return Optional.empty();
    }

    public Optional<ClientState> showRejoinScreen(Client client) {
        var game = client.getGame();
        lobbyScreen = null;
        game.setScreen(new RejoinScreen(game));
        return Optional.empty();
    }
}