/*******************************************************************************
 * 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.handler.client;

import com.r3944realms.dg_lab.api.operation.ClientOperation;
import com.r3944realms.dg_lab.api.operation.IOperation;
import com.r3944realms.dg_lab.websocket.handler.AbstractDgLabPowerBoxHandlerContextWrapper;
import com.r3944realms.dg_lab.api.websocket.message.PowerBoxMessage;
import com.r3944realms.dg_lab.api.websocket.message.data.PowerBoxData;
import com.r3944realms.dg_lab.api.websocket.message.role.Role;
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketClientRole;
import com.r3944realms.dg_lab.api.websocket.message.role.WebSocketServerRole;
import com.r3944realms.dg_lab.api.websocket.message.role.type.RoleType;
import com.r3944realms.dg_lab.websocket.sharedData.ClientPowerBoxSharedData;
import com.r3944realms.dg_lab.api.websocket.sharedData.ISharedData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.net.SocketException;
import java.util.Objects;
import java.util.Timer;
import java.util.function.Consumer;

/**
 * The type Client dlpb handler context wrapper.
 */
public class ClientDLPBHandlerContextWrapper extends AbstractDgLabPowerBoxHandlerContextWrapper {

    /**
     * The Sr msg.
     */
    public final boolean SRMsg;

    /**
     * Instantiates a new Client dlpb handler.
     *
     * @param SharedData the shared data
     * @param role       the role
     * @param Co         the co
     */
    public ClientDLPBHandlerContextWrapper(ClientPowerBoxSharedData SharedData, WebSocketClientRole role, ClientOperation Co) {
        this(SharedData, role, Co, false);
    }

    /**
     * Instantiates a new Client dlpb handler.
     *
     * @param SharedData      the shared data
     * @param role            the role
     * @param clientOperation the client operation
     * @param SRMsg           the sr msg
     */
    public ClientDLPBHandlerContextWrapper(ClientPowerBoxSharedData SharedData, WebSocketClientRole role, ClientOperation clientOperation, boolean SRMsg) {
        super(SharedData, role, clientOperation);
        this.SRMsg = SRMsg;
    }

    /**
     * Instantiates a new Client dlpb handler context wrapper.
     *
     * @param sharedData the shared data
     * @param role       the role
     * @param operation  the operation
     */
    public ClientDLPBHandlerContextWrapper(ISharedData sharedData, Role role, IOperation operation) {
        super(sharedData, role, operation);
        this.SRMsg = false;
    }

    @Override
    public ClientPowerBoxSharedData getSharedData() {
        return (ClientPowerBoxSharedData) sharedData;
    }
    @Override
    public void SessionBuildInHandle(ChannelHandlerContext session) {
        logger.info("连接已建立");
    }

    @Override
    public void ActiveSessionHandle(ChannelHandlerContext session) {
        //NOOP
    }

    @Override
    public void SessionCloseHandle(ChannelHandlerContext session) {
        PowerBoxMessage breakMsg = PowerBoxMessage.createPowerBoxMessage("break", ConnectionId(), TargetWSId(), "200", new WebSocketClientRole("Cl" + ConnectionId()), new WebSocketServerRole("WebSocketServer"));
        if(session != null && session.channel().isActive() && session.channel().isOpen()) {
            sendMsgOrData(session, breakMsg);

        }
        logger.info("连接已断开");

    }

    @Override
    public void ReadMsgHandle(ChannelHandlerContext session, TextWebSocketFrame msg) {
        PowerBoxMessage dataMsg;
        PowerBoxData data;
        String json = msg.text();
        if(SRMsg && !TargetWSId().isEmpty()) {
            //通过构造开启且如果有目标（即连上了APP端）则开启Message对象校验，只有通过校验才能进读取data(雾)
            dataMsg = PowerBoxMessage.getNullMessage().getMessage(json);
            //发送对象必须是服务器类型 且 接收者（本客户端）为占位对象类型 或 本客户端对象（名字类型相同），这里取反，只有满足条件才能进入 else读取 data
            if(
                    dataMsg.direction.sender().type != RoleType.T_SERVER
                            &&
                            !(
                                    dataMsg.direction.receiver().type == RoleType.PLACEHOLDER
                                            ||
                                            (dataMsg.direction.receiver().type == RoleType.T_CLIENT && Objects.equals(dataMsg.direction.receiver().name, role.name))
                            )
            ) {
                logger.info("消息验证者错误：{}", dataMsg.direction.sender().name);
                data = PowerBoxMessage.getNullMessage().getPayload(dataMsg.getInvalidMessageJson());
            } else {
                data = PowerBoxMessage.getNullMessage().getPayload(dataMsg.getDataJson());
            }
        } else {
            if (SRMsg) {
                dataMsg = PowerBoxMessage.getNullMessage().getMessage(json);
                data = dataMsg == null ? PowerBoxMessage.getNullMessage().getPayload(json) : PowerBoxMessage.getNullMessage().getPayload(dataMsg.getDataJson());
            } else {
                data = PowerBoxMessage.getNullMessage().getPayload(json);
            }
        }
        assert data != null;
        switch (data.getCommandType()) {
            case _NC_BIND_ -> {
                if(data.getTargetId().isEmpty()) {
                    //初次连接客户端获取服务器指定id
                    ((ClientPowerBoxSharedData)sharedData).connectionId = data.getClientId();
                    logger.info("收到clientId: {}", ConnectionId());
                    String qrCodeContext = "https://www.dungeon-lab.com/app-download.php#DGLAB-SOCKET#" + ((ClientPowerBoxSharedData)sharedData).getUrl()  + ConnectionId();
                    TryCatch(i -> ((ClientOperation)operation).QrCodeUrlHandler(qrCodeContext));
                    TryCatch(i -> ((ClientOperation)operation).ShowQrCodeHandler());
                    logger.debug("重新生成QrCodeContext: {}",qrCodeContext);
                } else {
                    if(!Objects.equals(data.getClientId(), ConnectionId())) {
                        logger.debug("接收到不正确的target消息,消息中目标Id{}",data.getClientId());
                        return;
                    }
                    ((ClientPowerBoxSharedData)sharedData).targetWSId = data.getTargetId();
                    logger.info("已建立绑定连接，TargetId:{}, msg:{}",TargetWSId(),data.getMessage());
                    TryCatch(
                            i -> ((ClientOperation)operation).ConnectSuccessfulNoticeHandler()
                    );
                }
            }
            case _NC_BREAK_ -> {
                //对方断开
                if (!Objects.equals(data.getTargetId(), TargetWSId())) {
                    return;
                }
                TryCatch(
                    i -> ((ClientOperation)operation).DisconnectHandler(data)
                );
                logger.info("对方已断开，targetId:{}, msg:{}", TargetWSId(), data.getMessage());
            }
            case _NC_ERROR_ -> {
                if (!Objects.equals(data.getTargetId(), TargetWSId())) {
                    return;
                }
                TryCatch(
                        i -> ((ClientOperation)operation).ErrorHandler(data)
                );
                logger.info("接收到错误码:{}",data.getMessage());
            }
            case _NC_HEARTBEAT_ -> {
                logger.info("收到心跳");
                TryCatch(
                        i -> ((ClientOperation)operation).HeartBeatHandler(data)
                );
                ((ClientPowerBoxSharedData)sharedData).connectionId = data.getClientId();
                role.UpdateName("Cl" + ((ClientPowerBoxSharedData)sharedData).connectionId);
            }
            default -> {
                logger.info("收到其它信息{}",data.getMessage());
                TryCatch(
                        i -> ((ClientOperation)operation).OtherMessageHandler(data)
                );
            }
        }

    }
    private void TryCatch(Consumer<Void> event) {
        try {
            event.accept(null);
        } catch (Exception e) {
            logger.error("DataTypeHandler Error:{}", e.getMessage());
        }
    }



    @Override
    public void ErrorHandler(ChannelHandlerContext session, Throwable cause) {
        logger.error("连接出现错误：{}: {}",cause.getMessage(), cause.getStackTrace());
        //错误处理逻辑待加入
        if(cause instanceof SocketException) {
            logger.info("远程服务器关闭");
        }
    }

    /**
     * Send msg or data.
     *
     * @param target  the target
     * @param dataMsg the data msg
     */
    protected void sendMsgOrData(ChannelHandlerContext target, PowerBoxMessage dataMsg) {
        String json = SRMsg ? dataMsg.getMsgJson() : dataMsg.getDataJson();
        target.channel().writeAndFlush(new TextWebSocketFrame(json));
    }

    /**
     * Connection id string.
     *
     * @return the string
     */
    public String ConnectionId() {
        return ((ClientPowerBoxSharedData)sharedData).connectionId;
    }

    /**
     * Target ws id string.
     *
     * @return the string
     */
    public String TargetWSId() {
        return ((ClientPowerBoxSharedData)sharedData).targetWSId;
    }

    /**
     * Delay int.
     *
     * @return the int
     */
    public int Delay() {
        return ((ClientPowerBoxSharedData)sharedData).delay;
    }

    /**
     * Delay timer timer.
     *
     * @return the timer
     */
    public Timer DelayTimer() {
        return ((ClientPowerBoxSharedData)sharedData).delayTimer;
    }

    /**
     * Follow a strength boolean.
     *
     * @return the boolean
     */
    public boolean FollowAStrength() {
        return ((ClientPowerBoxSharedData)sharedData).followAStrength;
    }

    /**
     * Follow b strength boolean.
     *
     * @return the boolean
     */
    public boolean FollowBStrength() {
        return ((ClientPowerBoxSharedData)sharedData).followBStrength;
    }
}
