/*******************************************************************************
 * Copyright (c) 2024-2025 R3944Realms. All rights reserved.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 *     http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.r3944realms.dg_lab.websocket;


import com.r3944realms.dg_lab.DgLab;
import com.r3944realms.dg_lab.api.manager.Status;
import com.r3944realms.dg_lab.utils.stringUtils.UrlValidator;
import com.r3944realms.dg_lab.websocket.handler.server.HttpRequestHandler;
import com.r3944realms.dg_lab.api.websocket.message.Message;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CompletableFuture;

/**
 * 主要是处理netty逻辑以建立服务端
 */
@SuppressWarnings({"DuplicatedCode", "LoggingSimilarMessage"})
public abstract class AbstractWebSocketServer {

    /**
     * The constant logger.
     */
    protected static final Logger logger =  LoggerFactory.getLogger(AbstractWebSocketServer.class);

    private int Port;

    /**
     * The Server bootstrap.
     */
    protected ServerBootstrap ServerBootstrap;
    /**
     * The Boss group.
     */
    protected EventLoopGroup BossGroup,
    /**
     * The Worker group.
     */
    WorkerGroup;
    /**
     * The Server channel.
     */
    protected Channel ServerChannel;

    /**
     * The Websocket server thread.
     */
    protected Thread WebsocketServerThread;
    /**
     * The Server status.
     */
    protected volatile Status ServerStatus = Status.WAITING_FOR_INIT;

    /**
     * Instantiates a new Abstract web socket server.
     */
    protected AbstractWebSocketServer() {
        Port = 9000;
    }

    /**
     * Instantiates a new Abstract web socket server.
     *
     * @param port the port
     */
    protected AbstractWebSocketServer(int port) {
        Port = port;
    }

    /**
     * Gets status.
     *
     * @return the status
     */
    public Status getStatus() {
        return this.ServerStatus;
    }

    /**
     * Sets status.
     *
     * @param status the status
     */
    public void setStatus(Status status) {
        this.ServerStatus = status;
    }

    /**
     * Sets port.
     *
     * @param port the port
     */
    public void setPort(int port) {
        if(this.ServerStatus != Status.WAITING_FOR_INIT && this.ServerStatus != Status.STOPPED) {
            logger.error("Unable to Change Port to {}", port);
            return;
        }
        this.Port = UrlValidator.isValidPort(port) ? port : 9000;
    }

    /**
     * Gets port.
     *
     * @return the port
     */
    public int getPort() {
        return Port;
    }

    /**
     * 提供实现此处添加对Message处理Handler
     *
     * @param pipeline 管道
     */
    protected abstract void MessagePipeLineHandler(ChannelPipeline pipeline);

    /**
     * Init thread 0.
     */
    protected void initThread0() {}

    /**
     * Init thread.
     */
    protected final void initThread() {
        BossGroup = new NioEventLoopGroup();
        WorkerGroup = new NioEventLoopGroup();
        WebsocketServerThread = new Thread(() ->{
            try {
                ServerBootstrap = new ServerBootstrap();
                ServerStatus = Status.STARTING;
                ServerBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                        .childOption(ChannelOption.SO_KEEPALIVE, true);
                ServerBootstrap.group(BossGroup, WorkerGroup);
                ServerBootstrap.channel(NioServerSocketChannel.class);
                ServerBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(DgLab.LOGGING_HANDLER);
                        pipeline.addLast(new HttpServerCodec());
                        pipeline.addLast(new HttpObjectAggregator(65536));
                        pipeline.addLast(new HttpRequestHandler());//去除路径请求，APP会发送带路径请求的HTTP升级请求，目前用不到
                        pipeline.addLast("WSP",new WebSocketServerProtocolHandler("/"));
                        pipeline.addLast(new WebSocketFrameAggregator(65536));
                        MessagePipeLineHandler(pipeline);
                    }
                });
                logger.debug("WebSocketServer try binding port ... ");
                ChannelFuture channelFuture = ServerBootstrap.bind("0.0.0.0",Port);
                channelFuture.sync();
                ServerChannel = channelFuture.channel();
                ServerStatus = Status.RUNNING;
                started();
                logger.info("WebSocketServer start on the port of {}", Port);
                logger.debug("WebSocketServer listening on port {}", Port);
                channelFuture.channel().closeFuture().sync();
            } catch (Exception e) {
                ServerStatus = Status.ERROR;
                stoppingError();
                logger.error(e.getMessage());
            } finally {
                if(ServerStatus == Status.ERROR || ServerStatus == Status.RUNNING) stop();
            }
        }, "WebSocketServer");
        initThread0();
    }

    /**
     * Starting.
     */
    protected abstract void starting();

    /**
     * Starting error.
     */
    protected abstract void startingError();

    /**
     * Started.
     */
    protected abstract void started();

    /**
     * Stopping.
     */
    protected abstract void stopping();

    /**
     * Stopping error.
     */
    protected abstract void stoppingError();

    /**
     * Stopped.
     */
    protected abstract void stopped();

    /**
     * 自定义Start后续逻辑
     */
    protected void start0() {
        //NOOP
    }

    /**
     * Start.
     */
    public final void start() {
        starting();
        switch (ServerStatus) {
            case STARTING -> {
                startingError();
                logger.info("Server is already starting.");
            }
            case RUNNING -> {
                startingError();
                logger.info("Server is already running.");
            }
            case STOPPING -> {
                startingError();
                logger.info("Server is stopping");
            }
            case STOPPED, WAITING_FOR_INIT -> {
                initThread();
                WebsocketServerThread.start();
                start0();
            }
        }
    }

    /**
     * 自定义Stop后续逻辑
     *
     * @return the completable future
     */
    protected CompletableFuture<Void> stop0() {
        return CompletableFuture.runAsync(() -> {});
    }

    /**
     * Stop.
     */
    public final void stop() {
        stopping();
        switch (ServerStatus) {
            case WAITING_FOR_INIT -> {
                stoppingError();
                logger.warn("Not Init. (It shouldn't be happened)");
            }
            case STARTING -> {
                stoppingError();
                logger.info("Server is starting, please waiting.");
            }
            case RUNNING -> {
                ServerStatus = Status.STOPPING;
                // Close the server channel if it's open
                CompletableFuture<Void> SerChanClose = CompletableFuture.runAsync(() -> {
                    if (ServerChannel != null && ServerChannel.isOpen()) {
                        try {
                            ServerChannel.close().sync();
                        } catch (InterruptedException e) {
                            logger.error(e.getMessage());
                        } finally {
                            ServerChannel = null;
                        }
                    }
                });
                // Shutdown the event loop groups
                CompletableFuture<Void> BosGroClose = SerChanClose.thenRunAsync(() -> {
                            if (BossGroup != null) {
                                try {
                                    BossGroup.shutdownGracefully().sync();
                                } catch (InterruptedException e) {
                                    logger.error(e.getMessage());
                                } finally {
                                    BossGroup = null;
                                }
                            }
                        }
                );
                CompletableFuture<Void> WorGroClose = SerChanClose.thenRunAsync(() -> {
                    if (WorkerGroup != null) {
                        try {
                            WorkerGroup.shutdownGracefully().sync();
                        } catch (InterruptedException e) {
                            logger.error(e.getMessage());
                        } finally {
                            WorkerGroup = null;
                        }
                    }
                });
                BosGroClose.thenCombineAsync(WorGroClose, (a,b) -> {
                    stop0().thenRunAsync(() -> {
                        ServerStatus = Status.STOPPED;
                        ServerBootstrap = null;
                        stopped();
                    });
                    return null;
                });

            }
            case STOPPING -> {
                stoppingError();
                logger.info("Server is already stopping");
            }
            case STOPPED -> {
                stoppingError();
                logger.info("Server has stopped");
            }
        }
    }

    /**
     * 向指定的客户端发送消息
     *
     * @param clientId 客户端UUID
     * @param message  消息
     */
    public abstract void send(String clientId, Message message);
}
