package pp.monopoly.client;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.TextField;

import pp.dialog.Dialog;
import pp.dialog.DialogBuilder;
import pp.dialog.SimpleDialog;

/**
 * Represents a dialog for setting up a network connection in the Monopoly game.
 * Allows users to specify the host and port for connecting to a game server.
 */
class NetworkDialog extends SimpleDialog {
    private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName());
    private static final String LOCALHOST = "localhost";
    private static final String DEFAULT_PORT = "1234";
    private final NetworkSupport network;
    private final TextField host = new TextField(LOCALHOST);
    private final TextField port = new TextField(DEFAULT_PORT);
    private String hostname;
    private int portNumber;
    private Future<Object> connectionFuture;
    private Dialog progressDialog;

    /**
     * Constructs a new NetworkDialog.
     *
     * @param network The NetworkSupport instance to be used for network operations.
     */
    NetworkDialog(NetworkSupport network) {
        super(network.getApp().getDialogManager());
        this.network = network;
        initializeDialog();
    }

    /**
     * Initializes the dialog with input fields and connection buttons.
     */
    private void initializeDialog() {
        final MonopolyApp app = network.getApp();
        Container inputContainer = new Container();

        // Titel und Eingabefelder für Host und Port
        inputContainer.addChild(new Label("Server-Adresse"));
        inputContainer.addChild(host);

        inputContainer.addChild(new Label("Port"));
        inputContainer.addChild(port);

        Button connectButton = inputContainer.addChild(new Button("Verbinden"));
        connectButton.addClickCommands(source -> connect());

        Button cancelButton = inputContainer.addChild(new Button("Abbrechen"));
        cancelButton.addClickCommands(source -> app.closeApp());

        app.getGuiNode().attachChild(inputContainer);
    }

    /**
     * Initiates the connection attempt based on the entered host and port.
     */
    private void connect() {
        LOGGER.log(Level.INFO, "Connecting to host={0}, port={1}", host, port);
        try {
            hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
            portNumber = Integer.parseInt(port.getText());
            openProgressDialog();
            connectionFuture = network.getApp().getExecutor().submit(this::initNetwork);
        } catch (NumberFormatException e) {
            network.getApp().errorDialog("Port muss eine Zahl sein.");
        }
    }

    /**
     * Opens a progress dialog while connecting.
     */
    private void openProgressDialog() {
        progressDialog = DialogBuilder.simple(network.getApp().getDialogManager())
                                      .setText("Verbinde zum Server...")
                                      .build();
        progressDialog.open();
    }

    /**
     * Attempts to initialize the network connection.
     *
     * @throws RuntimeException If an error occurs when creating the client.
     */
    private Object initNetwork() {
        try {
            network.initNetwork(hostname, portNumber);
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Updates the connection status and handles completion or failure.
     */
    @Override
    public void update(float delta) {
        if (connectionFuture != null && connectionFuture.isDone()) {
            try {
                connectionFuture.get();
                onSuccess();
            } catch (ExecutionException e) {
                onFailure(e.getCause());
            } catch (InterruptedException e) {
                LOGGER.log(Level.WARNING, "Connection interrupted.", e);
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * Handles a successful connection to the game server.
     */
    private void onSuccess() {
        connectionFuture = null;
        progressDialog.close();
        this.close();
        network.getApp().setInfoText("Warte auf einen Gegner...");
    }

    /**
     * Handles a failed connection attempt.
     *
     * @param e The cause of the failure.
     */
    private void onFailure(Throwable e) {
        connectionFuture = null;
        progressDialog.close();
        network.getApp().errorDialog("Verbindung zum Server fehlgeschlagen.");
        network.getApp().setInfoText(e.getLocalizedMessage());
    }
}
