package jnpf.culture.service.impl;

import cn.xuyanwu.spring.file.storage.FileInfo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jnpf.base.SysConfigApi;
import jnpf.base.UserInfo;
import jnpf.base.model.systemconfig.SysConfigModel;
import jnpf.base.service.SuperServiceImpl;
import jnpf.constant.FileTypeConstant;
import jnpf.culture.mapper.*;
import jnpf.culture.service.CultureClockInService;
import jnpf.culture.util.ImageComboUtil;
import jnpf.entity.culture.*;
import jnpf.exception.HandleException;
import jnpf.exception.QueryException;
import jnpf.file.FileApi;
import jnpf.file.FileUploadApi;
import jnpf.model.culture.vo.CultureClockInVo;
import jnpf.model.culture.vo.RecordDataVo;
import jnpf.model.culture.vo.RecordListVo;
import jnpf.model.culture.vo.YearDataVo;
import jnpf.permission.V2UserApi;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.util.*;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.awt.image.BufferedImage;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 文化打卡服务实现
 *
 * @author yanwenfu
 * @create 2025-12-23
 */
@Service
public class CultureClockInServiceImpl extends SuperServiceImpl<CultureClockInMapper, CultureClockIn> implements CultureClockInService {

    @Resource
    private CultureClockInStatMapper cultureClockInStatMapper;
    @Resource
    private CultureClockInMapper cultureClockInMapper;
    @Resource
    private CultureTextSettingMapper cultureTextSettingMapper;
    @Resource
    private CulturePicSettingMapper culturePicSettingMapper;
    @Resource
    private CulturePicTempMapper culturePicTempMapper;
    @Autowired
    private FileUploadApi fileUploadApi;
    @Autowired
    private FileApi fileApi;
    @Autowired
    private SysConfigApi sysConfigApi;
    @Autowired
    private ImageComboUtil imageComboUtil;
    @Autowired
    private V2UserApi v2UserApi;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public MutablePair<String, BufferedImage> getRandomPicPreview(String lastCombo, String requestUrl) throws Exception {

        UserInfo user = UserProvider.getUser();
        // 查询最近2天被删除的图片id和文字id
        LocalDate today = LocalDate.now();
        LocalDateTime startTime = today.minusDays(1).atStartOfDay();
        LocalDateTime endTime = today.plusDays(1).atStartOfDay();
        List<String> delPicIds = culturePicSettingMapper.getDelIdsByDate(startTime, endTime);
        List<String> delTextIds = cultureTextSettingMapper.getDelIdsByDate(startTime, endTime);
        if (imageComboUtil.checkNeedGenerate()) {
            // 生成新的组合池
            List<String> allPicIds = culturePicSettingMapper.selectAllIds();
            List<String> allTextIds = cultureTextSettingMapper.selectAllIds();
            imageComboUtil.generateComboPool(allPicIds, allTextIds, delPicIds, delTextIds);
        }
        String randomCombo = imageComboUtil.getRandomCombo(lastCombo, user.getUserId(), delPicIds, delTextIds);
        if (null == randomCombo) {
            List<String> allPicIds = culturePicSettingMapper.selectAllIds();
            List<String> allTextIds = cultureTextSettingMapper.selectAllIds();
            imageComboUtil.generateComboPool(allPicIds, allTextIds, delPicIds, delTextIds);
            randomCombo = imageComboUtil.getRandomCombo(lastCombo, user.getUserId(), delPicIds, delTextIds);
            if (null == randomCombo) {
                throw new QueryException("获取图片失败，请重试");
            }
        }
        BufferedImage finalImage = generateClockImage(randomCombo, requestUrl, user, ConstantUtil.NUM_FALSE);
        return MutablePair.of(randomCombo, finalImage);
    }

    /**
     * 生成打卡图片
     * @param randomCombo 随机组合
     * @param requestUrl 请求地址
     * @param user 用户信息
     * @param clockType 0: 预览, 1: 打卡
     * @return java.awt.image.BufferedImage
     */
    private BufferedImage generateClockImage(String randomCombo, String requestUrl, UserInfo user, Integer clockType) throws Exception {

        // 查询图片缓存中是否有当前组合
        String[] split = randomCombo.split("_");
        LambdaQueryWrapper<CulturePicTemp> tempQuery = new LambdaQueryWrapper<CulturePicTemp>()
                .eq(CulturePicTemp::getPicId, split[0])
                .eq(CulturePicTemp::getContentId, split[1]);
        CulturePicTemp tempPic = culturePicTempMapper.selectOne(tempQuery);
        String tempUrl;
        if (null == tempPic) {
            // 查询图片、文案原始数据
            CulturePicSetting pic = culturePicSettingMapper.selectById(split[0]);
            CultureTextSetting text = cultureTextSettingMapper.selectById(split[1]);
            // 生成图片并上传到oss
            // 查询企业头像
            String companyIconUrl = getCompanyIconUrl(requestUrl);
            BufferedImage baseImage = imageComboUtil.generateBaseImage(requestUrl + pic.getPicUrl(), companyIconUrl, text.getCultureTitle(), text.getCultureContent());
            MultipartFile multiFile = imageComboUtil.bufferedImageToMultipartFile(baseImage);
            String path = fileApi.getPath(FileTypeConstant.TEMPORARY);
            FileInfo fileInfo = fileUploadApi.uploadFileCustomName(multiFile, path, multiFile.getOriginalFilename());
            tempUrl = ConstantUtil.URL_HEAD + fileInfo.getBasePath() + fileInfo.getPath() + fileInfo.getOriginalFilename();
            // 1.3 存入临时表
            CulturePicTemp newTemp = new CulturePicTemp();
            newTemp.setId(FtbUtil.getId());
            newTemp.setPicId(split[0]);
            newTemp.setContentId(split[1]);
            newTemp.setTempUrl(tempUrl);
            culturePicTempMapper.insert(newTemp);
        } else {
            tempUrl = tempPic.getTempUrl();
        }
        // 打卡图片并添加 头像 姓名 已累计打卡xx天 打卡时间
        LambdaQueryWrapper<CultureClockInStat> statQuery = new LambdaQueryWrapper<CultureClockInStat>()
                .eq(CultureClockInStat::getUserId, user.getUserId())
                .eq(CultureClockInStat::getDeleteMark, ConstantUtil.NUM_FALSE);
        CultureClockInStat stat = cultureClockInStatMapper.selectOne(statQuery);
        String str;
        if (null == stat) {
            str = ConstantUtil.NUM_FALSE == clockType ? "0" : "1";
        } else {
            if (ConstantUtil.NUM_FALSE == clockType || stat.getLastClockInDate().equals(LocalDate.now())) {
                str = stat.getTotalDays().toString();
            } else {
                str = String.valueOf(stat.getTotalDays() + 1);
            }
        }
        MutablePair<String, String> pair = getUserIconUrl(user.getUserId(), user.getTenantId(), requestUrl);
        return imageComboUtil.generateFinalImage(tempUrl, pair.getRight(), pair.getLeft(), str);
    }

    private String getCompanyIconUrl(String requestUrl) {

        SysConfigModel configModel = sysConfigApi.getSysConfigModel();
        String companyIconUrl;
        if (null == configModel || StringUtil.isEmpty(configModel.getAppIcon())) {
            companyIconUrl = "https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com/jnpf-resources/SystemFile/company-default-logo.png";
        } else {
            companyIconUrl = requestUrl + configModel.getAppIcon();
        }
        return companyIconUrl;
    }

    private MutablePair<String, String> getUserIconUrl(String userId, String tenantId, String requestUrl) {

        List<UserBoundVO> list = v2UserApi.userListAndCopy(List.of(userId), null, tenantId);
        String userIconUrl = "https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com/jnpf-resources/SystemFile/user-default-icon.png";
        String userName = "未知";
        if (null != list && !list.isEmpty()) {
            UserBoundVO user = list.get(0);
            if (StringUtil.isNotEmpty(user.getHeadIcon())) {
                userIconUrl = requestUrl + UploaderUtil.uploaderImg(list.get(0).getHeadIcon());
            }
            if (StringUtil.isNotEmpty(user.getUserName())) {
                userName = user.getUserName();
            }
        }
        return MutablePair.of(userName, userIconUrl);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public CultureClockInVo getCultureClockInPic(String currentCombo, String requestUrl) throws Exception {

        UserInfo user = UserProvider.getUser();
        // 查询用户今日打卡次数
        LambdaQueryWrapper<CultureClockIn> clockQuery = new LambdaQueryWrapper<CultureClockIn>()
                .eq(CultureClockIn::getDeleteMark, ConstantUtil.NUM_FALSE)
                .eq(CultureClockIn::getUserId, user.getUserId())
                .eq(CultureClockIn::getClockInDate, LocalDate.now());
        Long count = cultureClockInMapper.selectCount(clockQuery);
        if (count.intValue() >= 50) {
            throw new HandleException("今日已打卡50次了~先休息一下明天继续吧");
        }
        // 生成图片
        BufferedImage bufferedImage = generateClockImage(currentCombo, requestUrl, user, ConstantUtil.NUM_TRUE);
        MultipartFile multiFile = imageComboUtil.bufferedImageToMultipartFile(bufferedImage);
        FileInfo fileInfo = fileUploadApi.uploadFileCustomName(multiFile, fileApi.getPath(FileTypeConstant.TEMPORARY), multiFile.getOriginalFilename());
        String url = ConstantUtil.URL_HEAD + fileInfo.getBasePath() + fileInfo.getPath() + fileInfo.getOriginalFilename();
        // String url = fileInfo.getUrl().replace(ConstantUtil.EXTRA_PATH, "");
        // 生成打卡记录
        CultureClockIn cultureClockIn = new CultureClockIn();
        cultureClockIn.setId(FtbUtil.getId());
        cultureClockIn.setUserId(user.getUserId());
        cultureClockIn.setClockInDate(LocalDate.now());
        cultureClockIn.setClockInTime(new Date());
        String[] split = currentCombo.split("_");
        cultureClockIn.setPicId(split[0]);
        cultureClockIn.setContentId(split[1]);
        cultureClockIn.setPicUrl(url);
        cultureClockIn.setCreatorUserId(user.getUserId());
        cultureClockIn.setLastModifyUserId(user.getUserId());
        cultureClockIn.setDeleteMark(ConstantUtil.NUM_FALSE);
        cultureClockInMapper.insert(cultureClockIn);
        CultureClockInVo vo = JsonUtil.getJsonToBean(cultureClockIn, CultureClockInVo.class);
        // 统计数据更新
        LambdaQueryWrapper<CultureClockInStat> statQuery = new LambdaQueryWrapper<CultureClockInStat>()
                .eq(CultureClockInStat::getUserId, user.getUserId())
                .eq(CultureClockInStat::getDeleteMark, ConstantUtil.NUM_FALSE);
        CultureClockInStat stat = cultureClockInStatMapper.selectOne(statQuery);
        if (null == stat) {
            stat = new CultureClockInStat();
            stat.setUserId(user.getUserId());
            stat.setTotalDays(1);
            stat.setCurrentContinuousDays(1);
            stat.setMaxContinuousDays(1);
            stat.setMaxMissDays(0);
            stat.setLastClockInDate(LocalDate.now());
            stat.setCreatorUserId(user.getUserId());
            stat.setLastModifyUserId(user.getUserId());
            stat.setDeleteMark(ConstantUtil.NUM_FALSE);
            vo.setFirstClick(Boolean.TRUE);
            vo.setContinueTimes(stat.getCurrentContinuousDays());
        }
        if (null == stat.getId()) {
            stat.setId(FtbUtil.getId());
            cultureClockInStatMapper.insert(stat);
        } else {
            // 计算统计值并更新
            if (!stat.getLastClockInDate().equals(LocalDate.now())) {
                stat.setTotalDays(stat.getTotalDays() + 1);
                vo.setFirstClick(Boolean.TRUE);
            }
            // 当前打卡日期如果等于今天, 今天已经打过了不增加, 如果等于昨天+1, 小于昨天归1
            LocalDate yesterday = LocalDate.now().minusDays(1);
            if (stat.getLastClockInDate().isBefore(yesterday)) {
                stat.setCurrentContinuousDays(1);
            } else if (stat.getLastClockInDate().isEqual(yesterday)) {
                stat.setCurrentContinuousDays(stat.getCurrentContinuousDays() + 1);
            }
            // 如果当前连续打卡天数大于历史连续打卡天数 更新
            if (stat.getCurrentContinuousDays() > stat.getMaxContinuousDays()) {
                stat.setMaxContinuousDays(stat.getCurrentContinuousDays());
            }
            // 当前打卡时间 - 上次打卡时间 如果大于历史最大缺卡天数则更新
            int currentMissDays = DateDetail.calculateDayDiff(stat.getLastClockInDate(), LocalDate.now()) - 1;
            stat.setMaxMissDays(Math.max(currentMissDays, stat.getMaxMissDays()));
            stat.setLastClockInDate(LocalDate.now());
            stat.setLastModifyTime(new Date());
            stat.setLastModifyUserId(user.getUserId());
            cultureClockInStatMapper.updateById(stat);
            vo.setContinueTimes(stat.getCurrentContinuousDays());
        }
        return vo;
    }

    @Override
    public RecordListVo getRecordList(String cursorDate, Integer limitNum) {

        RecordListVo recordList = new RecordListVo();
        UserInfo user = UserProvider.getUser();
        // 1. 查询日期游标列表
        List<LocalDate> dateList = cultureClockInMapper.getClockInDateList(user.getUserId(), cursorDate, limitNum + 1);
        if (dateList.isEmpty()) {
            return recordList;
        }
        // 实际用于返回的日期
        List<LocalDate> pageDates;
        if (dateList.size() > limitNum) {
            recordList.setHasMore(true);
            // 取前 limitNum 条作为本页
            pageDates = dateList.subList(0, limitNum);
            // 第 limitNum 条（下标 limitNum - 1）作为下一页游标
            recordList.setNextCursor(pageDates.get(pageDates.size() - 1).toString());
        } else {
            pageDates = dateList;
        }
        // 2. 查询这些日期下的打卡记录
        List<CultureClockIn> records = cultureClockInMapper.getClockInListByDates(user.getUserId(), pageDates);
        // 3. 按日期分组
        Map<LocalDate, List<CultureClockIn>> groupMap = records.stream().collect(Collectors.groupingBy(CultureClockIn::getClockInDate));
        // 4. 组装数据返回
        pageDates.forEach(day -> {
            List<CultureClockIn> returnList = groupMap.get(day);
            List<String> picList = returnList.stream().map(CultureClockIn::getPicUrl).collect(Collectors.toList());
            long epochMilli = day
                    .atStartOfDay(ZoneId.systemDefault())
                    .toInstant()
                    .toEpochMilli();
            recordList.getDataList().add(new RecordDataVo(epochMilli, picList.size(), picList));
        });
        return recordList;
    }

    @Override
    public YearDataVo getYearData(Integer year) {

        YearDataVo yearData = new YearDataVo();
        UserInfo user = UserProvider.getUser();
        List<UserBoundVO> list = v2UserApi.userListAndCopy(List.of(user.getUserId()), null, user.getTenantId());
        if (null != list && !list.isEmpty()) {
            yearData.setUserName(list.get(0).getUserName());
            yearData.setHeadIcon(list.get(0).getHeadIcon());
        }
        // 查询累计打卡次数
        LambdaQueryWrapper<CultureClockInStat> queryWrapper = new LambdaQueryWrapper<CultureClockInStat>()
                .eq(CultureClockInStat::getUserId, user.getUserId())
                .eq(CultureClockInStat::getDeleteMark, ConstantUtil.NUM_FALSE);
        CultureClockInStat stat = cultureClockInStatMapper.selectOne(queryWrapper);
        yearData.setTotalDays(null == stat ? 0 : stat.getTotalDays());
        // 查询打卡日期
        LocalDate startDate = LocalDate.of(year, 1, 1);
        LocalDate endDate = LocalDate.of(year + 1, 1, 1);
        List<String> dateList = cultureClockInMapper.getClockInDateByYear(user.getUserId(), startDate, endDate);
        yearData.setDataDays(dateList);
        return yearData;
    }
}
