package org.linlinjava.jz.admin.job;

import com.google.common.collect.Lists;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.jz.admin.service.AdminCamapaignAssignedService;
import org.linlinjava.jz.core.util.StringUtils;
import org.linlinjava.jz.db.domain.*;
import org.linlinjava.jz.db.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 任务数据拉取及分配处理job
 * 此job是任务分配的核心 目前暂定5分钟一次数据拉取及任务分配
 *
 * 处理逻辑;将符合任务条件的数据，插入到built_list任务总表；并同步插入到redis
 *
 */
@Component
public class CampaignJob {
    private final Log logger = LogFactory.getLog(CampaignJob.class);

    @Autowired
    private JzCampaignService campaignService;

    @Autowired
    private JzBuiltListService builtListService;

    @Autowired
    private JzSysUserOnlineService userOnlineService;

    @Autowired
    private JzCampaignAssignedLogService campaignAssignedLogService;

    @Autowired
    private AdminCamapaignAssignedService camapaignAssignedService;

    @Autowired
    private JzSkillGroupUserService skillGroupUserService;

    @Autowired
    private JzShopService shopService;

    @Scheduled(fixedDelay = 10 * 60 * 1000)
    public void startAssigned() {
        logger.info("系统开启任务数据ETL及分配");

        List<Integer> userOnlines = userOnlineService.queryAllOnline();//所有在线客服
        List<JzCampaign> campaignList = campaignService.queryEnable();//所有可执行分配的任务
        for (JzCampaign campaign : campaignList) {
            //抽取任务数据 插入到built中  非每日运行一次的任务
            if (campaign.getIsNightRun() == false)
                campaignRun(campaign);

            //获取可分配的设置
            List<JzCampaignAssigned> campaignAssigneds = camapaignAssignedService.list(campaign.getId());
            if (campaignAssigneds.size() == 0) continue;

            for (JzCampaignAssigned assigned : campaignAssigneds) {  //循环遍历每个分配任务
                if(assigned.getShops()==null) continue;

                //将门店id转成盒马门店no
                List<Integer> shops =  Arrays.asList(assigned.getShops());

                //获取built中所有待分配的数据(指定门店)
                List<JzBuiltList> builtLists = builtListService.queryCanAssignedByCampaignId(campaign.getId(), shops);
                if (builtLists == null || builtLists.size() == 0) continue;


                //获取所有负责此任务的坐席用户
                List<Integer> users = skillGroupUserService.findUserIdByGroupId(assigned.getSkills());// camapaignAssignedService.listUsers(campaign.getId());
                //如果只分配在线客服 剔除不在线的
                if (campaign.getIsCsrOnline()) {
                    users = removeNotOnline(userOnlines, users);
                    if (users.size() == 0) continue;//无在线客服 直接进行下一个任务分配
                }
                //上一次分配到的坐席id
                Integer lastAssignedUser = campaignAssignedLogService.findByCampaignId(campaign.getId(), assigned.getId());
                //根据最后分配坐席 重新排序数据 如队列abcdef 最后一个分配人是d 则队列排序为efabcd :最有一个人放最后
                users = sort(users, lastAssignedUser);

                /**
                 * 通过任务量和负责任务的坐席量 通过相除和取余分配队列
                 * 开始任务分配 待分配任务数量/坐席数量=均分几倍
                 * 例:100任务量/6人=16每人 余：4
                 */
                int times = builtLists.size() / users.size();//均分数量
                int remaining = builtLists.size() % users.size();//均分后剩余数量
                if (times > 0) {
                    int index = 0;//坐席位移
                    int amount = 1;//计数器 记录分配的数量 如果等于坐席队列数量 表示要分配下一个人 重新技术
                    for (JzBuiltList builtList : builtLists) {
                        if (amount > users.size()) {//分配完成 重置参数
                            amount = 1;
                            index = 0;
                        }
                        builtList.setUserId(users.get(index));
                        builtList.setAssignedTime(LocalDateTime.now());
                        amount = amount + 1;
                        index = index + 1;
                    }
                }
                //剩余数量分配
                if (remaining > 0)//存在刚好 没有剩余的情况
                {
                    int start = times * users.size();
                    int index = 0;
                    for (int i = start; i < builtLists.size(); i++) {
                        builtLists.get(i).setUserId(users.get(index));
                        builtLists.get(i).setAssignedTime(LocalDateTime.now());
                        index = index + 1;
                    }
                }

                //bulitlist队列分配完成 批量更新实体表    ----此处数据可以插入redis
                builtListService.runUpdateBuiltBatch(builtLists);

                //记录日志 最后一个分配人
                campaignAssignedLogService.add(campaign.getId(), assigned.getId(), users.get((remaining > 0 ? remaining - 1 : 0)), remaining);
            }

        }
        logger.info("系统结束任务");
    }

    /**
     * 每天运行一次的任务数据ETL
     * 每天上午6点15分执行
     */
    //@Scheduled(cron = "0 15 6 * * ? *")
    public void nightRun() {
        logger.info("系统开始night run任务");
        List<JzCampaign> campaignList = campaignService.queryEnable();//所有可执行分配的任务
        for (JzCampaign campaign : campaignList) {
            //抽取任务数据 插入到built中
            campaignRun(campaign);
        }
    }

    /**
     * 删除不在线的坐席
     * @param onlines
     * @param assignedUsers
     * @return
     */
    List<Integer> removeNotOnline(List<Integer>  onlines,List<Integer> assignedUsers){
        List<Integer> users= Lists.newArrayList();

        for(Integer user:assignedUsers){
            if(onlines.contains(user))//不在线
                users.add(user);
        }
        //正序排列
        Collections.sort(users);
        return  users;
    }

    /**
     * 重新排序集合 根据最后一次分配到的坐席 调整list顺序
     * 如：list：12345678  最后一次分配到6 排序后：78123456 保证任务分配的公平性
     * @param users
     * @param lastAssignedUser
     * @return
     */
    List<Integer> sort(List<Integer> users,Integer lastAssignedUser) {
        //logger.info(users);
        //logger.info(lastAssignedUser);
        //如果上次分配的坐席id=0 表示新的任务没有分配过 直接返回数组
        if (lastAssignedUser == 0) return users;
        //最后一次分配的客户在末尾 直接返回数组
        if (users.contains(lastAssignedUser) && users.indexOf(lastAssignedUser) == users.size() - 1) return users;

        List<Integer> newUsers = null;
        int index = 0;//最后一次分配人所在位置
        //分2中情况 最后一次分配的坐席在线和不在线
        if (users.contains(lastAssignedUser)) {
            index = users.indexOf(lastAssignedUser);
        } else {
           // logger.info("out of line");
            //如果最后一次分配坐席不在队列里 ，就找队列里中间位置，
            // 如队列123567，最后一次分配4 即3所在位置 ；最后一次分配8 即7位置
            int value = -1;
            int i = 0;//数组位置
            for (Integer user : users) {
                if (i > 0 && lastAssignedUser >= value && lastAssignedUser < user) {
                    index = i - 1;
                    break;
                }
                value = user;
                i++;
            }
            if (value == -1)//未找到匹配位置 默认队列后面
                index = users.size() - 1;
        }
        //logger.info(index);
        newUsers = users.subList(index + 1, users.size());
        //logger.info(newUsers);
        List<Integer> lastUsers = users.subList(0, index + 1);
        //logger.info(lastUsers);
        //将listUser拼到newUsers后面
        newUsers.addAll(lastUsers);

       // logger.info(newUsers);
        return newUsers;
    }

    /**
     * 执行任务数据的抽取 将任务数据插入到built（需要按customerid去重，重复数据不插入）
     * @param campaign
     */
    void campaignRun(JzCampaign campaign) {
        if (StringUtils.isEmpty(campaign.getCustomSql())) return;

        try {
            //执行sql脚本 获取待插入数据
            List<JzBuiltList> builtLists = builtListService.runSql(campaign.getCustomSql(), campaign.getId());

            //将数据集合 批量插入到bulitlist表
            builtListService.runInsertBatch(builtLists);
        } catch (Exception ex) {
            logger.error(ex);
        }

    }

}
