package com.fs.his.service.impl;

import com.fs.common.core.page.TableDataInfo;
import com.fs.common.core.redis.RedisCache;
import com.fs.common.exception.CustomException;
import com.fs.his.domain.FsDoctor;
import com.fs.his.mapper.FsDoctorMapper;
import com.fs.his.service.IPollingAssignDoctorService;
import com.fs.his.vo.PharmacistAssignmentVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class IPollingAssignDoctorServiceImpl implements IPollingAssignDoctorService {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private FsDoctorMapper doctorMapper;

    // 轮询计数器key
    private static final String ROUND_ROBIN_INDEX_KEY = "pharmacist:round:index";

    // 分配记录key前缀（用于后续查询）
    private static final String ASSIGNMENT_RECORD_KEY_PREFIX = "pharmacist:assign:record:";

    // 处方关联记录key前缀
    private static final String PRESCRIBE_ASSIGN_KEY_PREFIX = "pharmacist:prescribe:";

    @Override
    public FsDoctor getNextPharmacist(String prescribeCode) {
        // 1. 实时从数据库查询在线药师（按ID排序保证顺序）
        List<FsDoctor> onlineDoctors = doctorMapper.selectRandomOnlineDoctorForPackage();

        FsDoctor selectedDoctor;
        boolean isOnline;
        int onlineCount = onlineDoctors != null ? onlineDoctors.size() : 0;

        if (onlineDoctors != null && !onlineDoctors.isEmpty()) {
            // 有在线药师
            if (onlineDoctors.size() == 1) {
                // 只有一个在线药师，直接分配
                selectedDoctor = onlineDoctors.get(0);
                log.info("只有一个在线药师 - ID:{}, 姓名:{}",
                        selectedDoctor.getDoctorId(), selectedDoctor.getDoctorName());
            } else {
                // 多个在线药师，轮询分配
                selectedDoctor = roundRobinSelect(onlineDoctors);
                log.info("轮询分配在线药师 - ID:{}, 姓名:{}, 当前在线药师数:{}",
                        selectedDoctor.getDoctorId(), selectedDoctor.getDoctorName(), onlineCount);
            }
            isOnline = true;
        } else {
            // 无在线药师：兜底分配离线药师
            selectedDoctor = getOfflinePharmacist();
            isOnline = false;
            onlineCount = 0;
            log.info("无在线药师，分配离线药师 - ID:{}, 姓名:{}",
                    selectedDoctor.getDoctorId(), selectedDoctor.getDoctorName());
        }

        // 2. 保存完整的分配记录（关联处方编号）
        saveAssignmentRecord(selectedDoctor, prescribeCode, isOnline, onlineCount);

        return selectedDoctor;
    }

    @Override
    public FsDoctor getNextPharmacistWithPrescribeCode(String prescribeCode) {
        return getNextPharmacist(prescribeCode);
    }

    /**
     * 轮询选择药师（支持1-N个药师）
     * @param onlineDoctors 当前在线的药师列表（实时查询的）
     */
    private FsDoctor roundRobinSelect(List<FsDoctor> onlineDoctors) {
        // 如果只有1个药师，直接返回（不需要轮询）
        if (onlineDoctors.size() == 1) {
            return onlineDoctors.get(0);
        }

        // Redis原子自增实现轮询（2个及以上药师才需要）
        Long index = redisCache.incr(ROUND_ROBIN_INDEX_KEY, 1L);

        // 设置过期时间（7天，避免key永久存在）
        if (index == 1) {
            redisCache.expire(ROUND_ROBIN_INDEX_KEY, 7, TimeUnit.DAYS);
        }

        // 计算真实索引（从0开始,适配list下标从0开始）
        int realIndex = (int)((index - 1) % onlineDoctors.size());

        log.debug("轮询计数器 - 当前值:{}, 在线药师总数:{}, 选中索引:{}",
                index, onlineDoctors.size(), realIndex);

        return onlineDoctors.get(realIndex);
    }

    /**
     * 获取离线药师（兜底）
     */
    private FsDoctor getOfflinePharmacist() {
        FsDoctor doctor = doctorMapper.selectPackageFsDoctorType2Ids();
        if (doctor == null) {
            log.error("无可用药师");
            throw new CustomException("当前无可用药师，请稍后再试");
        }
        return doctor;
    }

    /**
     * 保存完整的分配记录（关联处方编号）
     */
    private void saveAssignmentRecord(FsDoctor doctor, String prescribeCode, boolean isOnline, int onlineCount) {
        try {
            String dateKey = new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date());
            String timeKey = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date());

            // 记录ID：时间戳_处方编号
            String recordId = System.currentTimeMillis() + "_" + prescribeCode;

            // 记录格式：记录ID|时间|处方编号|药师ID|药师姓名|是否在线|在线药师总数
            String recordValue = recordId + "|" +
                    timeKey + "|" +
                    prescribeCode + "|" +
                    doctor.getDoctorId() + "|" +
                    doctor.getDoctorName() + "|" +
                    (isOnline ? "在线" : "离线") + "|" +
                    onlineCount;

            // 1. 按日期存储的列表（用于按日期查询）
            String dateListKey = ASSIGNMENT_RECORD_KEY_PREFIX + dateKey;
            redisCache.setVoice(dateListKey, recordValue);
            redisCache.expire(dateListKey, 3, TimeUnit.DAYS);

            // 2. 按处方编号存储（用于通过处方号反查）
            String prescribeKey = PRESCRIBE_ASSIGN_KEY_PREFIX + prescribeCode;
            redisCache.setCacheObject(prescribeKey, recordValue, 3 * 24 * 60 * 60, TimeUnit.SECONDS);

            // 3. 按药师ID存储的列表（用于查询某个药师的分配记录）
            String doctorKey = ASSIGNMENT_RECORD_KEY_PREFIX + "doctor:" + doctor.getDoctorId() + ":" + dateKey;
            redisCache.setVoice(doctorKey, recordValue);
            redisCache.expire(doctorKey, 3, TimeUnit.DAYS);

            log.debug("保存分配记录成功 - 处方号:{}, 药师:{}, 记录ID:{}",
                    prescribeCode, doctor.getDoctorName(), recordId);

        } catch (Exception e) {
            log.error("保存分配记录失败 - 处方号:{}, 药师ID:{}", prescribeCode, doctor.getDoctorId(), e);
            // 不影响主流程
        }
    }

    /**
     * 根据处方编号查询分配的药师
     */
    @Override
    public String getPharmacistByPrescribeCode(String prescribeCode) {
        try {
            String prescribeKey = PRESCRIBE_ASSIGN_KEY_PREFIX + prescribeCode;
            return redisCache.getCacheObject(prescribeKey);
        } catch (Exception e) {
            log.error("查询处方分配记录失败 - 处方号:{}", prescribeCode, e);
            return null;
        }
    }

    /**
     * 查询某天的所有分配记录
     */
    @Override
    public List<String> getRecordsByDate(String date) {
        try {
            String dateListKey = ASSIGNMENT_RECORD_KEY_PREFIX + date;
            return redisCache.getCacheList(dateListKey);
        } catch (Exception e) {
            log.error("查询日期分配记录失败 - 日期:{}", date, e);
            return null;
        }
    }

    /**
     * 查询某药师某天的分配记录
     */
    @Override
    public List<String> getRecordsByDoctorAndDate(Long doctorId, String date) {
        try {
            String doctorKey = ASSIGNMENT_RECORD_KEY_PREFIX + "doctor:" + doctorId + ":" + date;
            return redisCache.getCacheList(doctorKey);
        } catch (Exception e) {
            log.error("查询药师分配记录失败 - 药师ID:{}, 日期:{}", doctorId, date, e);
            return null;
        }
    }

    /**
     * 重置轮询计数器（可选，一般用于测试）
     */
    @Override
    public void resetRoundRobin() {
        redisCache.deleteObject(ROUND_ROBIN_INDEX_KEY);
        log.info("轮询计数器已重置");
    }

    /**
     * 获取当前轮询索引（可选，用于监控）
     */
    @Override
    public Long getCurrentIndex() {
        return redisCache.getCacheObject(ROUND_ROBIN_INDEX_KEY);
    }

    // ========== 新增的分页查询方法 ==========

    @Override
    public TableDataInfo getRecordsByDateWithPage(String date, Integer pageNum, Integer pageSize) {
        try {
            // 1. 获取当天的所有记录
            List<String> records = getRecordsByDate(date);
            if (records == null || records.isEmpty()) {
                TableDataInfo dataInfo = new TableDataInfo(new ArrayList<>(), 0);
                dataInfo.setCode(200);
                dataInfo.setMsg("查询成功");
                return dataInfo;
            }

            // 2. 转换为VO并排序（最新的在前面）
            List<PharmacistAssignmentVO> voList = convertToVOList(records, date);

            // 3. 计算分页
            int total = voList.size();
            int fromIndex = (pageNum - 1) * pageSize;
            int toIndex = Math.min(fromIndex + pageSize, total);

            List<PharmacistAssignmentVO> pageList;
            if (fromIndex >= total) {
                pageList = new ArrayList<>();
            } else {
                pageList = voList.subList(fromIndex, toIndex);
            }

            // 4. 返回TableDataInfo
            TableDataInfo dataInfo = new TableDataInfo(pageList, total);
            dataInfo.setCode(200);
            dataInfo.setMsg("查询成功");
            return dataInfo;

        } catch (Exception e) {
            log.error("分页查询分配记录失败 - 日期:{}", date, e);
            TableDataInfo dataInfo = new TableDataInfo(new ArrayList<>(), 0);
            dataInfo.setCode(500);
            dataInfo.setMsg("查询失败：" + e.getMessage());
            return dataInfo;
        }
    }

    @Override
    public TableDataInfo getRecordsByDoctorAndDateWithPage(Long doctorId, String date, Integer pageNum, Integer pageSize) {
        try {
            // 1. 获取药师当天的记录
            List<String> records = getRecordsByDoctorAndDate(doctorId, date);
            if (records == null || records.isEmpty()) {
                TableDataInfo dataInfo = new TableDataInfo(new ArrayList<>(), 0);
                dataInfo.setCode(200);
                dataInfo.setMsg("查询成功");
                return dataInfo;
            }

            // 2. 转换为VO并排序
            List<PharmacistAssignmentVO> voList = convertToVOList(records, date);

            // 3. 计算分页
            int total = voList.size();
            int fromIndex = (pageNum - 1) * pageSize;
            int toIndex = Math.min(fromIndex + pageSize, total);

            List<PharmacistAssignmentVO> pageList;
            if (fromIndex >= total) {
                pageList = new ArrayList<>();
            } else {
                pageList = voList.subList(fromIndex, toIndex);
            }

            // 4. 返回TableDataInfo
            TableDataInfo dataInfo = new TableDataInfo(pageList, total);
            dataInfo.setCode(200);
            dataInfo.setMsg("查询成功");
            return dataInfo;

        } catch (Exception e) {
            log.error("分页查询药师分配记录失败 - 药师ID:{}, 日期:{}", doctorId, date, e);
            TableDataInfo dataInfo = new TableDataInfo(new ArrayList<>(), 0);
            dataInfo.setCode(500);
            dataInfo.setMsg("查询失败：" + e.getMessage());
            return dataInfo;
        }
    }

    /**
     * 将Redis记录列表转换为VO列表（按时间倒序）
     */
    private List<PharmacistAssignmentVO> convertToVOList(List<String> records, String date) {
        List<PharmacistAssignmentVO> voList = new ArrayList<>();

        for (String record : records) {
            PharmacistAssignmentVO vo = parseRecordToVO(record);
            if (vo != null) {
                vo.setAssignDate(date);
                voList.add(vo);
            }
        }

        // 按recordId倒序排序（最新的在前面）
        voList.sort((a, b) -> b.getRecordId().compareTo(a.getRecordId()));

        return voList;
    }

    /**
     * 解析单条记录为VO
     */
    private PharmacistAssignmentVO parseRecordToVO(String record) {
        try {
            String[] parts = record.split("\\|");
            if (parts.length >= 7) {
                PharmacistAssignmentVO vo = new PharmacistAssignmentVO();
                vo.setRecordId(parts[0]);           // 记录ID（包含时间戳）
                vo.setAssignTime(parts[1]);          // 分配时间
                vo.setPrescribeCode(parts[2]);       // 处方编号
                vo.setDoctorId(Long.parseLong(parts[3])); // 药师ID
                vo.setDoctorName(parts[4]);           // 药师姓名
                vo.setOnlineStatus(parts[5]);         // 在线状态
                vo.setOnlineCount(Integer.parseInt(parts[6])); // 在线药师总数
                return vo;
            }
        } catch (Exception e) {
            log.error("解析记录失败: {}", record, e);
        }
        return null;
    }

    /**
     * 获取当前药师数量情况（用于监控）
     */
    public java.util.Map<String, Object> getPharmacistStats() {
        java.util.Map<String, Object> stats = new java.util.HashMap<>();

        List<FsDoctor> onlineDoctors = doctorMapper.selectRandomOnlineDoctorForPackage();
        stats.put("onlineCount", onlineDoctors != null ? onlineDoctors.size() : 0);
        stats.put("onlineDoctors", onlineDoctors != null ?
                onlineDoctors.stream()
                        .map(d -> d.getDoctorId() + "-" + d.getDoctorName())
                        .collect(Collectors.toList()) :
                new ArrayList<>());

        stats.put("currentIndex", redisCache.getCacheObject(ROUND_ROBIN_INDEX_KEY));
        stats.put("usingRoundRobin", onlineDoctors != null && onlineDoctors.size() > 1);

        return stats;
    }
}