package com.ruoyi.system.service.impl;

import com.ruoyi.system.domain.DailyUser;
import com.ruoyi.system.domain.StUser;
import com.ruoyi.system.domain.SysUserOnlineTime;
import com.ruoyi.system.domain.UserInfo;
import com.ruoyi.system.mapper.DailyUserMapper;
import com.ruoyi.system.mapper.StUserMapper;
import com.ruoyi.system.service.ISysUserOnlineTimeService;
import com.ruoyi.system.service.IUserInfoService;
import org.apache.poi.ss.formula.functions.Now;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.Schedules;
import org.springframework.stereotype.Service;

//import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Year;
import java.time.ZoneId;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import java.util.Date;
import java.util.prefs.BackingStoreException;

@Service
public class SysUserOnlineTimeServiceImpl implements ISysUserOnlineTimeService {

    private final ConcurrentHashMap<Long, Date> userLastHeartbeatMap = new ConcurrentHashMap<>();

    @Autowired
    private DailyUserMapper dailyUserMapper;

    @Autowired
    private StUserMapper stUserMapper;

    @Autowired
    private IUserInfoService userInfoService;

    @Override
    public void recordUserLogin(Long userId){

        System.out.println("用户登录" + userId);

        Date now = new Date();
        LocalDate localNow = LocalDate.now();
        //若当前用户今天没登录过,每天登录中用户为null
        DailyUser dailyUser = dailyUserMapper.selectByUserIdAndDate(userId, localNow);
        if (dailyUser == null){
            //初始化daily_user用户数据
            dailyUser = new DailyUser();
            dailyUser.setUserId(userId);
            dailyUser.setOnlineTime(0L);
            dailyUser.setMrTime(0L);
            dailyUser.setConsumption(0L);
            dailyUser.settWork(0L);
            dailyUser.setCreateTime(now);
            dailyUser.setUpdateTime(now);
            // 这里是用户每天的记录,所以日期只记录到天
            dailyUser.setDate(localNow);

            dailyUserMapper.insertDailyUser(dailyUser);

        }
        //心跳初始化时间
        userLastHeartbeatMap.put(userId, now);
    }

    //记录登出时间, 同时更新在线时间数据
    @Override
    public int recordUserLogout(Long userId){

        //先更新在线时间
        Date now = new Date();
        Date lastHeartbeat = userLastHeartbeatMap.get(userId);
        if (lastHeartbeat != null){
            updateOnlineTime(userId, lastHeartbeat, now);
        }

        //移除websocket中的心跳对.
        userLastHeartbeatMap.remove(userId);

        System.out.println("用户退出时间:" + now);
        return 1;
    }

    // 更新心跳时间
    @Override
    public int updateUserHeartbeat(Long userId, Long duration){


        Date now = new Date();
        Date lastHeartbeat = userLastHeartbeatMap.get(userId);

        System.out.println("上一次心跳时间:" + lastHeartbeat);
        int back = 0;
        //如果上一次心跳时间不为空
        if (lastHeartbeat != null){
            back = updateOnlineTime(userId, lastHeartbeat, now);
        }

        userLastHeartbeatMap.put(userId, now);
        System.out.println("更新用户心跳时间");
        return back;
    }

    private int updateOnlineTime(Long userId, Date startTime, Date endTime){
        //-----------------------------------更新用户在线时间------------------------------------------
        // 获取开始时间的日期部分
        LocalDate startDate = startTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();

        // 获取结束时间的日期部分
        LocalDate endDate = endTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();

        // 根据今天的日期获取用户数据
        // 以开始时间作为查询的话,会导致若用户一直不退出,今天的在线时长会一直增加,能超出一天的总时长
        // 所以这里用结束时间查询,由于日期只记录到天,同一天开始和结束应该一样.
        DailyUser dailyUser = dailyUserMapper.selectByUserIdAndDate(userId, endDate);
        if (dailyUser != null){
            System.out.println("用户当天登录过");
            int back = updateSingleDayOnlineTime(userId, startTime, endTime);
            return back;

        }
        // 若更新时查不到用户记录-------------------用户登录时跨天了------------------------------------
        else{
            System.out.println("用户前一天登录,直到现在");
            //所以前一天的在线时长到 前一天的23:59:59
            Date firstDayEnd = Date.from(startDate.atTime(23, 59, 59)
                    .atZone(ZoneId.systemDefault())
                    .toInstant());
            updateSingleDayOnlineTime(userId, startTime, firstDayEnd);
            // 计算最后一天的在线时长（从当天开始00:00:00到结束时间）
            Date lastDayStart = Date.from(endDate.atStartOfDay()
                    .atZone(ZoneId.systemDefault())
                    .toInstant());

            int back = updateSingleDayOnlineTime(userId, startTime, lastDayStart);
            return back;
        }
        //----------------------------------------------end 更新用户在线时间-----------------------------------------------

    }

    private int updateSingleDayOnlineTime(Long userId, Date startTime, Date endTime) {
        long duration = (endTime.getTime() - startTime.getTime()) / 1000; // 转换为秒

        LocalDate startDate = startTime.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();

        DailyUser dailyUser = dailyUserMapper.selectByUserIdAndDate(userId, startDate);
        // 不跨天
        if (dailyUser != null) {

            System.out.println("不跨天");
            // 更新在线时长，使用userId和date作为条件
            int back = dailyUserMapper.updateByUserIdAndDate(dailyUser.getOnlineTime() + duration, userId, startDate, endTime);
            return back;
        //跨天的情况,
        } else {
            // 如果记录不存在，创建新记录
            dailyUser = new DailyUser();
            dailyUser.setUserId(userId);
            dailyUser.setOnlineTime(0L);
            dailyUser.setMrTime(0L);
            dailyUser.setConsumption(0L);
            dailyUser.settWork(0L);
            dailyUser.setCreateTime(startTime);
            dailyUser.setUpdateTime(startTime);
            // 这里是用户每天的记录,所以日期只记录到天
            dailyUser.setDate(startDate);
            int back = dailyUserMapper.insertDailyUser(dailyUser);
            int back2 = dailyUserMapper.updateByUserIdAndDate(duration, userId, startDate, endTime);
            if (back2 == 0 || back == 0){
                return 0;
            }
            return 1;

        }
    }

    //统计用户7/30天在线时长, 可以用定时任务,每天设置一个时间点计算
//    @Scheduled(cron="0 */5 * * * *")
//    public void test(){
//        Date date = new Date();
//        System.out.println("定时任务输出"+date);
//    }
    // 每天凌晨 1 点计算近7/30天的在线时长
//    @Scheduled(cron="0 0 1 * * ?")
//    @Scheduled(cron="0 * * * * ?")
    @Override
    public void calculateUserOnlineStats() {

        Date today = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());

        //获取所有用户, 假设用户在创建账号时就在st_user表中创建数据
        List<Long> userIds = stUserMapper.getAllUserIds();

        System.out.println("用户总数:"+userIds.size());

        //计算每个用户的7/30天在线时长, 每天计算前7天的在线时间
        /*
        * 近7天时间 = 当前近7天时间-7天前的时间+前1天时间
        * 今天 8月9日---计算8月8日的近7天时间------8月7日的近7天时间+8月8日的在线时间-8月1日的在线时间
        * 近30天时间同理
        * */
        for(Long userId : userIds){

            // 获取当前日期. 默认到天吧
            LocalDate now = LocalDate.now();

            // 计算前1天、前2天前8天前30天的日期,然后根据日期查找在线时间
            LocalDate oneDayAgo = now.minusDays(1);
//            LocalDate twoDaysAgo = now.minusDays(2);
            LocalDate eightDaysAgo = now.minusDays(8);

            LocalDate thirtyDaysAgo = now.minusDays(31);

            DailyUser oneDayAgoUser = dailyUserMapper.selectByUserIdAndDate(userId, oneDayAgo);
            Long oneDayAgoOnlineTime = oneDayAgoUser != null ? oneDayAgoUser.getOnlineTime() : 0L;

            DailyUser eightDaysAgoUser = dailyUserMapper.selectByUserIdAndDate(userId, eightDaysAgo);
            Long eightDaysAgoOnlineTime = eightDaysAgoUser != null ? eightDaysAgoUser.getOnlineTime() : 0L;

            DailyUser thirtyDaysAgoUser = dailyUserMapper.selectByUserIdAndDate(userId, thirtyDaysAgo);
            Long thirtyDaysAgoOnlineTime = thirtyDaysAgoUser != null ? thirtyDaysAgoUser.getOnlineTime() : 0L;

            // st_user表中每个用户应该只有一行数据,所以直接通过userId来获取
            StUser stUser = stUserMapper.selectStUserByUserId(userId);
            // 应该一定不会为null吧
            Long last7daysTime = stUser.getSeOnlineTime();

            Long new7daysTime = last7daysTime + oneDayAgoOnlineTime - eightDaysAgoOnlineTime;

            Long last30daysTime = stUser.getThOnlineTime();

            Long new30daysTime = last30daysTime + oneDayAgoOnlineTime - thirtyDaysAgoOnlineTime;

            //重写数据
            stUser.setSeOnlineTime(new7daysTime);
            stUser.setThOnlineTime(new30daysTime);
            stUser.setUpdateTime(new Date());
            stUser.setDate(oneDayAgo);
            //更新数据库
            int back = stUserMapper.updateStUser(stUser);
            if(back == 0){
                System.out.println(userId+"用户更新数据失败");
            }

            // 更新在线总时长
            UserInfo userInfo = userInfoService.selectUserInfoByUserId(userId);
            userInfo.settOnlineTime(userInfo.gettOnlineTime()+oneDayAgoOnlineTime);
            userInfo.setsOnlineTime(new7daysTime);
            int back2 = userInfoService.updateUserInfo(userInfo);
            if(back2 == 0){
                System.out.println(userId+"user_info更新在线总时长失败");
            }

        }
    }

}
