/*
 * 文 件 名:  MockGunState
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  Mock充电枪状态
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/12/23
 * 修改内容:  修复交易流水号格式(32位而非34位)
 */
package com.ruoyi.ems.charging.mock.model;

import com.ruoyi.ems.charging.protocol.ProtocolConstants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Mock充电枪状态
 * 维护单个充电枪的模拟状态数据
 *
 * @author lvwenbin
 * @version [版本号, 2025/12/23]
 */
public class MockGunState {

    private static final Random RANDOM = new Random();
    private static final AtomicLong TRANSACTION_COUNTER = new AtomicLong(1);

    /**
     * 修复: 日期格式改为12位 (yyMMddHHmmss)
     * 协议要求: 桩号(14位) + 枪号(2位) + 日期时间(12位) + 序号(4位) = 32位
     */
    private static final DateTimeFormatter TRANS_DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMddHHmmss");

    /**
     * 桩编号
     */
    private final String pileCode;

    /**
     * 枪号
     */
    private final String gunNo;

    /**
     * 额定功率(kW)
     */
    private final int ratedPower;

    /**
     * 当前状态
     */
    private byte status = ProtocolConstants.GUN_STATUS_IDLE;

    /**
     * 是否插枪
     */
    private boolean gunConnected = false;

    /**
     * 枪是否归位
     */
    private byte gunReturned = ProtocolConstants.GUN_RETURNED;

    /**
     * 当前交易流水号
     */
    private String transactionNo;

    /**
     * 充电开始时间
     */
    private LocalDateTime chargingStartTime;

    /**
     * 当前输出电压(V)
     */
    private double outputVoltage = 0;

    /**
     * 当前输出电流(A)
     */
    private double outputCurrent = 0;

    /**
     * 当前输出功率(kW)
     */
    private double outputPower = 0;

    /**
     * 枪线温度(℃)
     */
    private int gunTemperature = 25;

    /**
     * SOC(%)
     */
    private int soc = 0;

    /**
     * 电池组最高温度(℃)
     */
    private int batteryMaxTemp = 25;

    /**
     * 累计充电时间(min)
     */
    private int chargingTime = 0;

    /**
     * 剩余时间(min)
     */
    private int remainingTime = 0;

    /**
     * 充电度数(kWh)
     */
    private double chargingEnergy = 0;

    /**
     * 已充金额(元)
     */
    private double chargedAmount = 0;

    /**
     * 硬件故障码
     */
    private int hardwareFault = 0;

    /**
     * 最大输出功率百分比(30-100)
     */
    private int maxOutputPowerPercent = 100;

    /**
     * VIN码
     */
    private String vin;

    /**
     * 电池类型
     */
    private byte batteryType = 0x06; // 三元材料电池

    /**
     * 电池额定容量(Ah)
     */
    private double ratedCapacity = 80.0;

    /**
     * 电池额定电压(V)
     */
    private double ratedVoltage = 400.0;

    public MockGunState(String pileCode, String gunNo, int ratedPower) {
        this.pileCode = pileCode;
        this.gunNo = gunNo;
        this.ratedPower = ratedPower;
    }

    /**
     * 开始充电
     */
    public void startCharging() {
        this.status = ProtocolConstants.GUN_STATUS_CHARGING;
        this.gunConnected = true;
        this.gunReturned = ProtocolConstants.GUN_NOT_RETURNED;
        this.chargingStartTime = LocalDateTime.now();
        this.transactionNo = generateTransactionNo();

        // 初始化充电参数
        this.soc = 20 + RANDOM.nextInt(30); // 初始SOC 20-50%
        this.chargingTime = 0;
        this.chargingEnergy = 0;
        this.chargedAmount = 0;

        // 模拟车辆信息
        this.vin = generateRandomVin();
        this.batteryType = (byte) (0x03 + RANDOM.nextInt(5)); // 随机电池类型
        this.ratedCapacity = 60 + RANDOM.nextInt(80); // 60-140 Ah
        this.ratedVoltage = 350 + RANDOM.nextInt(100); // 350-450 V

        // 计算输出参数
        updateChargingOutput();
    }

    /**
     * 更新充电输出(模拟充电过程中的数据变化)
     */
    public void updateChargingOutput() {
        if (status != ProtocolConstants.GUN_STATUS_CHARGING) {
            this.outputVoltage = 0;
            this.outputCurrent = 0;
            this.outputPower = 0;
            return;
        }

        // 根据功率限制和SOC计算当前输出
        double effectiveMaxPower = ratedPower * maxOutputPowerPercent / 100.0;

        // SOC越高,充电功率越低(模拟真实充电曲线)
        double socFactor = 1.0;
        if (soc > 80) {
            socFactor = 0.5 - (soc - 80) * 0.02;
        } else if (soc > 60) {
            socFactor = 0.8 - (soc - 60) * 0.015;
        }
        socFactor = Math.max(0.1, socFactor);

        // 添加一些随机波动
        double randomFactor = 0.95 + RANDOM.nextDouble() * 0.1;

        this.outputPower = effectiveMaxPower * socFactor * randomFactor;
        this.outputVoltage = ratedVoltage * (0.9 + soc * 0.001); // 电压随SOC略有变化
        this.outputCurrent = outputPower * 1000 / outputVoltage;

        // 限制电流不超过设备能力
        double maxCurrent = ratedPower * 1000 / outputVoltage;
        if (outputCurrent > maxCurrent) {
            outputCurrent = maxCurrent;
            outputPower = outputVoltage * outputCurrent / 1000;
        }

        // 温度变化
        this.gunTemperature = 25 + (int) (outputPower / ratedPower * 30) + RANDOM.nextInt(5);
        this.batteryMaxTemp = 30 + (int) (outputPower / ratedPower * 15) + RANDOM.nextInt(3);
    }

    /**
     * 更新充电进度(每秒调用一次)
     *
     * @param elapsedSeconds 从上次更新经过的秒数
     */
    public void updateChargingProgress(int elapsedSeconds) {
        if (status != ProtocolConstants.GUN_STATUS_CHARGING) {
            return;
        }

        // 更新充电时间
        if (chargingStartTime != null) {
            this.chargingTime = (int) java.time.Duration.between(chargingStartTime, LocalDateTime.now()).toMinutes();
        }

        // 更新充电能量 (kWh = kW * h)
        double addedEnergy = outputPower * elapsedSeconds / 3600.0;
        this.chargingEnergy += addedEnergy;

        // 更新SOC (假设电池容量换算)
        double batteryCapacityKwh = ratedCapacity * ratedVoltage / 1000.0;
        this.soc = Math.min(100, (int) (20 + chargingEnergy / batteryCapacityKwh * 100));

        // 更新剩余时间
        if (outputPower > 0) {
            double remainingEnergy = batteryCapacityKwh * (100 - soc) / 100.0;
            this.remainingTime = (int) (remainingEnergy / outputPower * 60);
        }

        // 更新金额(假设1.5元/kWh)
        this.chargedAmount = chargingEnergy * 1.5;

        // 更新输出参数
        updateChargingOutput();
    }

    /**
     * 停止充电
     */
    public void stopCharging() {
        this.status = ProtocolConstants.GUN_STATUS_IDLE;
        this.gunConnected = false;
        this.gunReturned = ProtocolConstants.GUN_RETURNED;

        this.outputVoltage = 0;
        this.outputCurrent = 0;
        this.outputPower = 0;
        this.gunTemperature = 25;
        this.batteryMaxTemp = 25;
    }

    /**
     * 生成交易流水号
     * 格式: 桩号(14位) + 枪号(2位) + 年月日时分秒(12位) + 自增序号(4位) = 32位
     *
     * 修复: 使用yyMMddHHmmss(12位)而非yyyyMMddHHmmss(14位)
     */
    private String generateTransactionNo() {
        // 桩号14位
        String pileCodePart = pileCode;
        // 枪号2位
        String gunNoPart = String.format("%02d", Integer.parseInt(gunNo));
        // 日期时间12位 (yyMMddHHmmss)
        String datetime = LocalDateTime.now().format(TRANS_DATE_FORMAT);
        // 序号4位
        long seq = TRANSACTION_COUNTER.getAndIncrement() % 10000;
        String seqPart = String.format("%04d", seq);

        String transNo = pileCodePart + gunNoPart + datetime + seqPart;

        // 验证长度
        if (transNo.length() != 32) {
            throw new IllegalStateException("交易流水号长度错误: " + transNo.length() + ", 期望32位");
        }

        return transNo;
    }

    /**
     * 生成随机VIN码
     */
    private String generateRandomVin() {
        StringBuilder vin = new StringBuilder("L");
        String chars = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789";
        for (int i = 0; i < 16; i++) {
            vin.append(chars.charAt(RANDOM.nextInt(chars.length())));
        }
        return vin.toString();
    }

    /**
     * 获取完整枪号
     */
    public String getFullGunNo() {
        return pileCode + String.format("%02d", Integer.parseInt(gunNo));
    }

    /**
     * 是否正在充电
     */
    public boolean isCharging() {
        return status == ProtocolConstants.GUN_STATUS_CHARGING
            || status == ProtocolConstants.GUN_STATUS_CHARGING_PULSE;
    }

    /**
     * 是否空闲
     */
    public boolean isIdle() {
        return status == ProtocolConstants.GUN_STATUS_IDLE
            || status == ProtocolConstants.GUN_STATUS_IDLE_PULSE;
    }

    // Getters and Setters
    public String getPileCode() {
        return pileCode;
    }

    public String getGunNo() {
        return gunNo;
    }

    public int getRatedPower() {
        return ratedPower;
    }

    public byte getStatus() {
        return status;
    }

    public void setStatus(byte status) {
        this.status = status;
    }

    public boolean isGunConnected() {
        return gunConnected;
    }

    public void setGunConnected(boolean gunConnected) {
        this.gunConnected = gunConnected;
    }

    public byte getGunReturned() {
        return gunReturned;
    }

    public void setGunReturned(byte gunReturned) {
        this.gunReturned = gunReturned;
    }

    public String getTransactionNo() {
        return transactionNo;
    }

    public void setTransactionNo(String transactionNo) {
        this.transactionNo = transactionNo;
    }

    public LocalDateTime getChargingStartTime() {
        return chargingStartTime;
    }

    public void setChargingStartTime(LocalDateTime chargingStartTime) {
        this.chargingStartTime = chargingStartTime;
    }

    public double getOutputVoltage() {
        return outputVoltage;
    }

    public void setOutputVoltage(double outputVoltage) {
        this.outputVoltage = outputVoltage;
    }

    public double getOutputCurrent() {
        return outputCurrent;
    }

    public void setOutputCurrent(double outputCurrent) {
        this.outputCurrent = outputCurrent;
    }

    public double getOutputPower() {
        return outputPower;
    }

    public void setOutputPower(double outputPower) {
        this.outputPower = outputPower;
    }

    public int getGunTemperature() {
        return gunTemperature;
    }

    public void setGunTemperature(int gunTemperature) {
        this.gunTemperature = gunTemperature;
    }

    public int getSoc() {
        return soc;
    }

    public void setSoc(int soc) {
        this.soc = soc;
    }

    public int getBatteryMaxTemp() {
        return batteryMaxTemp;
    }

    public void setBatteryMaxTemp(int batteryMaxTemp) {
        this.batteryMaxTemp = batteryMaxTemp;
    }

    public int getChargingTime() {
        return chargingTime;
    }

    public void setChargingTime(int chargingTime) {
        this.chargingTime = chargingTime;
    }

    public int getRemainingTime() {
        return remainingTime;
    }

    public void setRemainingTime(int remainingTime) {
        this.remainingTime = remainingTime;
    }

    public double getChargingEnergy() {
        return chargingEnergy;
    }

    public void setChargingEnergy(double chargingEnergy) {
        this.chargingEnergy = chargingEnergy;
    }

    public double getChargedAmount() {
        return chargedAmount;
    }

    public void setChargedAmount(double chargedAmount) {
        this.chargedAmount = chargedAmount;
    }

    public int getHardwareFault() {
        return hardwareFault;
    }

    public void setHardwareFault(int hardwareFault) {
        this.hardwareFault = hardwareFault;
    }

    public int getMaxOutputPowerPercent() {
        return maxOutputPowerPercent;
    }

    public void setMaxOutputPowerPercent(int maxOutputPowerPercent) {
        this.maxOutputPowerPercent = Math.max(30, Math.min(100, maxOutputPowerPercent));
        updateChargingOutput();
    }

    public String getVin() {
        return vin;
    }

    public void setVin(String vin) {
        this.vin = vin;
    }

    public byte getBatteryType() {
        return batteryType;
    }

    public void setBatteryType(byte batteryType) {
        this.batteryType = batteryType;
    }

    public double getRatedCapacity() {
        return ratedCapacity;
    }

    public void setRatedCapacity(double ratedCapacity) {
        this.ratedCapacity = ratedCapacity;
    }

    public double getRatedVoltage() {
        return ratedVoltage;
    }

    public void setRatedVoltage(double ratedVoltage) {
        this.ratedVoltage = ratedVoltage;
    }

    @Override
    public String toString() {
        return String.format("MockGunState[pile=%s, gun=%s, status=%s, power=%.1fkW, soc=%d%%, energy=%.2fkWh]",
            pileCode, gunNo, getStatusDesc(), outputPower, soc, chargingEnergy);
    }

    private String getStatusDesc() {
        switch (status) {
            case ProtocolConstants.GUN_STATUS_OFFLINE:
                return "离线";
            case ProtocolConstants.GUN_STATUS_FAULT:
                return "故障";
            case ProtocolConstants.GUN_STATUS_IDLE:
                return "空闲";
            case ProtocolConstants.GUN_STATUS_CHARGING:
                return "充电中";
            default:
                return "未知";
        }
    }
}