package com.yupi.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.project.common.ErrorCode;
import com.yupi.project.exception.BusinessException;
import com.yupi.project.exception.ThrowUtils;
import com.yupi.project.mapper.ChargingSessionMapper;
import com.yupi.project.model.dto.charging_session.ChargingRequest;
import com.yupi.project.model.dto.charging_session.ChargingSessionQueryRequest;
import com.yupi.project.model.entity.*;
import com.yupi.project.model.enums.*;
import com.yupi.project.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* @author Yupi
* @description 针对表【charging_session(充电记录表)】的数据库操作Service实现
* @createDate 2023-12-03 10:00:00
*/
@Service
@Slf4j
public class ChargingSessionServiceImpl extends ServiceImpl<ChargingSessionMapper, ChargingSession>
    implements ChargingSessionService {

    @Resource
    private ParkingSpotService parkingSpotService;

    @Resource
    private ChargingRobotService chargingRobotService;

    @Resource
    private RobotTaskService robotTaskService; // 用于创建和关联机器人任务

    @Resource
    @Lazy // 避免循环依赖 MqttService -> ChargingSessionService -> MqttService (通过TaskTimeoutHandler)
    private MqttService mqttService; // 用于发送指令给机器人

    // 计费相关配置 (后续可以移到配置类或数据库)
    @Value("${charging.price.perHour:10.0}") // 每小时10元
    private BigDecimal pricePerHour;

    @Value("${charging.minChargeAmount:0.5}") // 最低消费0.5元
    private BigDecimal minChargeAmount;

    @Override
    @Transactional
    public ChargingSession requestCharging(User currentUser, ChargingRequest chargingRequest) {
        if (currentUser == null || chargingRequest == null || chargingRequest.getSpotId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数错误");
        }

        // 1. 校验用户账户状态，例如余额是否充足（如果需要预付费或押金）
        // (此处简化，暂不校验余额)

        // 2. 校验车位是否存在且可用
        ParkingSpot spot = parkingSpotService.getById(chargingRequest.getSpotId());
        if (spot == null || !spot.getRobotAssignable() || !ParkingSpotStatusEnum.AVAILABLE.getValue().equals(spot.getStatus())) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "车位不可用或不存在");
        }

        // 3. 创建充电会话记录
        ChargingSession session = new ChargingSession();
        session.setUserId(currentUser.getId());
        session.setSpotId(spot.getId());
        session.setSpotUidSnapshot(spot.getSpotUid());
        session.setStatus(ChargingSessionStatusEnum.REQUESTED.getValue());
        session.setRequestTime(new Date());
        session.setPaymentStatus(PaymentStatusEnum.PENDING.getValue()); // 初始为待支付
        session.setCreateTime(new Date());
        session.setUpdateTime(new Date());

        boolean saved = this.save(session);
        if (!saved) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "创建充电会话失败");
        }
        log.info("用户 {} 请求在车位 {} (ID:{}) 开始充电会话 (ID:{})", currentUser.getId(), spot.getSpotUid(), spot.getId(), session.getId());

        // 4. 尝试分配机器人 (可以同步或异步)
        // 此处为简化，采用同步调用。实际项目中可能需要异步处理并通知用户分配结果。
        ChargingSession updatedSession = assignRobotToSession(session.getId());
        if (updatedSession == null) {
            // 如果没有可用机器人，标记会话为系统取消
            this.cancelChargingSession(session.getId(), null, "无可用机器人", ChargingSessionStatusEnum.CANCELLED_BY_SYSTEM);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "无可用机器人，充电请求失败");
        }

        return updatedSession;
    }

    @Override
    @Transactional
    public ChargingSession assignRobotToSession(Long sessionId) {
        ChargingSession session = this.getById(sessionId);
        if (session == null || !ChargingSessionStatusEnum.REQUESTED.getValue().equals(session.getStatus())) {
            log.warn("分配机器人失败，会话不存在或状态不正确: sessionId={}", sessionId);
            return null; // 或抛出异常
        }

        // 1. 查找并分配一个空闲机器人
        ChargingRobot assignedRobot = chargingRobotService.assignIdleRobot(); // assignIdleRobot 内部已将机器人状态改为MOVING
        if (assignedRobot == null) {
            log.warn("分配机器人失败，没有空闲机器人可供会话 {} 使用", sessionId);
            return null;
        }

        // 2. 更新车位状态为"已预订"或"机器人前往中"
        boolean spotOccupied = parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.RESERVED, session.getId());
        if (!spotOccupied) {
            log.error("分配机器人后，占用车位 {} (UID: {}) 失败 for session {}", session.getSpotId(), session.getSpotUidSnapshot(), sessionId);
            // 需要回滚机器人状态
            chargingRobotService.releaseRobot(assignedRobot.getId());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "占用车位失败");
        }

        // 3. 创建机器人任务：移动到指定车位
        // Payload 需要根据与硬件的约定来构建
        String moveToSpotPayload = String.format("{\"command\":\"MOVE\", \"target_spot_uid\":\"%s\"}", session.getSpotUidSnapshot());
        RobotTask moveTask = robotTaskService.createTask(assignedRobot.getRobotUid(), CommandTypeEnum.MOVE_TO_SPOT, moveToSpotPayload, sessionId);
        if (moveTask == null) {
            log.error("为会话 {} 创建机器人移动任务失败", sessionId);
            chargingRobotService.releaseRobot(assignedRobot.getId());
            parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.AVAILABLE, null);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "创建机器人移动任务失败");
        }

        // 关键修复：将任务ID关联到机器人实例上，并持久化到数据库
        log.info("将任务 {} 关联到机器人 {}", moveTask.getId(), assignedRobot.getRobotUid());
        boolean robotUpdated = chargingRobotService.update(
            new UpdateWrapper<ChargingRobot>()
                .eq("id", assignedRobot.getId())
                .set("current_task_id", moveTask.getId())
        );
        if (!robotUpdated) {
            // 如果更新失败，这是严重的内部错误，需要回滚所有操作
            log.error("为会话 {} 关联机器人任务ID失败！机器人ID: {}", sessionId, assignedRobot.getId());
            // 抛出异常以触发 @Transactional 回滚
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "关联机器人任务失败");
        }

        // 4. 更新充电会话信息
        session.setRobotId(assignedRobot.getId());
        session.setRobotUidSnapshot(assignedRobot.getRobotUid());
        session.setStatus(ChargingSessionStatusEnum.ROBOT_ASSIGNED.getValue());
        session.setRobotAssignedTime(new Date());
        session.setRelatedRobotTaskId(moveTask.getId()); // 关联当前任务
        session.setUpdateTime(new Date());
        this.updateById(session);

        // 5. 发送MQTT指令给机器人
        try {
            boolean commandPublished = mqttService.sendCommand(assignedRobot.getRobotUid(), CommandTypeEnum.MOVE_TO_SPOT, moveToSpotPayload, sessionId, moveTask.getId());

            if (!commandPublished) {
                log.error("MQTT移动指令发送失败 (sendCommand返回false) for session {}, 任务ID: {}. 将任务标记为失败。", sessionId, moveTask.getId());
                robotTaskService.markTaskAsFailed(moveTask.getId(), "MQTT 指令未成功发布 (sendCommand返回false)", new Date());
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "发送MQTT指令失败 (未发布)");
            }
            
            log.info("已成功请求向机器人 {} 发送移动指令 for session {}, 任务ID: {}", assignedRobot.getRobotUid(), sessionId, moveTask.getId());
        } catch (Exception e) {
            log.error("发送MQTT移动指令失败 for session {}, 任务ID: {}: ", sessionId, moveTask.getId(), e);
            // 此处可能需要更复杂的错误处理，如重试、标记任务失败等
            // 为了简化，如果发送失败，也回滚，并标记会话错误
            robotTaskService.markTaskAsFailed(moveTask.getId(), "MQTT发送失败: " + e.getMessage(), new Date());
            chargingRobotService.releaseRobot(assignedRobot.getId());
            parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.AVAILABLE, null);
            session.setStatus(ChargingSessionStatusEnum.ERROR.getValue());
            session.setErrorMessage("分配机器人并发送指令失败");
            this.updateById(session);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "发送MQTT指令失败");
        }
        
        log.info("成功为会话 {} 分配机器人 {} (任务ID: {})", sessionId, assignedRobot.getRobotUid(), moveTask.getId());
        return session;
    }

    @Override
    @Transactional
    public boolean handleRobotArrival(Long sessionId, Long robotTaskId) {
        ChargingSession session = this.getById(sessionId);
        if (session == null) {
            log.warn("处理机器人到达事件失败，会话不存在: sessionId={}", sessionId);
            return false;
        }
        // 校验会话状态和关联的任务ID
        // 原始状态应该是 ROBOT_ASSIGNED，表示移动任务已发出
        if (!ChargingSessionStatusEnum.ROBOT_ASSIGNED.getValue().equals(session.getStatus()) || 
            (session.getRelatedRobotTaskId() != null && !robotTaskId.equals(session.getRelatedRobotTaskId())) ) { // 确保关联的任务ID也匹配
            log.warn("处理机器人到达事件失败 (或视为开始充电事件)，会话状态 ({}) 或任务ID ({}) 不匹配 (期望任务ID: {}) for session {}", 
                session.getStatus(), robotTaskId, session.getRelatedRobotTaskId(), sessionId);
            return false;
        }

        // 更新会话状态为 CHARGING_STARTED，并记录相关信息
        session.setStatus(ChargingSessionStatusEnum.CHARGING_STARTED.getValue());
        session.setRobotArrivalTime(new Date()); // 机器人到达时间 (即充电开始时间点)
        session.setChargeStartTime(new Date());  // 明确记录充电开始时间
        session.setUpdateTime(new Date());
        
        boolean updated = this.updateById(session);
        if(updated) {
            log.info("机器人已到达，会话 {} 状态更新为 CHARGING_STARTED (视为充电开始). 关联任务ID: {}", sessionId, robotTaskId);
            
            // 更新机器人DB状态为 CHARGING
            chargingRobotService.updateRobotStatus(session.getRobotUidSnapshot(), RobotStatusEnum.CHARGING, null, null, robotTaskId, new Date());
            
            // 确保车位状态为 CHARGING (这一步之前已经有了，保持即可)
            parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.CHARGING, session.getId());
        }
        return updated;
    }

    @Override
    @Transactional
    public boolean handleChargingStart(Long sessionId, Long robotTaskId) {
        ChargingSession session = this.getById(sessionId);
        if (session == null) {
            log.warn("处理充电开始事件失败，会话不存在: sessionId={}", sessionId);
            return false;
        }
        // 校验状态，应该在 ROBOT_ARRIVED 之后
        if (!ChargingSessionStatusEnum.ROBOT_ARRIVED.getValue().equals(session.getStatus()) ||
            !robotTaskId.equals(session.getRelatedRobotTaskId())) {
            log.warn("处理充电开始事件失败，会话状态 ({}) 或任务ID ({}) 不匹配 (期望任务ID: {}) for session {}", 
                session.getStatus(), robotTaskId, session.getRelatedRobotTaskId(), sessionId);
            return false;
        }

        session.setStatus(ChargingSessionStatusEnum.CHARGING_STARTED.getValue());
        session.setChargeStartTime(new Date());
        session.setUpdateTime(new Date());
        boolean updated = this.updateById(session);
        if (updated) {
            log.info("充电开始，会话 {} 状态更新为 CHARGING_STARTED. 关联任务ID: {}", sessionId, robotTaskId);
            // 更新机器人状态为 CHARGING
            chargingRobotService.updateRobotStatus(session.getRobotUidSnapshot(), RobotStatusEnum.CHARGING, null, null, robotTaskId, new Date());
            // 确保车位状态为 CHARGING
            parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.CHARGING, session.getId());
        }
        return updated;
    }

    @Override
    @Transactional
    public boolean handleChargingEnd(Long sessionId, Long robotTaskId, BigDecimal energyConsumedKwh, int durationSeconds) {
        ChargingSession session = this.getById(sessionId);
        if (session == null) {
            log.warn("处理充电结束事件失败，会话不存在: sessionId={}", sessionId);
            return false;
        }
        // 校验会话状态
        List<String> validPreviousStates = Arrays.asList(
            ChargingSessionStatusEnum.CHARGING_STARTED.getValue()
        );
        if (!validPreviousStates.contains(session.getStatus())) {
            log.warn("处理充电结束事件失败，会话状态 ({}) 不正确 for session {}", session.getStatus(), sessionId);
            // 如果已经是COMPLETED，直接返回成功，避免重复处理
            if (session.getStatus().equals(ChargingSessionStatusEnum.CHARGING_COMPLETED.getValue())) {
                return true;
            }
            return false;
        }

        log.info("充电结束，开始处理会话 {} 的收尾工作. 关联任务ID: {}", sessionId, robotTaskId);

        // 1. 更新会话状态和核心数据
        session.setStatus(ChargingSessionStatusEnum.CHARGING_COMPLETED.getValue());
        session.setChargeEndTime(new Date());
        session.setEnergyConsumedKwh(energyConsumedKwh); // 从机器人实际上报或系统估算
        
        // 如果外部传入了时长，使用它；否则自己计算
        if (durationSeconds > 0) {
            session.setTotalDurationSeconds(durationSeconds);
        } else if (session.getChargeStartTime() != null) {
            long durationMillis = System.currentTimeMillis() - session.getChargeStartTime().getTime();
            session.setTotalDurationSeconds((int) (durationMillis / 1000));
        }
        session.setUpdateTime(new Date());

        boolean updated = this.updateById(session);
        if (!updated) {
            log.error("更新会话 {} 状态为COMPLETED失败", sessionId);
            return false;
        }

        // 2. 释放资源
        // 释放机器人，使其变为空闲
        if(session.getRobotId() != null) {
            chargingRobotService.releaseRobot(session.getRobotId());
            log.info("已释放机器人 {} for session {}", session.getRobotUidSnapshot(), sessionId);
        }
        // 释放车位
        if(session.getSpotId() != null) {
            parkingSpotService.releaseSpot(session.getSpotId());
            log.info("已释放车位 {} for session {}", session.getSpotUidSnapshot(), sessionId);
        }
        
        // 3. 进行计费
        log.info("开始为会话 {} 进行计费...", sessionId);
        calculateCostAndFinalizeSession(sessionId);

        return true;
    }

    @Override
    @Transactional
    public boolean calculateCostAndFinalizeSession(Long sessionId) {
        ChargingSession session = this.getById(sessionId);
        if (session == null || session.getChargeEndTime() == null || session.getChargeStartTime() == null) {
            log.error("计算费用失败，会话 {} 不存在或充电时间不完整", sessionId);
            return false;
        }

        long durationMillis = session.getChargeEndTime().getTime() - session.getChargeStartTime().getTime();
        double hours = (double) durationMillis / TimeUnit.HOURS.toMillis(1);

        // 费用 = 时长(小时) * 每小时单价
        BigDecimal cost = pricePerHour.multiply(BigDecimal.valueOf(hours)).setScale(2, RoundingMode.HALF_UP);

        // 应用最低消费
        if (cost.compareTo(minChargeAmount) < 0) {
            cost = minChargeAmount;
        }
        session.setCost(cost);
        session.setStatus(ChargingSessionStatusEnum.PAYMENT_PENDING.getValue()); // 更新为待支付
        session.setPaymentStatus(PaymentStatusEnum.PENDING.getValue());
        session.setUpdateTime(new Date());

        boolean updated = this.updateById(session);
        if(updated){
            log.info("会话 {} 费用计算完成: {}元. 状态更新为 PAYMENT_PENDING", sessionId, cost);
            // 此处可以触发通知用户支付
        }
        return updated;
    }

    @Override
    @Transactional
    public boolean processPayment(Long sessionId, Long userId) {
        // 省略具体支付逻辑，假设支付成功，更新会话状态
        // 实际应用中这里会调用支付网关，并根据支付结果更新
        ChargingSession session = this.getById(sessionId);
        if (session == null || !session.getUserId().equals(userId)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "会话不存在或不属于您");
        }
        if (!ChargingSessionStatusEnum.PAYMENT_PENDING.getValue().equals(session.getStatus())) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "会话状态不正确，无法支付");
        }

        session.setPaymentStatus(PaymentStatusEnum.PAID.getValue());
        session.setStatus(ChargingSessionStatusEnum.PAID.getValue());
        boolean success = this.updateById(session);
        ThrowUtils.throwIf(!success, ErrorCode.SYSTEM_ERROR, "支付状态更新失败");
        // TODO: 触发其他后续逻辑，如发送通知
        return true;
    }

    @Override
    @Transactional
    public boolean cancelChargingSession(Long sessionId, Long userId, String reason, ChargingSessionStatusEnum newStatus) {
        ChargingSession session = this.getById(sessionId);
        if (session == null) {
            log.warn("尝试取消会话 {} 失败: 会话不存在", sessionId);
            return false;
        }

        // 检查是否可以取消 (例如，不能取消已完成或已支付的会话)
        ChargingSessionStatusEnum currentStatus = ChargingSessionStatusEnum.getEnumByValue(session.getStatus());
        if (currentStatus == ChargingSessionStatusEnum.CHARGING_COMPLETED ||
            currentStatus == ChargingSessionStatusEnum.PAYMENT_PENDING ||
            currentStatus == ChargingSessionStatusEnum.PAID ||
            currentStatus == ChargingSessionStatusEnum.CANCELLED_BY_SYSTEM ||
            currentStatus == ChargingSessionStatusEnum.CANCELLED_BY_USER) {
            log.warn("会话 {} 当前状态 {} 不可取消", sessionId, currentStatus);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "当前状态不可取消");
        }
        
        if (userId != null && !session.getUserId().equals(userId)) {
             throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权取消他人会话");
        }

        session.setStatus(newStatus.getValue());
        session.setErrorMessage("会话取消: " + reason);
        session.setChargeEndTime(new Date()); // 标记一个结束时间
        session.setUpdateTime(new Date());

        boolean updated = this.updateById(session);
        if (updated) {
            log.info("充电会话 {} 已被取消，原因: {}. 操作者ID(若有): {}", sessionId, reason, userId);
            // 如果机器人已分配或在执行任务，需要处理机器人和车位状态
            if (session.getRobotId() != null) {
                chargingRobotService.releaseRobot(session.getRobotId());
                 // 如果有关联的MQTT任务，可能需要发送取消指令或标记任务为取消
                if (session.getRelatedRobotTaskId() != null) {
                    RobotTask task = robotTaskService.getById(session.getRelatedRobotTaskId());
                    if (task != null && 
                        (task.getStatus() == RobotTaskStatusEnum.PENDING || task.getStatus() == RobotTaskStatusEnum.SENT || task.getStatus() == RobotTaskStatusEnum.PROCESSING)) {
                        robotTaskService.markTaskAsFailed(task.getId(), "充电会话被取消: " + reason, new Date());
                        log.info("因会话 {} 取消，关联的机器人任务 {} 已标记为失败。", sessionId, task.getId());
                    }
                }
            }
            // 释放车位 (如果它被此会话占用)
            ParkingSpot spot = parkingSpotService.findBySpotUid(session.getSpotUidSnapshot());
            if (spot != null && sessionId.equals(spot.getCurrentSessionId()) && !ParkingSpotStatusEnum.AVAILABLE.getValue().equals(spot.getStatus())) {
                 log.info("取消会话 {} 时，释放车位 {} (UID: {})。", sessionId, spot.getId(), spot.getSpotUid());
                 parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.AVAILABLE, null);
            }
        }
        return updated;
    }

    @Override
    public List<ChargingSession> findUserSessions(Long userId) {
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        queryWrapper.orderByDesc("request_time");
        return this.list(queryWrapper);
    }

    @Override
    public ChargingSession findSessionByRobotTaskId(Long robotTaskId) {
        if (robotTaskId == null) return null;
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("related_robot_task_id", robotTaskId);
        // 通常一个任务只关联一个会话的特定阶段
        return this.getOne(queryWrapper, false); // false: 不抛异常如果找到多个
    }

    @Override
    @Transactional
    public void handleSessionTaskTimeout(Long robotTaskId) {
        log.warn("处理机器人任务超时，任务ID: {}", robotTaskId);
        RobotTask task = robotTaskService.getById(robotTaskId);
        if (task == null) {
            log.error("任务超时处理失败：任务 {} 未找到", robotTaskId);
            return;
        }

        ChargingSession session = findSessionByRobotTaskId(robotTaskId);
        if (session == null) {
            log.warn("任务 {} 超时，但未找到关联的充电会话。可能任务与会话解耦或已被处理。", robotTaskId);
            // 如果任务不直接关联会话，或者会话已结束，可能不需要进一步操作。
            // 但如果机器人仍占用，需要释放
            ChargingRobot robot = chargingRobotService.findByRobotUid(task.getRobotId()); // task.getRobotId()是robotUid
            if(robot != null && !RobotStatusEnum.IDLE.getValue().equals(robot.getStatus())){
                log.info("超时任务 {} 的机器人 {} 将被释放。", robotTaskId, robot.getRobotUid());
                chargingRobotService.releaseRobot(robot.getId());
            }
            return;
        }

        log.info("任务 {} (类型: {}) 超时，关联会话ID: {}", robotTaskId, task.getCommandType(), session.getId());
        // 根据当前会话状态和任务类型决定如何处理
        // 例如：如果是移动任务超时，可能意味着机器人未能到达
        // 如果是充电任务超时，可能意味着充电异常
        // 此处统一将会话标记为错误，并释放资源
        session.setStatus(ChargingSessionStatusEnum.ERROR.getValue());
        session.setErrorMessage("机器人任务超时 (TaskID: " + robotTaskId + ", Type: " + task.getCommandType() + ")");
        session.setChargeEndTime(new Date()); // 标记一个结束时间
        session.setUpdateTime(new Date());
        this.updateById(session);

        if (session.getRobotId() != null) {
            chargingRobotService.releaseRobot(session.getRobotId());
        }
        if (session.getSpotId() != null) {
            parkingSpotService.releaseSpot(session.getSpotId());
        }
        log.warn("会话 {} 因任务 {} 超时而被标记为错误并释放了资源。", session.getId(), robotTaskId);

        // 如果会话被标记为错误或取消，并且之前占用了车位，则释放车位
        if (session != null && session.getSpotUidSnapshot() != null &&
            (ChargingSessionStatusEnum.ERROR.getValue().equals(session.getStatus()) ||
             ChargingSessionStatusEnum.CANCELLED_BY_SYSTEM.getValue().equals(session.getStatus()) ||
             ChargingSessionStatusEnum.CANCELLED_BY_USER.getValue().equals(session.getStatus()))) {
            
            ParkingSpot spot = parkingSpotService.findBySpotUid(session.getSpotUidSnapshot());
            if (spot != null && session.getId().equals(spot.getCurrentSessionId()) && !ParkingSpotStatusEnum.AVAILABLE.getValue().equals(spot.getStatus())) {
                log.info("任务 {} 超时导致会话 {} 结束，释放车位 {} (UID: {})。", robotTaskId, session.getId(), spot.getId(), spot.getSpotUid());
                parkingSpotService.updateSpotStatus(session.getSpotUidSnapshot(), ParkingSpotStatusEnum.AVAILABLE, null);
            }
        }
    }

    @Override
    public QueryWrapper<ChargingSession> getQueryWrapper(ChargingSessionQueryRequest queryRequest) {
        if (queryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        Long id = queryRequest.getId();
        Long userId = queryRequest.getUserId();
        Long robotId = queryRequest.getRobotId();
        Long spotId = queryRequest.getSpotId();
        String robotUidSnapshot = queryRequest.getRobotUidSnapshot();
        String spotUidSnapshot = queryRequest.getSpotUidSnapshot();
        String status = queryRequest.getStatus();
        List<String> orStatusList = queryRequest.getOrStatusList();
        String paymentStatus = queryRequest.getPaymentStatus();
        String sortField = queryRequest.getSortField();
        String sortOrder = queryRequest.getSortOrder();

        queryWrapper.eq(id != null && id > 0, "id", id);
        queryWrapper.eq(userId != null && userId > 0, "user_id", userId);
        queryWrapper.eq(robotId != null && robotId > 0, "robot_id", robotId);
        queryWrapper.eq(spotId != null && spotId > 0, "spot_id", spotId);
        queryWrapper.like(StringUtils.isNotBlank(robotUidSnapshot), "robot_uid_snapshot", robotUidSnapshot);
        queryWrapper.like(StringUtils.isNotBlank(spotUidSnapshot), "spot_uid_snapshot", spotUidSnapshot);
        queryWrapper.eq(StringUtils.isNotBlank(status), "status", status);
        queryWrapper.eq(StringUtils.isNotBlank(paymentStatus), "payment_status", paymentStatus);

        if (CollectionUtils.isNotEmpty(orStatusList)) {
            queryWrapper.and(qw -> {
                for (String orStatus : orStatusList) {
                    qw.or().eq("status", orStatus);
                }
            });
        }
        queryWrapper.orderBy(StringUtils.isNotBlank(sortField),
                sortOrder.equals(com.yupi.project.constant.CommonConstant.SORT_ORDER_ASC), sortField);
        return queryWrapper;
    }

    @Override
    @Transactional
    public boolean stopChargingByUser(Long sessionId, Long userId) {
        ChargingSession session = this.getById(sessionId);
        if (session == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "充电会话不存在");
        }
        if (!session.getUserId().equals(userId)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权操作他人充电记录");
        }

        if (!ChargingSessionStatusEnum.CHARGING_STARTED.getValue().equals(session.getStatus())) {
            log.warn("用户 {} 尝试停止一个非充电中状态的会话 {} (当前状态: {})", userId, sessionId, session.getStatus());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "当前会话并非正在充电状态，无法停止");
        }

        if (StringUtils.isBlank(session.getRobotUidSnapshot())) {
            log.error("会话 {} 缺少机器人UID快照，无法发送停止指令", sessionId);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "会话数据异常，缺少机器人UID快照");
        }

        String stopChargePayload = String.format("{\"command\":\"STOP_CHARGE\", \"session_id\":%d}", sessionId);
        RobotTask stopTask = robotTaskService.createTask(session.getRobotUidSnapshot(), CommandTypeEnum.STOP_CHARGE, stopChargePayload, sessionId);
        if (stopTask == null) {
            log.error("为会话 {} 创建机器人停止充电任务失败", sessionId);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "创建机器人停止任务失败");
        }

        session.setRelatedRobotTaskId(stopTask.getId());
        session.setUpdateTime(new Date());
        this.updateById(session);

        boolean mqttSent = false;
        try {
            mqttSent = mqttService.sendCommand(session.getRobotUidSnapshot(), CommandTypeEnum.STOP_CHARGE, stopChargePayload, sessionId, stopTask.getId());
        } catch (Exception e) {
            log.error("发送MQTT停止充电指令失败 for session {}, 任务ID: {}: {}", sessionId, stopTask.getId(), e.getMessage(), e);
            robotTaskService.markTaskAsFailed(stopTask.getId(), "MQTT发送失败: " + e.getMessage(), new Date());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "发送MQTT停止指令失败: " + e.getMessage());
        }
        
        if (!mqttSent) {
            log.error("发送MQTT停止充电指令失败 for session {}, 任务ID: {}", sessionId, stopTask.getId());
            robotTaskService.markTaskAsFailed(stopTask.getId(), "MQTT发送失败", new Date());
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "发送MQTT停止指令失败");
        }

        log.info("用户 {} 已请求停止充电会话 {}。等待机器人确认。", userId, sessionId);
        return true;
    }

    @Override
    public ChargingSession getActiveSessionByUserId(Long userId) {
        if (userId == null) {
            return null;
        }
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        // 扩大活动会话状态的定义，以便用户能看到早期状态
        List<String> activeStatuses = Arrays.asList(
                ChargingSessionStatusEnum.REQUESTED.getValue(),
                ChargingSessionStatusEnum.ROBOT_ASSIGNED.getValue(),
                ChargingSessionStatusEnum.ROBOT_EN_ROUTE.getValue(), // 假设此状态会被使用
                ChargingSessionStatusEnum.ROBOT_ARRIVED.getValue(),   // 假设此状态会被使用
                ChargingSessionStatusEnum.CHARGING_STARTED.getValue()
        );
        queryWrapper.in("status", activeStatuses);
        queryWrapper.orderByDesc("create_time"); 
        queryWrapper.last("LIMIT 1");
        return this.getOne(queryWrapper);
    }

    @Override
    public long countTodaySessions() {
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        LocalDateTime startOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        LocalDateTime endOfDay = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
        queryWrapper.ge("create_time", startOfDay); // 或者用 start_time，取决于业务定义
        queryWrapper.le("create_time", endOfDay);
        // 包括正在进行的和已完成/已支付的
        List<String> relevantStatuses = Arrays.asList(
                ChargingSessionStatusEnum.CHARGING_STARTED.getValue(), // 充电进行中
                // ChargingSessionStatusEnum.ROBOT_EN_ROUTE.getValue(), // 如果也算今日会话可以加上
                // ChargingSessionStatusEnum.ROBOT_ARRIVED.getValue(), // 如果也算
                ChargingSessionStatusEnum.CHARGING_COMPLETED.getValue(), // 充电已完成
                ChargingSessionStatusEnum.PAID.getValue() // 已支付
        );
        queryWrapper.in("status", relevantStatuses);
        return this.count(queryWrapper);
    }

    @Override
    public BigDecimal sumTodayRevenue() {
        // 获取今天的起始和结束时间
        LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
        LocalDateTime endOfDay = LocalDate.now().atTime(LocalTime.MAX);

        Date startDate = Date.from(startOfDay.atZone(java.time.ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(endOfDay.atZone(java.time.ZoneId.systemDefault()).toInstant());

        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", ChargingSessionStatusEnum.PAID.getValue());
        queryWrapper.between("charge_end_time", startDate, endDate); // 假设以充电结束时间为准
        queryWrapper.select("IFNULL(SUM(cost), 0) as totalRevenue"); // 使用IFNULL处理没有记录的情况

        Map<String, Object> result = this.getMap(queryWrapper);
        if (result != null && result.get("totalRevenue") != null) {
            // SUM(cost) 返回的可能是 BigDecimal 或 Double，取决于数据库和驱动
            Object revenueObj = result.get("totalRevenue");
            if (revenueObj instanceof BigDecimal) {
                return (BigDecimal) revenueObj;
            } else if (revenueObj instanceof Number) {
                return BigDecimal.valueOf(((Number) revenueObj).doubleValue());
            }
        }
        return BigDecimal.ZERO;
    }

    @Override
    public long countActiveSessions() {
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        // 定义哪些状态算作"活动中"
        List<String> activeStatuses = Arrays.asList(
                ChargingSessionStatusEnum.REQUESTED.getValue(),
                ChargingSessionStatusEnum.ROBOT_ASSIGNED.getValue(),
                ChargingSessionStatusEnum.ROBOT_ARRIVED.getValue(),
                ChargingSessionStatusEnum.CHARGING_STARTED.getValue(),
                ChargingSessionStatusEnum.PAYMENT_PENDING.getValue() // 用户停止充电后，等待支付也算活动
        );
        queryWrapper.in("status", activeStatuses);
        return this.count(queryWrapper);
    }

    @Override
    public BigDecimal getTotalRevenue() {
        QueryWrapper<ChargingSession> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", ChargingSessionStatusEnum.PAID.getValue());
        queryWrapper.select("IFNULL(SUM(cost), 0) as totalRevenue");

        Map<String, Object> result = this.getMap(queryWrapper);
        if (result != null && result.get("totalRevenue") != null) {
            Object revenueObj = result.get("totalRevenue");
            if (revenueObj instanceof BigDecimal) {
                return (BigDecimal) revenueObj;
            } else if (revenueObj instanceof Number) {
                return BigDecimal.valueOf(((Number) revenueObj).doubleValue());
            }
        }
        return BigDecimal.ZERO;
    }
} 