/*
 * 文 件 名:  ChargingMeterService
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  充电计量服务
 * 修 改 人:  lvwenbin
 * 修改时间:  2026/01/09
 * 修改内容:  使用统一设备映射缓存 ChargingDeviceMappingCache
 */
package com.ruoyi.ems.charging.service;


import com.ruoyi.ems.charging.model.req.TransactionRecordFrame;
import com.ruoyi.ems.domain.ChargingMeterH;
import com.ruoyi.ems.domain.ChargingTransaction;
import com.ruoyi.ems.domain.EmsDevice;
import com.ruoyi.ems.mapper.ChargingMeterHMapper;
import com.ruoyi.ems.mapper.ChargingTransactionMapper;
import com.ruoyi.ems.service.IEmsDeviceService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 充电计量服务
 * 处理充电交易记录的存储、分时电量拆分
 *
 * @author lvwenbin
 * @version [版本号, 2026/01/09]
 */
@Service
public class ChargingMeterService {

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

    @Autowired
    private ChargingTransactionMapper transactionMapper;

    @Autowired
    private ChargingMeterHMapper meterHMapper;

    @Autowired
    private IEmsDeviceService deviceService;

    @Autowired
    private ChargingDeviceMappingCache deviceMappingCache;

    /**
     * 保存交易记录
     * 核心方法：将协议帧数据转换为交易记录和分时计量记录
     *
     * @param frame 交易记录帧
     * @return 保存是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean saveTransactionRecord(TransactionRecordFrame frame) {
        String transactionNo = frame.getTransactionNo();
        String pileCode = frame.getPileCode();
        String gunNo = frame.getGunNo();

        log.info("开始保存交易记录 - 流水号: {}, 桩号: {}, 枪号: {}", transactionNo, pileCode, gunNo);

        try {
            // 1. 检查是否已存在（防止重复上报）
            ChargingTransaction existRecord = transactionMapper.selectByTransactionNo(transactionNo);
            if (existRecord != null) {
                log.warn("交易记录已存在，跳过保存 - 流水号: {}", transactionNo);
                return true;
            }

            // 2.查找设备编码和区域编码
            String deviceCode = findGunDeviceCode(pileCode, gunNo);
            String areaCode = null;
            if (StringUtils.isNotBlank(deviceCode)) {
                areaCode = findAreaCode(deviceCode);
            }

            // 3. 构建并保存交易主表记录
            ChargingTransaction transaction = buildTransaction(frame, deviceCode, areaCode);
            transactionMapper.insert(transaction);
            log.info("交易主表保存成功 - 流水号: {}, ID: {}", transactionNo, transaction.getId());

            // 4. 拆分并保存分时计量记录
            List<ChargingMeterH> meterHList = buildMeterHList(frame, transactionNo, deviceCode, areaCode);
            if (CollectionUtils.isNotEmpty(meterHList)) {
                meterHMapper.insertBatch(meterHList);
                log.info("分时计量记录保存成功 - 流水号: {}, 记录数: {}", transactionNo, meterHList.size());
            }

            // 5. 打印计量汇总日志
            logMeterSummary(frame, meterHList);

            return true;

        } catch (Exception e) {
            log.error("保存交易记录异常 - 流水号: {}", transactionNo, e);
            throw e;
        }
    }

    /**
     * 构建交易主表记录
     */
    private ChargingTransaction buildTransaction(TransactionRecordFrame frame, String deviceCode, String areaCode) {
        ChargingTransaction transaction = new ChargingTransaction();

        transaction.setTransactionNo(frame.getTransactionNo());
        transaction.setPileCode(frame.getPileCode());
        transaction.setGunNo(frame.getGunNo());
        transaction.setDeviceCode(deviceCode);
        transaction.setAreaCode(areaCode);

        // 时间
        transaction.setStartTime(frame.getStartTime());
        transaction.setEndTime(frame.getEndTime());
        transaction.setChargingDuration(frame.getChargingDurationMinutes());
        transaction.setRecordDate(frame.getEndTime()); // 以结束时间为记录日期

        // 电表读数
        transaction.setMeterStart(frame.getMeterStartValueKwh());
        transaction.setMeterEnd(frame.getMeterEndValueKwh());

        // 汇总电量
        transaction.setTotalEnergy(frame.getTotalEnergyValue());
        transaction.setTotalEnergyLoss(frame.getTotalEnergyLossValue());
        transaction.setTotalAmount(frame.getTotalAmountValue());

        // 车辆信息
        transaction.setVin(frame.getVin());

        // 交易信息
        transaction.setTransactionType((int) frame.getTransactionType());
        transaction.setStopReason(String.format("%02X", frame.getStopReason() & 0xFF));
        transaction.setStopReasonDesc(frame.getStopReasonDesc());
        transaction.setIsNormalStop(frame.isNormalStop() ? 1 : 0);
        transaction.setPhysicalCardNo(frame.getPhysicalCardNo());

        // 默认不纳入能耗统计（根据业务配置决定）
        transaction.setIncludeInEms(0);

        return transaction;
    }

    /**
     * 构建分时计量记录列表
     * 将协议帧中的尖/峰/平/谷四个时段拆分为独立记录
     */
    private List<ChargingMeterH> buildMeterHList(TransactionRecordFrame frame, String transactionNo,
        String deviceCode, String areaCode) {
        List<ChargingMeterH> list = new ArrayList<>();
        Date recordTime = frame.getEndTime();

        // 尖时段 (meter_type = 2)
        if (frame.getSharpEnergyValue() > 0) {
            ChargingMeterH sharp = new ChargingMeterH(
                transactionNo, areaCode, deviceCode, recordTime,
                ChargingMeterH.METER_TYPE_SHARP,
                frame.getSharpEnergyValue(),
                frame.getSharpEnergyLossValue(),
                frame.getSharpUnitPriceValue(),
                frame.getSharpAmountValue()
            );
            list.add(sharp);
        }

        // 峰时段 (meter_type = 1)
        if (frame.getPeakEnergyValue() > 0) {
            ChargingMeterH peak = new ChargingMeterH(
                transactionNo, areaCode, deviceCode, recordTime,
                ChargingMeterH.METER_TYPE_PEAK,
                frame.getPeakEnergyValue(),
                frame.getPeakEnergyLossValue(),
                frame.getPeakUnitPriceValue(),
                frame.getPeakAmountValue()
            );
            list.add(peak);
        }

        // 平时段 (meter_type = 0)
        if (frame.getFlatEnergyValue() > 0) {
            ChargingMeterH flat = new ChargingMeterH(
                transactionNo, areaCode, deviceCode, recordTime,
                ChargingMeterH.METER_TYPE_FLAT,
                frame.getFlatEnergyValue(),
                frame.getFlatEnergyLossValue(),
                frame.getFlatUnitPriceValue(),
                frame.getFlatAmountValue()
            );
            list.add(flat);
        }

        // 谷时段 (meter_type = -1)
        if (frame.getValleyEnergyValue() > 0) {
            ChargingMeterH valley = new ChargingMeterH(
                transactionNo, areaCode, deviceCode, recordTime,
                ChargingMeterH.METER_TYPE_VALLEY,
                frame.getValleyEnergyValue(),
                frame.getValleyEnergyLossValue(),
                frame.getValleyUnitPriceValue(),
                frame.getValleyAmountValue()
            );
            list.add(valley);
        }

        return list;
    }

    /**
     * 打印计量汇总日志
     */
    private void logMeterSummary(TransactionRecordFrame frame, List<ChargingMeterH> meterHList) {
        StringBuilder sb = new StringBuilder();
        sb.append("交易计量汇总 - 流水号: ").append(frame.getTransactionNo());
        sb.append(", 总电量: ").append(String.format("%.4f", frame.getTotalEnergyValue())).append("kWh");
        sb.append(", 总金额: ").append(String.format("%.2f", frame.getTotalAmountValue())).append("元");
        sb.append(" [分时明细: ");

        for (ChargingMeterH meter : meterHList) {
            sb.append(meter.getMeterTypeDesc())
                .append("=").append(String.format("%.4f", meter.getElecQuantity())).append("kWh")
                .append("@").append(String.format("%.5f", meter.getMeterUnitPrice())).append("元")
                .append("; ");
        }
        sb.append("]");

        log.info(sb.toString());
    }

    // ==================== 设备查找方法 ====================

    private String findGunDeviceCode(String pileCode, String gunNo) {
        String deviceCode = deviceMappingCache.getGunDeviceCode(pileCode, gunNo);

        if (StringUtils.isBlank(deviceCode)) {
            log.warn("未找到充电枪设备 - 桩号: {}, 枪号: {}", pileCode, gunNo);
        }

        return deviceCode;
    }

    private String findAreaCode(String deviceCode) {
        // 查询设备信息获取区域编码
        EmsDevice device = deviceService.selectByCode(deviceCode);
        if (device != null && StringUtils.isNotBlank(device.getAreaCode())) {
            return device.getAreaCode();
        }

        log.warn("未找到设备 {} 的区域编码", deviceCode);
        return null;
    }

    // ==================== 查询方法 ====================

    /**
     * 根据交易流水号查询交易记录
     */
    public ChargingTransaction getTransaction(String transactionNo) {
        return transactionMapper.selectByTransactionNo(transactionNo);
    }

    /**
     * 根据交易流水号查询分时计量记录
     */
    public List<ChargingMeterH> getMeterHList(String transactionNo) {
        return meterHMapper.selectByTransactionNo(transactionNo);
    }

    /**
     * 查询指定设备日期范围内的交易记录
     */
    public List<ChargingTransaction> getTransactionsByDevice(String deviceCode, Date startDate, Date endDate) {
        return transactionMapper.selectByDeviceAndDateRange(deviceCode, startDate, endDate);
    }

    /**
     * 查询指定区域日期的分时电量统计
     */
    public List<ChargingMeterH> getDailyStatByMeterType(String areaCode, Date date) {
        return meterHMapper.selectDailyStatByMeterType(areaCode, date);
    }

    /**
     * 更新交易记录是否纳入能耗统计
     */
    public int updateIncludeInEms(String transactionNo, boolean includeInEms) {
        return transactionMapper.updateIncludeInEms(transactionNo, includeInEms ? 1 : 0);
    }
}