/*
 * 文 件 名:  MockPileClient
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  Mock充电桩客户端
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/12/23
 */
package com.ruoyi.ems.charging.mock.simulator;

import com.ruoyi.ems.charging.core.ChargingPileEncoder;
import com.ruoyi.ems.charging.mock.config.MockPileConfig;
import com.ruoyi.ems.charging.mock.config.MockPileConfig.GunConfig;
import com.ruoyi.ems.charging.mock.config.MockPileConfig.PileHostConfig;
import com.ruoyi.ems.charging.mock.handler.MockClientDecoder;
import com.ruoyi.ems.charging.mock.handler.MockClientHandler;
import com.ruoyi.ems.charging.mock.model.MockGunState;
import com.ruoyi.ems.charging.model.req.*;
import com.ruoyi.ems.charging.protocol.ProtocolConstants;
import com.ruoyi.ems.charging.utils.ByteUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Mock充电桩客户端
 * 模拟单个充电桩主机的行为,包括登录、心跳、数据上报、充电模拟等
 *
 * @author lvwenbin
 * @version [版本号, 2025/12/23]
 */
public class MockPileClient {

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

    /**
     * 主机配置
     */
    private final PileHostConfig config;

    /**
     * 全局配置
     */
    private final MockPileConfig globalConfig;

    /**
     * 事件循环组
     */
    private final EventLoopGroup eventLoopGroup;

    /**
     * 是否共享EventLoopGroup
     */
    private final boolean sharedEventLoopGroup;

    /**
     * 通道
     */
    private volatile Channel channel;

    /**
     * 是否已登录
     */
    private final AtomicBoolean loggedIn = new AtomicBoolean(false);

    /**
     * 是否运行中
     */
    private final AtomicBoolean running = new AtomicBoolean(false);

    /**
     * 序列号计数器
     */
    private final AtomicInteger sequenceNo = new AtomicInteger(0);

    /**
     * 枪状态映射 - 枪号 -> 状态
     */
    private final Map<String, MockGunState> gunStateMap = new ConcurrentHashMap<>();

    /**
     * 调度线程池
     */
    private ScheduledExecutorService scheduler;

    /**
     * 心跳任务
     */
    private ScheduledFuture<?> heartbeatTask;

    /**
     * 数据上报任务
     */
    private ScheduledFuture<?> reportTask;

    /**
     * 充电模拟任务
     */
    private ScheduledFuture<?> chargingSimulatorTask;

    /**
     * 充电更新任务
     */
    private ScheduledFuture<?> chargingUpdateTask;

    /**
     * 构造函数
     *
     * @param config       主机配置
     * @param globalConfig 全局配置
     */
    public MockPileClient(PileHostConfig config, MockPileConfig globalConfig) {
        this(config, globalConfig, null);
    }

    /**
     * 构造函数
     *
     * @param config         主机配置
     * @param globalConfig   全局配置
     * @param eventLoopGroup 事件循环组(可共享)
     */
    public MockPileClient(PileHostConfig config, MockPileConfig globalConfig, EventLoopGroup eventLoopGroup) {
        this.config = config;
        this.globalConfig = globalConfig;

        if (eventLoopGroup != null) {
            this.eventLoopGroup = eventLoopGroup;
            this.sharedEventLoopGroup = true;
        }
        else {
            this.eventLoopGroup = new NioEventLoopGroup(1);
            this.sharedEventLoopGroup = false;
        }

        // 初始化枪状态
        initGunStates();
    }

    /**
     * 初始化枪状态
     */
    private void initGunStates() {
        for (GunConfig gunConfig : config.getGuns()) {
            MockGunState gunState = new MockGunState(config.getPileCode(), gunConfig.getGunNo(),
                gunConfig.getRatedPower());
            gunStateMap.put(gunConfig.getGunNo(), gunState);
        }

        // 如果配置的枪数量大于实际配置的枪,补充默认枪
        for (int i = gunStateMap.size() + 1; i <= config.getGunCount(); i++) {
            String gunNo = String.valueOf(i);
            MockGunState gunState = new MockGunState(config.getPileCode(), gunNo, 120);
            gunStateMap.put(gunNo, gunState);
        }
    }

    /**
     * 启动客户端
     */
    public void start() {
        if (running.compareAndSet(false, true)) {
            log.info("[{}] 启动Mock充电桩客户端...", getPileCode());

            scheduler = Executors.newScheduledThreadPool(2, r -> {
                Thread t = new Thread(r, "mock-pile-" + getPileCode());
                t.setDaemon(true);
                return t;
            });

            connect();
        }
    }

    /**
     * 连接服务器
     */
    private void connect() {
        if (!running.get()) {
            return;
        }

        log.info("[{}] 正在连接服务器 {}:{}", getPileCode(), globalConfig.getServerHost(),
            globalConfig.getServerPort());

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).option(ChannelOption.SO_KEEPALIVE, true)
            .option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
            .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();

                    // 空闲检测
                    pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 30, 0, TimeUnit.SECONDS));

                    // 解码器
                    pipeline.addLast("decoder", new MockClientDecoder());

                    // 编码器
                    pipeline.addLast("encoder", new ChargingPileEncoder());

                    // 业务处理器
                    pipeline.addLast("handler", new MockClientHandler(MockPileClient.this));
                }
            });

        bootstrap.connect(globalConfig.getServerHost(), globalConfig.getServerPort())
            .addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    log.info("[{}] 连接服务器成功", getPileCode());
                }
                else {
                    log.warn("[{}] 连接服务器失败: {}", getPileCode(), future.cause().getMessage());
                    scheduleReconnect();
                }
            });
    }

    /**
     * 安排重连
     */
    private void scheduleReconnect() {
        if (!running.get()) {
            return;
        }

        int delaySeconds = globalConfig.getReconnectIntervalSeconds();
        log.info("[{}] {}秒后尝试重连...", getPileCode(), delaySeconds);

        scheduler.schedule(this::connect, delaySeconds, TimeUnit.SECONDS);
    }

    /**
     * 连接建立回调
     */
    public void onConnected(Channel channel) {
        this.channel = channel;
        this.loggedIn.set(false);

        // 发送登录认证
        sendLogin();
    }

    /**
     * 连接断开回调
     */
    public void onDisconnected() {
        this.channel = null;
        this.loggedIn.set(false);

        // 停止定时任务
        stopScheduledTasks();

        // 安排重连
        scheduleReconnect();
    }

    /**
     * 登录成功回调
     */
    public void onLoginSuccess() {
        this.loggedIn.set(true);

        // 启动定时任务
        startScheduledTasks();
    }

    /**
     * 登录失败回调
     */
    public void onLoginFailed() {
        this.loggedIn.set(false);

        // 关闭连接,触发重连
        if (channel != null && channel.isActive()) {
            channel.close();
        }
    }

    /**
     * 心跳应答回调
     */
    public void onHeartbeatResponse() {
        // 心跳正常,可以在这里做一些统计
    }

    /**
     * 发送登录认证
     */
    private void sendLogin() {
        LoginReqFrame frame = new LoginReqFrame();
        frame.setSequenceNo(nextSequenceNo());
        frame.setPileCode(config.getPileCode());
        frame.setPileType(config.getPileType());
        frame.setGunCount(config.getGunCount());
        frame.setProtocolVersion(config.getProtocolVersion());
        frame.setSoftwareVersion(config.getSoftwareVersion());
        frame.setNetworkType(config.getNetworkType());
        frame.setSimCard("00000000000000000000");
        frame.setCarrier((byte) 0x04);

        sendFrame(frame);
        log.info("[{}] 发送登录认证: gunCount={}", getPileCode(), config.getGunCount());
    }

    /**
     * 发送心跳
     */
    private void sendHeartbeat() {
        if (!loggedIn.get() || channel == null || !channel.isActive()) {
            return;
        }

        // 为每个枪发送心跳
        for (MockGunState gunState : gunStateMap.values()) {
            HeartbeatReqFrame frame = new HeartbeatReqFrame();
            frame.setSequenceNo(nextSequenceNo());
            frame.setPileCode(config.getPileCode());
            frame.setGunNo(String.format("%02d", Integer.parseInt(gunState.getGunNo())));
            frame.setGunStatus((byte) 0x00); // 正常

            sendFrame(frame);
        }

        log.debug("[{}] 发送心跳, 枪数量: {}", getPileCode(), gunStateMap.size());
    }

    /**
     * 发送实时数据
     */
    private void sendRealtimeData() {
        if (!loggedIn.get() || channel == null || !channel.isActive()) {
            return;
        }

        for (MockGunState gunState : gunStateMap.values()) {
            RealtimeDataFrame frame = buildRealtimeDataFrame(gunState);
            sendFrame(frame);

            log.debug("[{}] 发送实时数据: gun={}, status={}, power={:.1f}kW, energy={:.4f}kWh", getPileCode(),
                gunState.getGunNo(), gunState.isCharging() ? "充电中" : "空闲", gunState.getOutputPower(),
                gunState.getChargingEnergy());
        }
    }

    /**
     * 发送充电握手
     */
    private void sendChargingHandshake(MockGunState gunState) {
        ChargingHandshakeFrame frame = new ChargingHandshakeFrame();
        frame.setSequenceNo(nextSequenceNo());
        frame.setTransactionNo(gunState.getTransactionNo());
        frame.setPileCode(gunState.getPileCode());
        frame.setGunNo(String.format("%02d", Integer.parseInt(gunState.getGunNo())));
        frame.setBmsProtocolVersion(new byte[] { 0x01, 0x01, 0x00 });
        frame.setBmsBatteryType(gunState.getBatteryType());
        frame.setBmsRatedCapacity((int) (gunState.getRatedCapacity() * 10));
        frame.setBmsRatedVoltage((int) (gunState.getRatedVoltage() * 10));
        frame.setBmsManufacturer(new byte[] { 0x54, 0x45, 0x53, 0x54 }); // TEST
        frame.setBmsBatterySerialNo(new byte[4]);
        frame.setBmsProductionYear(2023 - 1985);
        frame.setBmsProductionMonth(6);
        frame.setBmsProductionDay(15);
        frame.setBmsChargingCount(100);
        frame.setBmsOwnership((byte) 0x01);
        frame.setReserved((byte) 0x00);
        frame.setVin(gunState.getVin());
        frame.setBmsSoftwareVersion(new byte[8]);

        sendFrame(frame);
        log.info("[{}] 发送充电握手: gun={}, vin={}", getPileCode(), gunState.getGunNo(), gunState.getVin());
    }

    /**
     * 发送充电结束帧 (供外部调用，如远程停止充电)
     * @param gunState 枪状态
     */
    public void sendChargingEndFrame(MockGunState gunState) {
        if (gunState == null || !gunState.isCharging()) {
            return;
        }
        sendChargingEnd(gunState);
    }

    /**
     * 发送充电结束
     */
    private void sendChargingEnd(MockGunState gunState) {
        ChargingEndFrame frame = new ChargingEndFrame();
        frame.setSequenceNo(nextSequenceNo());
        frame.setTransactionNo(gunState.getTransactionNo());
        frame.setPileCode(gunState.getPileCode());
        frame.setGunNo(String.format("%02d", Integer.parseInt(gunState.getGunNo())));
        frame.setBmsFinalSoc(gunState.getSoc());
        frame.setBmsMinCellVoltage((int) (gunState.getRatedVoltage() / 100 * 0.95 * 100)); // 模拟最低单体电压
        frame.setBmsMaxCellVoltage((int) (gunState.getRatedVoltage() / 100 * 1.05 * 100)); // 模拟最高单体电压
        frame.setBmsMinTemp(gunState.getBatteryMaxTemp() - 5 + 50); // 偏移量+50
        frame.setBmsMaxTemp(gunState.getBatteryMaxTemp() + 50);
        frame.setTotalChargingTime(gunState.getChargingTime());
        frame.setOutputEnergy((int) (gunState.getChargingEnergy() * 10)); // 0.1kWh/位
        frame.setChargerNo(1);

        sendFrame(frame);
        log.info("[{}] 发送充电结束: gun={}, energy={:.2f}kWh, time={}min, soc={}%", getPileCode(), gunState.getGunNo(),
            gunState.getChargingEnergy(), gunState.getChargingTime(), gunState.getSoc());
    }

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

        // 基础信息
        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;
    }

    /**
     * 启动定时任务
     */
    private void startScheduledTasks() {
        // 心跳任务
        heartbeatTask = scheduler.scheduleAtFixedRate(this::sendHeartbeat, 0,
            globalConfig.getHeartbeatIntervalSeconds(), TimeUnit.SECONDS);

        // 数据上报任务 (使用充电时的上报间隔)
        reportTask = scheduler.scheduleAtFixedRate(this::sendRealtimeData, 1,
            globalConfig.getChargingReportIntervalSeconds(), TimeUnit.SECONDS);

        // 充电更新任务 (每秒更新一次充电状态)
        chargingUpdateTask = scheduler.scheduleAtFixedRate(this::updateChargingStates, 1, 1, TimeUnit.SECONDS);

        // 充电模拟任务
        if (globalConfig.isAutoChargingEnabled()) {
            chargingSimulatorTask = scheduler.scheduleAtFixedRate(this::simulateChargingCycle, 10, // 10秒后开始第一次
                globalConfig.getChargingCycleSeconds(), TimeUnit.SECONDS);
        }

        log.info("[{}] 定时任务已启动: 心跳间隔={}s, 数据上报间隔={}s, 充电周期={}s", getPileCode(),
            globalConfig.getHeartbeatIntervalSeconds(), globalConfig.getChargingReportIntervalSeconds(),
            globalConfig.getChargingCycleSeconds());
    }

    /**
     * 停止定时任务
     */
    private void stopScheduledTasks() {
        if (heartbeatTask != null) {
            heartbeatTask.cancel(false);
            heartbeatTask = null;
        }
        if (reportTask != null) {
            reportTask.cancel(false);
            reportTask = null;
        }
        if (chargingSimulatorTask != null) {
            chargingSimulatorTask.cancel(false);
            chargingSimulatorTask = null;
        }
        if (chargingUpdateTask != null) {
            chargingUpdateTask.cancel(false);
            chargingUpdateTask = null;
        }
    }

    /**
     * 更新所有枪的充电状态
     */
    private void updateChargingStates() {
        for (MockGunState gunState : gunStateMap.values()) {
            if (gunState.isCharging()) {
                gunState.updateChargingProgress(1); // 每秒更新一次
            }
        }
    }

    /**
     * 模拟充电周期
     * 随机选择一把空闲的枪开始充电
     */
    private void simulateChargingCycle() {
        if (!loggedIn.get()) {
            return;
        }

        // 查找空闲的枪
        List<MockGunState> idleGuns = new ArrayList<>();
        for (MockGunState gunState : gunStateMap.values()) {
            if (gunState.isIdle()) {
                idleGuns.add(gunState);
            }
        }

        if (idleGuns.isEmpty()) {
            log.debug("[{}] 所有枪都在充电中,跳过本次充电模拟", getPileCode());
            return;
        }

        // 随机选择一把枪开始充电
        MockGunState selectedGun = idleGuns.get(new Random().nextInt(idleGuns.size()));

        log.info("[{}] 开始模拟充电: gun={}", getPileCode(), selectedGun.getGunNo());

        // 开始充电
        selectedGun.startCharging();

        // 发送充电握手
        sendChargingHandshake(selectedGun);

        // 安排充电结束
        scheduler.schedule(() -> {
            if (selectedGun.isCharging()) {
                // 发送充电结束
                sendChargingEnd(selectedGun);

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

                log.info("[{}] 充电模拟结束: gun={}", getPileCode(), selectedGun.getGunNo());
            }
        }, globalConfig.getChargingDurationSeconds(), TimeUnit.SECONDS);
    }

    /**
     * 手动开始指定枪的充电
     */
    public void startCharging(String gunNo) {
        MockGunState gunState = gunStateMap.get(gunNo);
        if (gunState == null) {
            log.warn("[{}] 未找到枪号: {}", getPileCode(), gunNo);
            return;
        }

        if (gunState.isCharging()) {
            log.warn("[{}] 枪[{}]已在充电中", getPileCode(), gunNo);
            return;
        }

        gunState.startCharging();
        sendChargingHandshake(gunState);

        log.info("[{}] 手动开始充电: gun={}", getPileCode(), gunNo);
    }

    /**
     * 手动停止指定枪的充电
     */
    public void stopCharging(String gunNo) {
        MockGunState gunState = gunStateMap.get(gunNo);
        if (gunState == null) {
            log.warn("[{}] 未找到枪号: {}", getPileCode(), gunNo);
            return;
        }

        if (!gunState.isCharging()) {
            log.warn("[{}] 枪[{}]未在充电", getPileCode(), gunNo);
            return;
        }

        sendChargingEnd(gunState);
        gunState.stopCharging();

        log.info("[{}] 手动停止充电: gun={}", getPileCode(), gunNo);
    }

    /**
     * 发送帧
     */
    private void sendFrame(com.ruoyi.ems.charging.model.BaseFrame frame) {
        if (channel != null && channel.isActive()) {
            channel.writeAndFlush(frame);
        }
    }

    /**
     * 获取下一个序列号
     */
    private int nextSequenceNo() {
        return sequenceNo.getAndIncrement() & 0xFFFF;
    }

    /**
     * 停止客户端
     */
    public void stop() {
        if (running.compareAndSet(true, false)) {
            log.info("[{}] 停止Mock充电桩客户端...", getPileCode());

            stopScheduledTasks();

            if (channel != null && channel.isActive()) {
                channel.close();
            }

            if (scheduler != null && !scheduler.isShutdown()) {
                scheduler.shutdownNow();
            }

            if (!sharedEventLoopGroup) {
                eventLoopGroup.shutdownGracefully();
            }
        }
    }

    /**
     * 获取桩编号
     */
    public String getPileCode() {
        return config.getPileCode();
    }

    /**
     * 获取枪状态
     */
    public MockGunState getGunState(String gunNo) {
        // 尝试直接匹配
        MockGunState state = gunStateMap.get(gunNo);
        if (state != null) {
            return state;
        }

        // 尝试去掉前导零匹配
        try {
            int gunNoInt = Integer.parseInt(gunNo);
            return gunStateMap.get(String.valueOf(gunNoInt));
        }
        catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 获取所有枪状态
     */
    public Collection<MockGunState> getAllGunStates() {
        return gunStateMap.values();
    }

    /**
     * 是否已登录
     */
    public boolean isLoggedIn() {
        return loggedIn.get();
    }

    /**
     * 是否运行中
     */
    public boolean isRunning() {
        return running.get();
    }

    /**
     * 是否已连接
     */
    public boolean isConnected() {
        return channel != null && channel.isActive();
    }
}
