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.UnlessBuildProperty;
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;

@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "h2", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "firebase", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "neo4j", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "cassandra", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "mariadb", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "postgres", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "ibmdb2", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "cubrid", enableIfMissing = false)
@UnlessBuildProperty(name = "DEFAULT_DB", stringValue = "sqlite3", enableIfMissing = false)
@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 {
//        System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT] [%4$s] %5$s %3$s %n");
        String mode = Server.isProduction ? "prod" : "dev";

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

        dodexHibernateConfig = DbConfiguration.getDefaultDb();
        dodexHibernateConfig.configDatabase();
//        DodexService dodexService = new DodexService();
//        dodexService.configureDatabase();
        sessionFactory = dodexHibernateConfig.getSessionFactory();
//        configureDatabase();
    }

    @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);
//            connection.sendText("Connection failed, logged off").subscribe().asCompletionStage();
        }

        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());
        }
    }
}
