package com.ruoyi.dz.service.impl;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.Arrays;

import cn.hutool.core.util.RandomUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.ExamType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.NumberUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.criteria.CardCriteria;
import com.ruoyi.dz.domain.*;
import com.ruoyi.dz.mapper.*;
import com.ruoyi.enums.*;
import com.ruoyi.learn.domain.LearnStudent;
import com.ruoyi.learn.mapper.LearnStudentMapper;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.mapper.SysUserMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.dz.service.IDzCardsService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.ValidationException;

/**
 * 学习卡Service业务层处理
 *
 * @author ruoyi
 * @date 2025-09-12
 */
@Service
public class DzCardsServiceImpl implements IDzCardsService
{
    @Autowired
    private DzCardsMapper dzCardsMapper;
    @Autowired
    private DzCardsOpenMapper dzCardsOpenMapper;
    @Autowired
    private DzAgentMapper dzAgentMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private DzSchoolMapper dzSchoolMapper;
    @Autowired
    private DzClassesMapper dzClassesMapper;
    @Autowired
    private SysUserMapper userMapper;

    private final String format = "%d%08d";
    @Autowired
    private LearnStudentMapper learnStudentMapper;

    /**
     * 查询学习卡
     *
     * @param cardId 学习卡主键
     * @return 学习卡
     */
    @Override
    public DzCards selectDzCardsByCardId(Long cardId)
    {
        return dzCardsMapper.selectDzCardsByCardId(cardId);
    }

    @Override
    public DzCards selectDzCardsByCardNo(String cardNo)
    {
        DzCards cond = new DzCards();
        cond.setCardNo(cardNo);
        List<DzCards> list = dzCardsMapper.selectDzCardsList(cond);
        return list.size() != 1 ? null : list.get(0);
    }

    @Override
    public DzCards selectOneECard()
    {
        return dzCardsMapper.selectOneECard();
    }

    private List<DzCards> fillNames(List<DzCards> list) {
        Long start = System.currentTimeMillis();
        Set<Long> classIdSet = Sets.newHashSet();
        Set<Long> schoolIdSet = Sets.newHashSet();
        Set<Long> deptIdSet = Sets.newHashSet();
        Set<Long> agentIdSet = Sets.newHashSet();
        Set<Long> cardIdSet = Sets.newHashSet();
        for(DzCards c : list) {
            if(null != c.getCardId()) {
                cardIdSet.add(c.getCardId());
            }
            if(null != c.getClassId()) {
                classIdSet.add(c.getClassId());
            }
            if(null != c.getCampusClassId()) {
                classIdSet.add(c.getCampusClassId());
            }
            if(null != c.getAssignSchoolId()) {
                schoolIdSet.add(c.getAssignSchoolId());
            }
            if(null != c.getSchoolId()) {
                schoolIdSet.add(c.getSchoolId());
            }
            if(null != c.getCampusId()) {
                schoolIdSet.add(c.getCampusId());
            }
            if(null != c.getDeptId()) {
                deptIdSet.add(c.getDeptId());
            }
            if(null != c.getLeafAgentId()) {
                agentIdSet.add(c.getLeafAgentId());
            }
            if(null != c.getAgentId()) {
                agentIdSet.add(c.getAgentId());
            }
        }
        Map<Long, String> deptMap = !deptIdSet.isEmpty() ? sysDeptMapper.selectDeptByIds(deptIdSet).stream().collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName)) : Maps.newHashMap();
        Map<Long, String> schoolMap = !schoolIdSet.isEmpty() ? dzSchoolMapper.selectDzSchoolListByIds(schoolIdSet).stream().collect(Collectors.toMap(DzSchool::getId, DzSchool::getName)) : Maps.newHashMap();;
        Map<Long, String> agentMap = !agentIdSet.isEmpty() ? dzAgentMapper.selectDzAgentByAgentIds(agentIdSet).stream().collect(Collectors.toMap(DzAgent::getAgentId, DzAgent::getName)) : Maps.newHashMap();
        Map<Long, String> classesMap = !classIdSet.isEmpty() ? dzClassesMapper.selectClassesByIds(classIdSet).stream().collect(Collectors.toMap(DzClasses::getClassId, DzClasses::getName)) : Maps.newHashMap();
        Map<Long, SysUser> userMap = !cardIdSet.isEmpty() ? userMapper.selectUserByCardIds(cardIdSet).stream() .collect(Collectors.toMap( SysUser::getCardId, user -> user, (existing, replacement) -> existing)) : new HashMap<>();

        for(DzCards c : list) {
            c.setClassName(classesMap.get(c.getClassId()));
            c.setCampusClassName(classesMap.get(c.getCampusClassId()));
            c.setAssignSchoolName(schoolMap.get(c.getAssignSchoolId()));
            c.setSchoolName(schoolMap.get(c.getSchoolId()));
            c.setCampusName(schoolMap.get(c.getCampusId()));
            c.setDeptName(deptMap.get(c.getDeptId()));
            if(null != c.getAgentId()) {
                String name = agentMap.get(c.getAgentId());
                c.setAgentName(null != c.getLeafAgentId() && !c.getLeafAgentId().equals(c.getAgentId()) ? agentMap.get(c.getLeafAgentId()) + "(" + name + ")" : name);
            }
            if (userMap.containsKey(c.getCardId())) {
                SysUser user = userMap.get(c.getCardId());
                if (user != null ) {
                    c.setPhonenumber(user.getPhonenumber());
                    c.setNickName(user.getNickName());
                    c.setExamType(user.getExamType().name());
                    c.setLocation(user.getLocation());
                    c.setExamMajor(user.getExamMajor());
                    c.setExamMajorName(user.getExamMajorName());
                    if (user.getDirectedStudy() != null){
                        c.setDirectedStudy(user.getDirectedStudy());
                    }
                }
            }
            c.setType(CardType.getNewVal(c.getType()));
        }
        System.out.println( "查询学习卡耗时2：{}"+( System.currentTimeMillis() - start));
        return list;
    }

    @Override
    public List<com.ruoyi.dz.dto.CardStudentDTO> selectDzCardsList3(DzCards dzCards)
    {
        return dzCardsMapper.selectDzCardsList3(dzCards);
    }

    /**
     * 查询学习卡列表
     *
     * @param dzCards 学习卡
     * @return 学习卡
     */
    @Override
    public List<DzCards> selectDzCardsList(DzCards dzCards)
    {
        return fillNames(dzCardsMapper.selectDzCardsList(dzCards));
    }

    @Override
    public List<DzCards> selectDzCardsList2(DzCards dzCards)
    {
        long start = System.currentTimeMillis();
        List<DzCards> cards=dzCardsMapper.selectDzCardsList2(dzCards);
        System.out.println( "查询学习卡耗时1：{}"+( System.currentTimeMillis() - start));
        return fillNames(cards);
    }

    /**
     * 新增学习卡
     *
     * @param dzCards 学习卡
     * @return 结果
     */
    @Override
    public int insertDzCards(DzCards dzCards)
    {
        dzCards.setCreateTime(DateUtils.getNowDate());
        return dzCardsMapper.insertDzCards(dzCards);
    }

    /**
     * 修改学习卡
     *
     * @param dzCards 学习卡
     * @return 结果
     */
    @Override
    public int updateDzCards(DzCards dzCards)
    {
        dzCards.setUpdateTime(DateUtils.getNowDate());
        return dzCardsMapper.updateDzCards(dzCards);
    }

    /**
     * 批量删除学习卡
     *
     * @param cardIds 需要删除的学习卡主键
     * @return 结果
     */
    @Override
    public int deleteDzCardsByCardIds(Long[] cardIds)
    {
        return dzCardsMapper.deleteDzCardsByCardIds(cardIds);
    }

    /**
     * 删除学习卡信息
     *
     * @param cardId 学习卡主键
     * @return 结果
     */
    @Override
    public int deleteDzCardsByCardId(Long cardId)
    {
        return dzCardsMapper.deleteDzCardsByCardId(cardId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void issueCard(Long deptId, CardType type, Integer count) {
        Long maxNo = dzCardsMapper.selectMaxNo(type.getVal());
        if(null == maxNo) {
            maxNo = type.getVal() * 10000000L;
        }
        for(Integer i = 1; i <= count; i++) {
            DzCards c = new DzCards();
            c.setType(type.getVal());
            c.setCardNo(String.valueOf(maxNo + i));
//            String pass = RandomUtil.randomNumbers(6);
//            if (pass.charAt(0) == '0') {
//                pass = RandomUtil.randomString("123456789", 1) + pass.substring(1);
//            }
            String pass = NumberUtils.generatePwd(6);
            c.setPassword(pass);
            c.setDistributeStatus(CardDistributeStatus.Free.getVal());
            c.setStatus(CardStatus.Free.getVal());
            c.setTimeStatus(CardTimeStatus.Valid.getVal());
            c.setPayStatus(PayStatus.UnPay.getVal());
            c.setIsSettlement(0);
            c.setDeptId(deptId);
            dzCardsMapper.insertDzCards(c);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignCard(Long deptId, Long agentId, Long leafAgentId, CardType cardType, String beginNo, String endNo, String location, ExamType examType, Long schoolId, Integer days) {
        CardCriteria cond = new CardCriteria();
        cond.setType(cardType.getVal());
        cond.setStartNo(beginNo);
        cond.setEndNo(endNo);

        List<DzCards> list = dzCardsMapper.selectListByCond(cond);
        if(CollectionUtils.isEmpty(list)) {
            throw new ValidationException("卡类型与卡号不一致或卡号不正确");
        }

        DzCards dzCards = new DzCards();

        DzCardsOpen newOpen = new DzCardsOpen();
        newOpen.setAgentId(leafAgentId);
        newOpen.setCardType(cardType.getVal());
        newOpen.setStartNo(beginNo);
        newOpen.setEndNo(endNo);
        newOpen.setDeptId(deptId);
        newOpen.setEndDate(DateUtils.addDays(DateUtils.getNowDate(), null == days ? 14 : days)); // TODO MF 卡默认有效期
        newOpen.setSchoolId(schoolId);
        newOpen.setLocation(location);
        newOpen.setSender(SecurityUtils.getLoginUser().getUser().getNickName());
        newOpen.setIsReopen(0);
        newOpen.setStatus(RequestStatus.Accept.getVal());
        dzCardsOpenMapper.insertDzCardsOpen(newOpen);

        list.forEach(c -> {
            dzCards.setCardId(c.getCardId());
            if(!CardStatus.Free.getVal().equals(c.getStatus())) {
                throw new ValidationException("卡分配错误");
            }
            dzCards.setAgentId(agentId);
            dzCards.setLeafAgentId(leafAgentId);
            dzCards.setDistributeTime(DateUtils.getNowDate());
            dzCards.setAssignLocation(location);
            dzCards.setAssignExamType(null != examType ? examType.name() : null);
            dzCards.setAssignSchoolId(schoolId);
            dzCards.setDistributeStatus(CardDistributeStatus.Assign.getVal());
            dzCards.setPayStatus(PayStatus.UnPay.getVal()); // TODO 暂时分配即开卡
            // dzCards.setStatus(CardStatus.Open.getVal());
            dzCards.setOpenTime(DateUtils.getNowDate());
            dzCards.setDays(days);
            dzCardsMapper.updateDzCardsWithAssign(dzCards);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean openCard(Long agentId, String location, Long schoolId, String beginNo, String endNo, Integer days) {
        DzCardsOpen newOpen = new DzCardsOpen();
        newOpen.setAgentId(agentId);
        newOpen.setStartNo(beginNo);
        newOpen.setEndNo(endNo);
        newOpen.setEndDate(DateUtils.addDays(DateUtils.getNowDate(), 14)); // TODO MF 卡默认有效期
        newOpen.setSchoolId(schoolId);
        newOpen.setLocation(location);
        newOpen.setSender(SecurityUtils.getLoginUser().getUser().getNickName());
        newOpen.setIsReopen(0);
        newOpen.setStatus(RequestStatus.Accept.getVal());
        dzCardsOpenMapper.insertDzCardsOpen(newOpen);
        // TODO MF 检查已经使用的或无效的
        CardCriteria cond = new CardCriteria();
        cond.setStartNo(beginNo);
        cond.setEndNo(endNo);
        List<DzCards> cards = dzCardsMapper.selectListByCond(cond);
        if(cards.stream().filter(t -> !t.getPayStatus().equals(PayStatus.UnPay.getVal())).count() > 0) {
            throw new ValidationException("请求打开已开卡: " + beginNo + "-" + endNo);
        }
        DzAgent dzAgent = dzAgentMapper.selectDzAgentByAgentId(agentId);
        DzCards dzCards = new DzCards();
        cards.stream().forEach(c -> {
            dzCards.setCardId(c.getCardId());
            dzCards.setPayTime(DateUtils.getNowDate());
            dzCards.setPayStatus(PayStatus.Paid.getVal());
            // dzCards.setStatus(CardStatus.Paid.getVal());
            dzCards.setDistributeStatus(CardDistributeStatus.Assign.getVal());
            dzCards.setAssignLocation(location);
            dzCards.setAssignSchoolId(schoolId);
            if(null != dzAgent.getParentId()) {
                dzCards.setAgentId(dzAgent.getParentId());
                dzCards.setLeafAgentId(dzAgent.getAgentId());
            } else {
                dzCards.setAgentId(dzAgent.getAgentId());
                dzCards.setLeafAgentId(dzAgent.getAgentId());
            }
            dzCards.setDays(days);
            dzCardsMapper.updateDzCardsWithAssign(dzCards);
        });
        // TODO MF 检查已经使用的或无效的
        return true;
    }

    @Override
    public Boolean requestOpenCard(DzCardsOpen dzCardsOpen) {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        if(UserTypeEnum.Agent.getVal().equals(sysUser.getUserType())) {
            dzCardsOpen.setAgentId(sysUser.getUserTypeId());
        }
        dzCardsOpen.setStatus(RequestStatus.Submit.getVal());
        dzCardsOpen.setEndDate(DateUtils.addDays(DateUtils.getNowDate(), 14));
        dzCardsOpen.setIsReopen(0);
        dzCardsOpenMapper.insertDzCardsOpen(dzCardsOpen);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmOpenCard(DzCardsOpen dzCardsOpen, SysUser sysUser) {
        DzCardsOpen exist = dzCardsOpenMapper.selectDzCardsOpenById(dzCardsOpen.getId());
        if(UserTypeEnum.Agent.getVal().equals(sysUser.getUserType()) && !exist.getAgentId().equals(dzCardsOpen.getAgentId())) {
            throw new ValidationException("不可修改他人申请");
        }
        if(RequestStatus.Accept.getVal().equals(exist.getStatus())) {
            throw new ValidationException("不可修改已经通过的申请");
        }
        if(!RequestStatus.Submit.getVal().equals(exist.getStatus())) {
            throw new ValidationException("状态无效");
        }
        DzCardsOpen upOpen = new DzCardsOpen();
        upOpen.setId(dzCardsOpen.getId());
        upOpen.setStatus(RequestStatus.Accept.getVal().equals(dzCardsOpen.getStatus()) ? RequestStatus.Accept.getVal() : RequestStatus.Reject.getVal());
        upOpen.setAuditDesc(dzCardsOpen.getAuditDesc());

        if(RequestStatus.Accept.getVal().equals(upOpen.getStatus())) {
            CardCriteria cond = new CardCriteria();
            cond.setStartNo(exist.getStartNo());
            cond.setEndNo(exist.getEndNo());
            List<DzCards> cards = dzCardsMapper.selectListByCond(cond);
            if(cards.stream().filter(t -> !t.getPayStatus().equals(PayStatus.UnPay.getVal())).count() > 0) {
                throw new ValidationException("重复打开已开卡: " + exist.getStartNo() + "-" + exist.getEndNo());
            }
            DzCards dzCards = new DzCards();
            cards.stream().forEach(c -> {
                dzCards.setCardId(c.getCardId());
                dzCards.setAssignLocation(exist.getLocation());
                dzCards.setAssignSchoolId(exist.getSchoolId());
                DzAgent dzAgent = dzAgentMapper.selectDzAgentByAgentId(exist.getAgentId());
                if(null != dzAgent.getParentId()) {
                    dzCards.setAgentId(dzAgent.getParentId());
                    dzCards.setLeafAgentId(dzAgent.getAgentId());
                } else {
                    dzCards.setAgentId(dzAgent.getAgentId());
                    dzCards.setLeafAgentId(dzAgent.getAgentId());
                }
                dzCards.setPayTime(DateUtils.getNowDate());
                dzCards.setPayStatus(PayStatus.Paid.getVal());
                // dzCards.setStatus(CardStatus.Paid.getVal());
                dzCards.setDistributeStatus(CardDistributeStatus.Assign.getVal());
                dzCardsMapper.updateDzCardsWithAssign(dzCards);
            });
        }
        dzCardsOpenMapper.updateDzCardsOpen(upOpen);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeCard(CardAction action, Long[] cardIds,Integer days) {
        List<DzCards> cards = dzCardsMapper.selectCardsByCardIds(cardIds);
        DzCards up = new DzCards();
        if(CardAction.Pay.equals(action)) {
            if(cards.stream().filter(t -> t.getPayStatus().equals(PayStatus.Paid.getVal())).count() > 0) {
                throw new ValidationException("重复支付已支付卡: " + StringUtils.join(cardIds, ","));
            }
            up.setPayStatus(PayStatus.Paid.getVal());
            // up.setStatus(CardStatus.Paid.getVal());
            up.setPayTime(DateUtils.getNowDate());
        } else if(CardAction.Close.equals(action)) {
            if(cards.stream().filter(t -> t.getDistributeStatus().equals(CardDistributeStatus.Close.getVal())).count() > 0) {
                throw new ValidationException("重复关闭卡: " + StringUtils.join(cardIds, ","));
            }
            up.setDistributeStatus(CardDistributeStatus.Close.getVal());
            up.setCloseTime(DateUtils.getNowDate());
        } else if(CardAction.ReOpen.equals(action)) {
            if(cards.stream().filter(t -> !t.getDistributeStatus().equals(CardDistributeStatus.Close.getVal())).count() > 0) {
                throw new ValidationException("重开非关闭卡: " + StringUtils.join(cardIds, ","));
            }
            up.setDistributeStatus(CardDistributeStatus.Assign.getVal());
        } else if(CardAction.Refund.equals(action)) {
            if(cards.stream().filter(t -> t.getPayStatus().equals(PayStatus.Refund.getVal())).count() > 0) {
                throw new ValidationException("重复退款: " + StringUtils.join(cardIds, ","));
            }
            up.setDistributeStatus(CardDistributeStatus.Close.getVal());
            up.setPayStatus(PayStatus.Refund.getVal());
            up.setRefundTime(DateUtils.getNowDate());
        } else if(CardAction.Settlement.equals(action)) {
            if(cards.stream().filter(t -> null != t.getIsSettlement() && t.getIsSettlement().equals(1)).count() > 0) {
                throw new ValidationException("有卡已经结算: " + StringUtils.join(cardIds, ","));
            }
            up.setSettlementTime(DateUtils.getNowDate());
            up.setIsSettlement(1);
        } else if(CardAction.Renew.equals(action)) {
        } else {
            throw new ValidationException("无效操作");
        }
        for(DzCards card : cards) {
            up.setCardId(card.getCardId());
            if(CardAction.Renew.equals(action)) {
                if (null==days){
                    days =30;
                }
                up.setOutDate(DateUtils.addDays(card.getOutDate(), days)); // TODO 暂时都续费三年？
            }
            dzCardsMapper.updateDzCards(up);
        }
    }

    public Boolean changeCampus(Long campusId, Long campusClassId, String beginNo, String endNo) {
        // 使用begin和end方式，验证不能为空
        if (StringUtils.isBlank(beginNo)) {
            throw new ValidationException("开始卡号不能为空");
        }
        if (StringUtils.isBlank(endNo)) {
            throw new ValidationException("结束卡号不能为空");
        }
        CardCriteria cond = new CardCriteria();
        cond.setStartNo(beginNo);
        cond.setEndNo(endNo);
        List<DzCards> cards = dzCardsMapper.selectListByCond(cond);
        if(cards.isEmpty()) {
            return false;
        }

        return changeCampusByCards( campusId,  campusClassId, cards);
    }

    private Boolean changeCampusByCards(Long campusId, Long campusClassId, List<DzCards> cards) {
        DzCards dzCards = new DzCards();
        Map<Long, SysUser> userMap = userMapper.selectUserByCardIds(cards.stream().map(DzCards::getCardId).collect(Collectors.toList())).stream()
                .collect(Collectors.toMap(SysUser::getCardId, user -> user, (existing, replacement) -> existing));
        cards.stream().forEach(c -> {
            dzCards.setCardId(c.getCardId());
            dzCards.setCampusId(campusId);
            dzCards.setCampusClassId(campusClassId);
            dzCardsMapper.updateDzCards(dzCards);

            SysUser u = userMap.get(c.getCardId());
            if (null != u) {
                LearnStudent upStudent = new LearnStudent();
                upStudent.setStudentId(u.getUserId());
                upStudent.setClassId(campusClassId);
                upStudent.setCampusId(campusId);
                learnStudentMapper.updateLearnStudent(upStudent);
            }
        });
        return true;
    }

    @Override
    public Boolean changeCampusByCardIds(Long campusId, Long campusClassId, Long[] cardIds) {
        if (cardIds == null || cardIds.length == 0) {
            return false;
        }
        // 通过cardIds获取卡片列表
        List<Long> cardIdList = Arrays.asList(cardIds);
        List<DzCards> cards = selectCardsByCardIds(cardIdList);
        if (CollectionUtils.isEmpty(cards)) {
            return false;
        }
        return changeCampusByCards( campusId,  campusClassId, cards);
    }

    @Override
    public List<DzCards> selectCardsByCardIds(List<Long> cardIds){
        //cardIds转换为数组
        return dzCardsMapper.selectCardsByCardIds(cardIds.toArray(new Long[0]));
    }

    @Override
    public List<com.ruoyi.dz.dto.CardStatisticsDTO> statisticCards(java.util.Map<String, Object> params)
    {
        return dzCardsMapper.statisticCards(params);
    }
}
