package com.haha.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haha.common.enums.DeviceDoorStatus;
import com.haha.common.enums.RecognizeConsumeType;
import com.haha.entity.DoorRecord;
import com.haha.mapper.DoorRecordMapper;
import com.haha.service.DoorRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 设备开关门记录服务实现类
 *
 * @author haha
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DoorRecordServiceImpl extends ServiceImpl<DoorRecordMapper, DoorRecord> implements DoorRecordService {

    private final StringRedisTemplate stringRedisTemplate;
    private static final String DOOR_RECORD_REDIS_KEY = "haha:door:open:";
    private static final long REDIS_EXPIRE_MINUTES = 30; // Redis 过期时间 30 分钟

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DoorRecord saveDoorOpenRecord(String deviceId, Long userId, String activityId,
                                         String doorIndex, String openType, String source) {
        try {
            LocalDateTime now = LocalDateTime.now();

            // 1. 保存到数据库
            DoorRecord record = new DoorRecord();
            record.setActivityId(activityId);
            record.setDeviceId(deviceId);
            record.setUserId(userId);
            record.setDoorIndex(doorIndex);
            record.setOpenType(openType);
            record.setDoorStatus(DeviceDoorStatus.OPENED.getCode());
            record.setNobuy(RecognizeConsumeType.CONSUMED.getCode()); // 默认假设有消费，后续 AI 识别会更新
            record.setOpenTime(now);
            record.setSource(source);

            boolean saved = this.save(record);
            if (saved) {
                log.info("保存开门记录到数据库 - activityId: {}, deviceId: {}, userId: {}",
                        activityId, deviceId, userId);
            } else {
                log.error("保存开门记录到数据库失败 - activityId: {}", activityId);
            }

            // 2. 同时保存到 Redis（用于快速查询和临时状态）
            saveToRedis(activityId, deviceId, userId, doorIndex, openType, now, source);

            return record;

        } catch (Exception e) {
            log.error("保存开门记录失败 - activityId: {}", activityId, e);
            throw new RuntimeException("保存开门记录失败", e);
        }
    }

    @Override
    public DoorRecord getByActivityId(String activityId) {
        // 优先从数据库查询
        return baseMapper.getByActivityId(activityId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateDoorClosed(String activityId) {
        try {
            DoorRecord record = getByActivityId(activityId);
            if (record == null) {
                log.warn("关门记录不存在 - activityId: {}", activityId);
                return false;
            }

            LocalDateTime closeTime = LocalDateTime.now();
            int result = baseMapper.updateDoorStatus(activityId, DeviceDoorStatus.CLOSED.getCode(), closeTime);

            if (result > 0) {
                log.info("更新门状态为已关门 - activityId: {}, 持续时长：{}秒", activityId, record.getDuration());

                // 同时更新 Redis
                updateRedisDoorStatus(activityId, DeviceDoorStatus.CLOSED.getCode(), closeTime);
                return true;
            } else {
                log.error("更新门状态失败 - activityId: {}", activityId);
                return false;
            }
        } catch (Exception e) {
            log.error("更新门状态失败 - activityId: {}", activityId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean markNoConsume(String activityId) {
        try {
            int result = baseMapper.markNoConsume(activityId);
            if (result > 0) {
                log.info("标记无消费记录 - activityId: {}", activityId);

                // 同时更新 Redis
                updateRedisNobuy(activityId, RecognizeConsumeType.NO_CONSUME.getCode());
                return true;
            } else {
                log.error("标记无消费记录失败 - activityId: {}", activityId);
                return false;
            }
        } catch (Exception e) {
            log.error("标记无消费记录失败 - activityId: {}", activityId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean linkOrderId(String activityId, Long orderId) {
        try {
            int result = baseMapper.linkOrderId(activityId, orderId);
            if (result > 0) {
                log.info("关联订单 ID - activityId: {}, orderId: {}", activityId, orderId);

                // 同时更新 Redis
                updateRedisOrderId(activityId, orderId);
                return true;
            } else {
                log.error("关联订单 ID 失败 - activityId: {}", activityId);
                return false;
            }
        } catch (Exception e) {
            log.error("关联订单 ID 失败 - activityId: {}", activityId, e);
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updatePayScorePreAuth(String activityId, String payScoreOrderId, String serviceId) {
        try {
            int result = baseMapper.updatePayScorePreAuth(activityId, payScoreOrderId, serviceId);
            if (result > 0) {
                log.info("支付分预授权信息已持久化到开门记录 - activityId: {}, payScoreOrderId: {}", activityId, payScoreOrderId);
                return true;
            } else {
                log.warn("更新支付分预授权信息失败（记录不存在） - activityId: {}", activityId);
                return false;
            }
        } catch (Exception e) {
            log.error("更新支付分预授权信息异常 - activityId: {}", activityId, e);
            return false;
        }
    }

    @Override
    public List<DoorRecord> getRecentRecords(Long userId, String deviceId, int limit) {
        return baseMapper.getRecentRecords(userId, deviceId, limit);
    }

    @Override
    public int countByTimeRange(String deviceId, LocalDateTime startTime, LocalDateTime endTime) {
        return baseMapper.countByTimeRange(deviceId, startTime, endTime);
    }

    @Override
    public void cleanupExpiredRedisRecords() {
        // 这个方法可以定时调用，清理过期的 Redis 记录
        // 由于数据库有唯一索引，不用担心数据丢失
        log.info("清理过期 Redis 开门记录任务已执行");
    }

    @Override
    public com.baomidou.mybatisplus.core.metadata.IPage<DoorRecord> getDeviceRecordsByPage(
            String deviceId, int page, int pageSize) {
        LambdaQueryWrapper<DoorRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DoorRecord::getDeviceId, deviceId)
               .orderByDesc(DoorRecord::getOpenTime);

        return this.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, pageSize), wrapper);
    }

    // ==================== Redis 操作方法 ====================

    private void saveToRedis(String activityId, String deviceId, Long userId,
                            String doorIndex, String openType, LocalDateTime openTime, String source) {
        try {
            String key = DOOR_RECORD_REDIS_KEY + activityId;
            Map<String, String> record = new HashMap<>();
            record.put("activityId", activityId);
            record.put("deviceId", deviceId);
            record.put("userId", String.valueOf(userId));
            record.put("doorIndex", doorIndex != null ? doorIndex : "");
            record.put("openType", openType);
            record.put("openTime", String.valueOf(openTime.toEpochSecond(java.time.ZoneOffset.UTC)));
            record.put("source", source != null ? source : "");
            record.put("status", DeviceDoorStatus.OPENED.getCode());

            stringRedisTemplate.opsForHash().putAll(key, record);
            stringRedisTemplate.expire(key, REDIS_EXPIRE_MINUTES, TimeUnit.MINUTES);

            log.debug("保存开门记录到 Redis: activityId={}", activityId);
        } catch (Exception e) {
            log.error("保存开门记录到 Redis 失败 - activityId: {}", activityId, e);
            // Redis 失败不影响数据库操作
        }
    }

    private void updateRedisDoorStatus(String activityId, String doorStatus, LocalDateTime closeTime) {
        try {
            String key = DOOR_RECORD_REDIS_KEY + activityId;
            stringRedisTemplate.opsForHash().put(key, "doorStatus", doorStatus);
            stringRedisTemplate.opsForHash().put(key, "closeTime",
                String.valueOf(closeTime.toEpochSecond(java.time.ZoneOffset.UTC)));
            log.debug("更新 Redis 门状态 - activityId: {}, status: {}", activityId, doorStatus);
        } catch (Exception e) {
            log.error("更新 Redis 门状态失败 - activityId: {}", activityId, e);
        }
    }

    private void updateRedisNobuy(String activityId, int nobuy) {
        try {
            String key = DOOR_RECORD_REDIS_KEY + activityId;
            stringRedisTemplate.opsForHash().put(key, "nobuy", String.valueOf(nobuy));
            log.debug("更新 Redis 无消费标记 - activityId: {}", activityId);
        } catch (Exception e) {
            log.error("更新 Redis 无消费标记失败 - activityId: {}", activityId, e);
        }
    }

    private void updateRedisOrderId(String activityId, Long orderId) {
        try {
            String key = DOOR_RECORD_REDIS_KEY + activityId;
            stringRedisTemplate.opsForHash().put(key, "orderId", String.valueOf(orderId));
            log.debug("更新 Redis 订单 ID - activityId: {}, orderId: {}", activityId, orderId);
        } catch (Exception e) {
            log.error("更新 Redis 订单 ID 失败 - activityId: {}", activityId, e);
        }
    }
}
