/*
 * 文 件 名:  MockClientHandler
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  Mock充电桩客户端消息处理器
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/01/07
 * 修改内容:  添加远程停止充电(0x36)处理
 */
package com.ruoyi.ems.charging.mock.handler;

import com.ruoyi.ems.charging.mock.model.MockGunState;
import com.ruoyi.ems.charging.mock.simulator.MockPileClient;
import com.ruoyi.ems.charging.model.BaseFrame;
import com.ruoyi.ems.charging.model.req.RealtimeDataFrame;
import com.ruoyi.ems.charging.model.req.RemoteStopRespFrame;
import com.ruoyi.ems.charging.model.req.WorkParamSetRespFrame;
import com.ruoyi.ems.charging.model.resp.HeartbeatRespFrame;
import com.ruoyi.ems.charging.model.resp.LoginRespFrame;
import com.ruoyi.ems.charging.model.resp.ReadRealtimeDataFrame;
import com.ruoyi.ems.charging.model.resp.RemoteStopFrame;
import com.ruoyi.ems.charging.model.resp.WorkParamSetFrame;
import com.ruoyi.ems.charging.protocol.ProtocolConstants;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Mock充电桩客户端消息处理器
 * 处理服务端下发的消息并进行响应
 *
 * @author lvwenbin
 * @version [版本号, 2025/01/07]
 */
public class MockClientHandler extends SimpleChannelInboundHandler<BaseFrame> {

    private static final Logger log = LoggerFactory.getLogger(MockClientHandler.class);

    private final MockPileClient pileClient;

    public MockClientHandler(MockPileClient pileClient) {
        this.pileClient = pileClient;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, BaseFrame frame) throws Exception {
        byte frameType = frame.getFrameType();

        switch (frameType) {
            case ProtocolConstants.FRAME_TYPE_LOGIN_RESP:           // 0x02
                handleLoginResponse(ctx, (LoginRespFrame) frame);
                break;
            case ProtocolConstants.FRAME_TYPE_HEARTBEAT_RESP:       // 0x04
                handleHeartbeatResponse(ctx, (HeartbeatRespFrame) frame);
                break;
            case ProtocolConstants.FRAME_TYPE_READ_REALTIME_DATA:   // 0x12
                handleReadRealtimeData(ctx, (ReadRealtimeDataFrame) frame);
                break;
            case ProtocolConstants.FRAME_TYPE_REMOTE_STOP:          // 0x36
                handleRemoteStop(ctx, (RemoteStopFrame) frame);
                break;
            case ProtocolConstants.FRAME_TYPE_WORK_PARAM_SET:       // 0x52
                handleWorkParamSet(ctx, (WorkParamSetFrame) frame);
                break;
            default:
                log.debug("[{}] 收到未处理的帧类型: 0x{}",
                    pileClient.getPileCode(), String.format("%02X", frameType & 0xFF));
                break;
        }
    }

    /**
     * 处理登录应答 (0x02)
     */
    private void handleLoginResponse(ChannelHandlerContext ctx, LoginRespFrame resp) {
        if (resp.isSuccess()) {
            log.info("[{}] 登录成功", pileClient.getPileCode());
            pileClient.onLoginSuccess();
        } else {
            log.warn("[{}] 登录失败", pileClient.getPileCode());
            pileClient.onLoginFailed();
        }
    }

    /**
     * 处理心跳应答 (0x04)
     */
    private void handleHeartbeatResponse(ChannelHandlerContext ctx, HeartbeatRespFrame resp) {
        log.debug("[{}] 收到心跳应答", pileClient.getPileCode());
        pileClient.onHeartbeatResponse();
    }

    /**
     * 处理读取实时数据请求 (0x12)
     * 服务端主动查询,客户端需要立即响应
     */
    private void handleReadRealtimeData(ChannelHandlerContext ctx, ReadRealtimeDataFrame req) {
        String gunNo = req.getGunNo();
        log.info("[{}] 收到读取实时数据请求, 枪号: {}", pileClient.getPileCode(), gunNo);

        // 查找对应枪的状态
        MockGunState gunState = pileClient.getGunState(gunNo);
        if (gunState == null) {
            log.warn("[{}] 未找到枪号: {}", pileClient.getPileCode(), gunNo);
            return;
        }

        // 构建实时数据帧并发送
        RealtimeDataFrame dataFrame = buildRealtimeDataFrame(gunState, req.getSequenceNo());
        ctx.writeAndFlush(dataFrame);

        log.info("[{}] 响应实时数据查询: gun={}, status={}",
            pileClient.getPileCode(), gunNo, gunState.isCharging() ? "充电中" : "空闲");
    }

    /**
     * 处理远程停止充电 (0x36)
     * 服务端下发停止充电命令
     */
    private void handleRemoteStop(ChannelHandlerContext ctx, RemoteStopFrame req) {
        String gunNo = req.getGunNo();
        log.info("[{}] 收到远程停止充电命令, 枪号: {}", pileClient.getPileCode(), gunNo);

        // 查找对应枪的状态
        MockGunState gunState = pileClient.getGunState(gunNo);

        // 构建应答
        RemoteStopRespFrame resp = new RemoteStopRespFrame();
        resp.setPileCode(req.getPileCode());
        resp.setGunNo(gunNo);
        resp.setSequenceNo(req.getSequenceNo());

        if (gunState == null) {
            // 枪号不存在
            log.warn("[{}] 远程停止充电失败: 未找到枪号 {}", pileClient.getPileCode(), gunNo);
            resp.setStopResult(RemoteStopRespFrame.RESULT_FAIL);
            resp.setFailReason(RemoteStopRespFrame.FAIL_REASON_DEVICE_MISMATCH);
        } else if (!gunState.isCharging()) {
            // 枪未在充电
            log.warn("[{}] 远程停止充电失败: 枪[{}]未在充电", pileClient.getPileCode(), gunNo);
            resp.setStopResult(RemoteStopRespFrame.RESULT_FAIL);
            resp.setFailReason(RemoteStopRespFrame.FAIL_REASON_NOT_CHARGING);
        } else {
            // 停止充电成功
            log.info("[{}] 远程停止充电成功: 枪[{}], 已充电量: {}kWh",
                pileClient.getPileCode(), gunNo,
                String.format("%.2f", gunState.getChargingEnergy()));

            // 先发送充电结束帧
            pileClient.sendChargingEndFrame(gunState);

            // 停止充电
            gunState.stopCharging();

            resp.setStopResult(RemoteStopRespFrame.RESULT_SUCCESS);
            resp.setFailReason(RemoteStopRespFrame.FAIL_REASON_NONE);
        }

        // 发送应答
        ctx.writeAndFlush(resp);
        log.info("[{}] 发送远程停止充电应答: {}", pileClient.getPileCode(), resp);
    }

    /**
     * 处理工作参数设置 (0x52)
     * 服务端下发功率限制等参数
     */
    private void handleWorkParamSet(ChannelHandlerContext ctx, WorkParamSetFrame req) {
        log.info("[{}] 收到工作参数设置: allowWork={}, maxPower={}%",
            pileClient.getPileCode(), req.isAllowWork(), req.getMaxOutputPowerPercent());

        // 更新所有枪的功率限制
        boolean success = true;
        try {
            int powerPercent = req.getMaxOutputPowerPercent();
            for (MockGunState gunState : pileClient.getAllGunStates()) {
                gunState.setMaxOutputPowerPercent(powerPercent);
            }

            // 如果不允许工作,停止所有充电
            if (!req.isAllowWork()) {
                for (MockGunState gunState : pileClient.getAllGunStates()) {
                    if (gunState.isCharging()) {
                        // 发送充电结束帧
                        pileClient.sendChargingEndFrame(gunState);
                        gunState.stopCharging();
                        log.info("[{}] 枪[{}] 因禁用而停止充电", pileClient.getPileCode(), gunState.getGunNo());
                    }
                }
            }
        } catch (Exception e) {
            log.error("[{}] 设置工作参数失败", pileClient.getPileCode(), e);
            success = false;
        }

        // 发送应答
        WorkParamSetRespFrame resp = new WorkParamSetRespFrame();
        resp.setPileCode(req.getPileCode());
        resp.setSetResult(success ? (byte) 0x01 : (byte) 0x00);
        resp.setSequenceNo(req.getSequenceNo());

        ctx.writeAndFlush(resp);

        log.info("[{}] 工作参数设置应答: {}", pileClient.getPileCode(), success ? "成功" : "失败");
    }

    /**
     * 构建实时数据帧
     */
    private RealtimeDataFrame buildRealtimeDataFrame(MockGunState gunState, int sequenceNo) {
        RealtimeDataFrame frame = new RealtimeDataFrame();
        frame.setSequenceNo(sequenceNo);

        // 基础信息
        frame.setTransactionNo(gunState.getTransactionNo() != null ?
            gunState.getTransactionNo() : "00000000000000000000000000000000");
        frame.setPileCode(gunState.getPileCode());
        frame.setGunNo(String.format("%02d", Integer.parseInt(gunState.getGunNo())));

        // 状态信息
        frame.setStatus(gunState.getStatus());
        frame.setGunReturned(gunState.getGunReturned());
        frame.setGunConnected(gunState.isGunConnected() ?
            ProtocolConstants.GUN_CONNECTED : ProtocolConstants.GUN_NOT_CONNECTED);

        // 输出参数 (保留一位小数)
        frame.setOutputVoltage((int) (gunState.getOutputVoltage() * 10));
        frame.setOutputCurrent((int) (gunState.getOutputCurrent() * 10));

        // 温度 (偏移量+50)
        frame.setGunTemperature(gunState.getGunTemperature() + 50);
        frame.setBatteryMaxTemp(gunState.getBatteryMaxTemp() + 50);

        // 枪线编码
        frame.setGunLineCode(new byte[8]);

        // SOC和充电时间
        frame.setSoc(gunState.getSoc());
        frame.setChargingTime(gunState.getChargingTime());
        frame.setRemainingTime(gunState.getRemainingTime());

        // 能量 (保留四位小数)
        frame.setChargingEnergy((long) (gunState.getChargingEnergy() * 10000));
        frame.setLossCorrectedEnergy((long) (gunState.getChargingEnergy() * 10000));

        // 金额 (保留四位小数)
        frame.setChargedAmount((long) (gunState.getChargedAmount() * 10000));

        // 故障码
        frame.setHardwareFault(gunState.getHardwareFault());

        return frame;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("[{}] 连接已建立", pileClient.getPileCode());
        pileClient.onConnected(ctx.channel());
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.warn("[{}] 连接已断开", pileClient.getPileCode());
        pileClient.onDisconnected();
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("[{}] 连接异常: {}", pileClient.getPileCode(), cause.getMessage());
        ctx.close();
    }
}