package dmo.fs.db.wsnext.hib;

import dmo.fs.db.wsnext.DbConfiguration;
import dmo.fs.quarkus.Server;
import dmo.fs.utils.ColorUtilConstants;
import dmo.fs.utils.DodexUtil;
import dmo.fs.utils.ParseQueryUtilHelper;
import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.arc.properties.StringValueMatch;
import io.quarkus.websockets.next.*;
import io.vertx.core.buffer.Buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Serial;
import java.io.Serializable;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Locale;
import java.util.stream.Collectors;

/*
        For 'production' and to run as default, uncomment line below and set 'dodex.default.db'
        in application.properties to 'mssql' or 'oracle'.
        See 'dodex-quarkus/dodex-handicap/build.gradle' for database and handicap configuration
        using 'quarkusDev'.
   */
//@IfBuildProperty(name = "dodex.default.db", stringValue = "oracle|mssql", match = StringValueMatch.REGEX)
@IfBuildProperty(name = "USE_HANDICAP", stringValue = "true", enableIfMissing = true)
@IfBuildProperty(name = "DEFAULT_DB", stringValue = "oracle|mssql", match = StringValueMatch.REGEX,
        enableIfMissing = true)
@WebSocket(path = "/dodex")
public class HibernateReactiveRouter extends DodexHibernateReactiveBase implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    protected static final Logger logger = LoggerFactory.getLogger(HibernateReactiveRouter.class.getName());
    DodexHibernateConfig dodexHibernateConfig;

    public HibernateReactiveRouter() throws IOException, SQLException, InterruptedException {
        String mode = Server.isProduction ? "prod" : "dev";

        Locale.setDefault(Locale.US);
        DodexUtil.setEnv(mode);

        dodexHibernateConfig = DbConfiguration.getDefaultDb();
        dodexHibernateConfig.configDatabase();
        sessionFactory = dodexHibernateConfig.getSessionFactory();
    }

    @OnOpen()
    public void onOpen() throws SQLException, IOException, InterruptedException {
        queryParams = connection.handshakeRequest().query().transform(q -> {
            String queryString = URLDecoder.decode(q, StandardCharsets.UTF_8);
            return ParseQueryUtilHelper.getQueryMap(queryString);
        });

        sessionsNext.put(connection.id(), queryParams);

        logger.info("{}{}{}",
                ColorUtilConstants.BLUE_BOLD_BRIGHT, queryParams.get("handle"), ColorUtilConstants.RESET);
        broadcast(connection, "User " + queryParams.get("handle") + " joined", queryParams);

        if (ke != null) {
            ke.setValue("sessions", connection.getOpenConnections().size());
        }
        setup();

        doConnection(connection);
        queryParams.put("remoteAddress", remoteAddress);
    }

    @OnTextMessage()
    public void onMessage(String message) {
        sessions = connection.getOpenConnections()
                .stream().collect(Collectors.toConcurrentMap(WebSocketConnection::id, v -> v));

        doMessage(connection, sessions, message);
    }

    @OnPongMessage
    void pong(Buffer data) {
        logger.debug("Pong received: {}", data);
    }

    @OnClose
    public void onClose() {
        String handle = sessionsNext.get(connection.id()).get("handle");
        if (logger.isInfoEnabled()) {
            logger.info("{}Closing ws-connection to client: {}{}", ColorUtilConstants.BLUE_BOLD_BRIGHT,
                    handle, ColorUtilConstants.RESET);
        }

        sessionsNext.remove(connection.id());
        connection.broadcast().sendText("User " + handle + " left").subscribe().asCompletionStage();
        if (ke != null) {
            ke.setValue("sessions", connection.getOpenConnections().size());
        }
    }

    @OnError
    public void onError(WebSocketConnection session, Throwable throwable) {
        sessions.remove(URLDecoder.decode(ParseQueryUtilHelper
                .getQueryMap(connection.handshakeRequest().query()).get("id"), StandardCharsets.UTF_8));
        if (logger.isInfoEnabled()) {
            logger.info("Websocket-failure...User {} {} {}", URLDecoder.decode(ParseQueryUtilHelper
                            .getQueryMap(connection.handshakeRequest().query())
                            .get("handle"), StandardCharsets.UTF_8), "left on error:",
                    throwable.getMessage());
        }
    }
}
