package com.fs.watch.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.fs.common.config.WatchConfig;
import com.fs.common.utils.DateUtils;
import com.fs.common.utils.StringUtils;
import com.fs.watch.calculation.SleepPreprocessor;
import com.fs.watch.domain.SleepSection;
import com.fs.watch.domain.WatchRriData;
import com.fs.watch.domain.WatchSleepData;
import com.fs.watch.domain.vo.AppWatchSleepDataVo;
import com.fs.watch.domain.vo.WatchSleepDataVo;
import com.fs.watch.mapper.WatchSleepDataMapper;
import com.fs.watch.service.WatchRriDataService;
import com.fs.watch.service.WatchSleepDataService;
import com.fs.watch.utils.ListConverterUtils;
import com.fs.watch.utils.MyHttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
* @author Administrator
* @description 针对表【watch_sleep_data(设备睡眠原始数据，储存用于算法计算睡眠结果)】的数据库操作Service实现
* @createDate 2024-10-15 11:06:51
*/
@Service
@Slf4j
public class WatchSleepDataServiceImpl implements WatchSleepDataService {
    @Autowired
    private WatchSleepDataMapper mapper;

    @Autowired
    private WatchRriDataService watchRriDataService;
    @Autowired
    private WatchConfig watchConfig;

    private static final MyHttpUtils httpUtils = new MyHttpUtils();

    @Override
    public Boolean insert(WatchSleepData watchSleepData) {
        //判断创建时间是否唯一
        Integer num = mapper.queryByCreateTime(watchSleepData.getCreateTime());
        if (num != null && num > 0) {
            return false;
        }
        try {
            mapper.insert(watchSleepData);
        } catch (Exception e) {
            log.error("===============watchSleepData:insert=========================={}", e.getMessage());
            return false;
        }

        return true;
    }

    private Map<String, Object> getSleepDataMap(WatchSleepData watchSleepData) {
        Map<String, Object> params = new HashMap<>();
        params.put("id", watchSleepData.getId().toString());
        params.put("deviceId", watchSleepData.getDeviceId());
        params.put("charget", watchSleepData.getCharget());
        params.put("shutdown", watchSleepData.getShutdown());
        params.put("dataCount", watchSleepData.getDataCount());
        params.put("sleepStr", watchSleepData.getSleepStr());
        params.put("seq", watchSleepData.getSeq());
        params.put("createTime", watchSleepData.getCreateTime());
        return params;
    }

    @Override
    public AppWatchSleepDataVo queryByDateAndDeviceIdAndState(String date, String deviceId) {
        //返回对象
        if (StringUtils.isBlank(date)) {
            date = DateUtils.dateTimeNow("yyyy-MM-dd");
        }
        WatchSleepDataVo vo = getSleepSection(date, deviceId);
        if (vo != null && vo.getSleepSection() != null && vo.getSleepSection().size() > 0) {
            return parserResult(deviceId, vo);
        }
        return null;
    }

    @Override
    public WatchSleepDataVo getSleepSection(String date, String deviceId) {
        WatchSleepDataVo vo = new WatchSleepDataVo();
        List<SleepSection> list = null;
        String preDate = this.getPreviousDateString(date);
        if (preDate != null) {
            //1.获取当天数据
            List<WatchSleepData> nextData = getByDateAndDeviceId(date, deviceId);
            List<WatchRriData> nextDayRri = watchRriDataService.getByDateAndDeviceId(date, deviceId);
            //2.获取前一天数据
            List<WatchSleepData> prevData = getByDateAndDeviceId(preDate, deviceId);
            List<WatchRriData> prevRri = watchRriDataService.getByDateAndDeviceId(preDate, deviceId);

            //3.组装算法参数
            HashMap<String, Object> param = getStringObjectHashMap(date.replace("-", ""), nextData, nextDayRri, prevData, prevRri);
            //2025.1.6新增三个参数
            param.put("device_id", deviceId);
//            param.put("account", watchConfig.getAccount());
//            param.put("password", watchConfig.getPassword());
            param.put("account", watchConfig.getAccount1());
            param.put("password", watchConfig.getPassword1());
            Map resData = httpUtils.getPost2("https://api1.iwown.com/algoservice", "/calculation/sleep", param);
            if (resData != null) {
                String startTime = resData.get("start_time").toString();
                String endTime = resData.get("end_time").toString();
                list = JSONObject.parseArray(JSONObject.toJSONString(resData.get("sections")), SleepSection.class);
                vo.setSleepSection(list);
                vo.setStartTime(startTime);
                vo.setEndTime(endTime);
                vo.setScore(Integer.parseInt(resData.get("score").toString()));
            } else {
                param.put("account", watchConfig.getAccount2());
                param.put("password", watchConfig.getPassword2());
                resData = httpUtils.getPost2("https://api1.iwown.com/algoservice", "/calculation/sleep", param);
                if (resData != null) {
                    String startTime = resData.get("start_time").toString();
                    String endTime = resData.get("end_time").toString();
                    list = JSONObject.parseArray(JSONObject.toJSONString(resData.get("sections")), SleepSection.class);
                    vo.setSleepSection(list);
                    vo.setStartTime(startTime);
                    vo.setEndTime(endTime);
                    vo.setScore(Integer.parseInt(resData.get("score").toString()));
                } else {
                    param.put("account", watchConfig.getAccount3());
                    param.put("password", watchConfig.getPassword3());
                    resData = httpUtils.getPost2("https://api1.iwown.com/algoservice", "/calculation/sleep", param);
                    if (resData != null) {
                        String startTime = resData.get("start_time").toString();
                        String endTime = resData.get("end_time").toString();
                        list = JSONObject.parseArray(JSONObject.toJSONString(resData.get("sections")), SleepSection.class);
                        vo.setSleepSection(list);
                        vo.setStartTime(startTime);
                        vo.setEndTime(endTime);
                        vo.setScore(Integer.parseInt(resData.get("score").toString()));
                    }
                }
            }

        }

        return vo;
    }

    private Integer calculateBreathingScore(List<WatchRriData> nextDayRri) {
        List<Integer> filteredRriData = new ArrayList<>();
        double sum = 0;
        int count = 0;

        for (WatchRriData data : nextDayRri) {
//            String jsonDataVal = data.getJsonDataVal();
//            List<Short> vals = ListConverterUtils.convertStringToShortList(jsonDataVal);
            List<Short> vals = data.getVal();
            for (Short val : vals) {
                if (val > 0) {  // 过滤掉负值
                    int intValue = val.intValue();
                    filteredRriData.add(intValue);
                    sum += intValue;  // 计算总和
                    count++;
                }
            }
        }

        // 计算均值
        double mean = count > 0 ? sum / count : 0.0;

        // 计算标准差
        double variance = 0.0;
        for (int val : filteredRriData) {
            variance += Math.pow(val - mean, 2);
        }
        variance /= count;
        double standardDeviation = Math.sqrt(variance);

        // 标准差的反比来计算得分
        double score = 100 - (standardDeviation * 10);
        return (int) Math.max(0, Math.min(100, score)); // 确保得分在0-100之间
    }

    @Override
    public AppWatchSleepDataVo list(Date startTime, Date endTime, String deviceId) {
        //返回对象
        AppWatchSleepDataVo vo = new AppWatchSleepDataVo();
        // 使用 Calendar 类来循环增加日期
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startTime);
        //返回 SleepSection
        List<SleepSection> sleepSection = new ArrayList<>();

        //深睡时间
        long sSleep = 0;
        //浅睡时间
        long lightSleep = 0;
        //清醒时间
        long weakSleep = 0;
        //快速眼动时间
        long eyemoveSleep = 0;
        //清醒次数
        Integer weakTime = 0;
        //夜间睡眠时间(深睡+浅睡)
        Integer sleepCount=0;
        long timeTotal = 0L; //睡眠总时间

        while (calendar.getTime().before(endTime) || calendar.getTime().equals(endTime)) {
            Date currentDate = calendar.getTime();
//                System.out.println(currentDate);
            WatchSleepDataVo watchSleepDataVo = getSleepSection(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, currentDate), deviceId);
            if (watchSleepDataVo != null && watchSleepDataVo.getSleepSection() !=null && watchSleepDataVo.getSleepSection().size() > 0) {
                AppWatchSleepDataVo appWatchSleepDataVo = parserResult(deviceId, watchSleepDataVo);
                //统计图对象
                sleepSection.addAll(appWatchSleepDataVo.getSleepSection());
                //深睡时间
                sSleep = sSleep + appWatchSleepDataVo.getDeepSleep();
                //浅睡时间
                lightSleep =lightSleep+ appWatchSleepDataVo.getLightSleep();
                //清醒时间
                weakSleep =weakSleep+ appWatchSleepDataVo.getWeakSleep();
                //快速眼动时间
                eyemoveSleep =eyemoveSleep+ appWatchSleepDataVo.getEyemoveSleep();
                //清醒次数
                weakTime = weakTime + appWatchSleepDataVo.getWeakTime();
                //夜间睡眠时间(深睡+浅睡)
                sleepCount=sleepCount+appWatchSleepDataVo.getSleepCount();

                timeTotal = timeTotal + appWatchSleepDataVo.getSleepTime();

                //呼吸质量 得分(百分制)
            }

            // 增加一天
            calendar.add(Calendar.DAY_OF_MONTH, 1);
        }
        vo.setDeviceId(deviceId);
        vo.setDeepSleep((int)sSleep);
        vo.setLightSleep((int)lightSleep);
        vo.setWeakSleep((int)weakSleep);
        vo.setEyemoveSleep((int)eyemoveSleep);
        vo.setSleepSection(sleepSection);
        vo.setWeakTime(weakTime);
        vo.setSleepCount(sleepCount);
        vo.setSleepTime((int)timeTotal);

        if (timeTotal == 0){
            vo.setDeepScale(0);
            //浅睡比例 46%
            vo.setLightScale(0);
            //快速眼动比例 26%
            vo.setEyemoveScale(0);
        } else {
            //深睡比例 28%
            vo.setDeepScale((int) (sSleep * 100 / timeTotal));
            //浅睡比例 46%
            vo.setLightScale((int) (lightSleep * 100 / timeTotal));
            //快速眼动比例 26%
            vo.setEyemoveScale((int) (eyemoveSleep * 100 / timeTotal));
        }
        //计算深睡连续性 得分(百分制)
        vo.setDeepScore(calculateDeepSleepScore(sSleep));
        //呼吸质量 得分(百分制)
        vo.setBreathScore(82);
        return vo;
    }

    @Override
    public WatchSleepData getLast(String deviceId) {
        return mapper.getLast(deviceId);
    }

    /**
     * 结果请求解析
     *
     * @param deviceId
     * @param watchSleepDataVo
     */
    private static AppWatchSleepDataVo parserResult(String deviceId, WatchSleepDataVo watchSleepDataVo) {
        AppWatchSleepDataVo vo = new AppWatchSleepDataVo();
        List<SleepSection> list = watchSleepDataVo.getSleepSection();

        Long timeTotal = 0L; //睡眠总时间
        Long sTime = 0L; //深睡时间3
        Long qTime = 0L; //浅睡4
        Long iTime = 0L; //清醒6
        Integer iCount = 0;
        Long kTime = 0L; // 快速眼动7
        //计算深睡连续性得分（百分制）
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");


        if (list != null && list.size() > 0) {
            for (SleepSection sleepSection : list) {
                /**
                 * {
                 *             "start": "2021-12-01 00:36",
                 *             "end": "2021-12-01 00:52",
                 *             "type": 4
                 *         },
                 */

                // 解析字符串为 LocalDateTime 对象
                LocalDateTime start = LocalDateTime.parse(sleepSection.getStart(), formatter);
                LocalDateTime end = LocalDateTime.parse(sleepSection.getEnd(), formatter);

                // 计算时间差（以分钟为单位）
                long minutes = Duration.between(start, end).toMinutes();

                Integer type = sleepSection.getType();
                switch (type) {
                    case 3:
                        sTime += minutes;
                        break;
                    case 4:
                        qTime += minutes;
                        break;
                    case 6:
                        iTime += minutes;
                        iCount++;
                        break;
                    case 7:
                        kTime += minutes;
                        break;
                    default:
                        break;
                }
                timeTotal = timeTotal + minutes;
            }
            //清醒次数
            vo.setWeakTime(iCount);
            //夜间睡眠时间(深睡+浅睡)
            vo.setSleepCount((int) (sTime + qTime));


            vo.setDeviceId(deviceId);
            vo.setStartTime(watchSleepDataVo.getStartTime());
            vo.setEndTime(watchSleepDataVo.getEndTime());
            vo.setDeepSleep(sTime.intValue());
            vo.setLightSleep(qTime.intValue());
            vo.setWeakSleep(iTime.intValue());
            vo.setEyemoveSleep(kTime.intValue());
            vo.setSleepTime(timeTotal.intValue());
            vo.setSleepSection(list);
            Integer score = watchSleepDataVo.getScore();
            if (score ==null || score ==0){
                //计算睡眠得分
                vo.setScore(calculateSleepScore(iCount, vo));
            } else {
                vo.setScore(score);
            }
            //深睡比例 28%
            vo.setDeepScale((int) (sTime * 100 / timeTotal));
            //浅睡比例 46%
            vo.setLightScale((int) (qTime * 100 / timeTotal));
            //快速眼动比例 26%
            vo.setEyemoveScale((int) (kTime * 100 / timeTotal));

        }
        return vo;
    }

    private static int calculateDeepSleepScore(Long sTime) {
        int MAX_SLEEP_HOURS = 6;
        // 满分标准为8小时深睡
        long maxDeepSleepMinutes = MAX_SLEEP_HOURS * 60;
        // 计算百分制得分
        int score = (int) (sTime * 100 / maxDeepSleepMinutes);
        // 确保得分不超过100分
        return Math.min(score, 100);
    }

    public static int calculateSleepScore(Integer numberOfAwakenings, WatchSleepDataVo sleepData) {
        // 计算总睡眠时间 (TST)
        int totalSleepTime = sleepData.getDeepSleep() + sleepData.getLightSleep() + sleepData.getEyemoveSleep();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        // 计算床上总时间
        LocalDateTime start = LocalDateTime.parse(sleepData.getStartTime(), formatter);
        LocalDateTime end = LocalDateTime.parse(sleepData.getEndTime(), formatter);
        long totalTimeInBed = Duration.between(start, end).toMinutes();

        // 计算夜间觉醒次数 (NA) numberOfAwakenings
        List<SleepSection> sections = sleepData.getSleepSection();

        // 计算夜间觉醒时间 (WASO)
        int wakeAfterSleepOnset = sleepData.getWeakSleep();

        // 计算入睡时间 (SL)
        int sleepLatency = 0;
        if (!sections.isEmpty()) {
            SleepSection firstSection = sections.get(0);
            if (firstSection.getType() == 6) {
                sleepLatency = Integer.parseInt(firstSection.getEnd()) - Integer.parseInt(firstSection.getStart());
            }
        }

        // 快速眼动睡眠时间 (REM)
        int remSleep = sleepData.getEyemoveSleep();

        // 计算睡眠效率 (SE)
        double sleepEfficiency = ((double) totalSleepTime / totalTimeInBed) * 100;

        // 根据之前定义的评分标准进行评分
        int tstScore = calculateTSTScore(totalSleepTime);
        int seScore = calculateSEScore(sleepEfficiency);
        int slScore = calculateSLScore(sleepLatency);
        int naScore = calculateNAScore(numberOfAwakenings);
        int remScore = calculateREMScore(remSleep);

        // 计算综合得分
        int totalScore = tstScore + seScore + slScore + naScore + remScore;

        return totalScore;
    }

    // 计算总睡眠时间评分
    private static int calculateTSTScore(int tst) {
        if (tst > 420) { // > 7小时
            return 20;
        } else if (tst >= 360) { // 6-7小时
            return 15;
        } else if (tst >= 300) { // 5-6小时
            return 10;
        } else {
            return 5;
        }
    }

    // 计算睡眠效率评分
    private static int calculateSEScore(double se) {
        if (se > 85) {
            return 20;
        } else if (se >= 75) {
            return 15;
        } else if (se >= 65) {
            return 10;
        } else {
            return 5;
        }
    }

    // 计算入睡时间评分
    private static int calculateSLScore(int sl) {
        if (sl < 15) {
            return 20;
        } else if (sl < 30) {
            return 15;
        } else if (sl < 60) {
            return 10;
        } else {
            return 5;
        }
    }

    // 计算觉醒次数评分
    private static int calculateNAScore(int na) {
        if (na == 0) {
            return 20;
        } else if (na <= 2) {
            return 15;
        } else if (na <= 4) {
            return 10;
        } else {
            return 5;
        }
    }

    // 计算快速眼动睡眠评分
    private static int calculateREMScore(int rem) {
        if (rem > 90) {
            return 20;
        } else if (rem >= 60) {
            return 15;
        } else if (rem >= 30) {
            return 10;
        } else {
            return 5;
        }
    }


    /**
     * 组装参数
     *
     * @param date
     * @param nextData
     * @param nextDayRri
     * @param prevData
     * @param prevRri
     * @return
     */
    private HashMap<String, Object> getStringObjectHashMap(String date, List<WatchSleepData> nextData, List<WatchRriData> nextDayRri, List<WatchSleepData> prevData, List<WatchRriData> prevRri) {
        List<String> nextDayListStr = new ArrayList<>();
        for (WatchSleepData watchSleepData : nextData) {
            nextDayListStr.add(watchSleepData.getSleepStr());
        }
        SleepPreprocessor sleepPreprocessor = new SleepPreprocessor(this);
        String nextDay = sleepPreprocessor.CombineSleepRawData(nextDayListStr);

        List<Short> nextDayRriListStr = new ArrayList<>();
        for (WatchRriData watchRriData : nextDayRri) {
//            String jsonDataVal = watchRriData.getJsonDataVal();
//            List<Short> vals = ListConverterUtils.convertStringToShortList(jsonDataVal);
            List<Short> vals = watchRriData.getVal();
            nextDayRriListStr.addAll(vals);
        }

        List<String> prevDayListStr = new ArrayList<>();
        for (WatchSleepData watchSleepData : prevData) {
            prevDayListStr.add(watchSleepData.getSleepStr());
        }
        String prevDay = sleepPreprocessor.CombineSleepRawData(prevDayListStr);

        List<Short> prevRriListStr = new ArrayList<>();
        for (WatchRriData watchRriData : prevRri) {
//            String jsonDataVal = watchRriData.getJsonDataVal();
//            List<Short> vals = ListConverterUtils.convertStringToShortList(jsonDataVal);
            List<Short> vals = watchRriData.getVal();
            prevRriListStr.addAll(vals);
        }
        HashMap<String, Object> param = new HashMap<>();
        param.put("prevDay", prevDay);
        param.put("nextDay", nextDay);
        param.put("prevDayRri", prevRriListStr);
        param.put("nextDayRri", nextDayRriListStr);
        param.put("recordDate", date);
        return param;
    }


    /**
     * 查询前一天日期
     *
     * @param dateStr
     * @return 前一天日期字符串
     */
    private String getPreviousDateString(String dateStr) {
        // 定义日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        try {
            // 将字符串解析为LocalDate
            LocalDate date = LocalDate.parse(dateStr, formatter);

            // 计算前一天的日期
            LocalDate previousDate = date.minusDays(1);

            // 将日期格式化为字符串
            return previousDate.format(formatter);
        } catch (DateTimeParseException e) {
            // 处理日期解析异常
            System.err.println("Invalid date format: " + dateStr);
            return null;
        }
    }

    /**
     * 查询某天某个设备的房颤数据
     *
     * @param date
     * @param deviceId
     * @return
     */
    public List<WatchSleepData> getByDateAndDeviceId(String date, String deviceId) {
        if (StringUtils.isBlank(date)) {
            date = DateUtils.dateTimeNow("yyyy-MM-dd");
        }
        return mapper.getByDateAndDeviceId(date, deviceId);
    }
}
