package com.sifang.modules.taskdispatch.service.autod;

import com.jfinal.plugin.activerecord.Record;
import com.sifang.modules.flowable.dao.ItemManageDao;
import com.sifang.modules.sys.util.SpringUtils;
import com.sifang.modules.taskdispatch.dao.CrmReviewGroupMemberDao;
import com.sifang.modules.taskdispatch.service.autod.vo.ItemTakeCtl;
import com.sifang.modules.taskdispatch.service.autod.vo.ItemTask;
import com.sifang.modules.taskdispatch.util.ReviewGroupUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;


public class GroupItemManager {

    private final String groupId;
    private final String partnerId;
    private static final Logger log = LoggerFactory.getLogger(GroupItemManager.class);
    private final Object itemIdsLock = new Object();
    private final Queue<ItemTask> itemIdsQ = createNewItemQ();
    private final Set<String> itemIds = new HashSet<>();
    private final String groupType;
    private static final ReentrantLock lock = new ReentrantLock();

    public boolean tryLock() {
        try {
            return lock.tryLock(3, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }

    public ItemTask peek(String userId) {
        if (itemIdsQ.isEmpty()) {
            loadMore(null);
        }
        for (ItemTask itemTask : itemIdsQ) {
            if (StringUtils.isBlank(itemTask.getPreferUserId()) || Objects.equals(itemTask.getPreferUserId(), userId)) {
                return itemTask;
            }
        }
        return null;
    }

    public void releaseLock() {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

    public GroupItemManager(String partnerId, String groupId, String groupType) {
        this.partnerId = partnerId;
        this.groupId = groupId;
        this.groupType = groupType;
        loadMore(null);
    }

    public String getGroupId() {
        return groupId;
    }

    public String getPartnerId() {
        return partnerId;
    }

    public String getGroupType() {
        return groupType;
    }

    static PriorityQueue<ItemTask> createNewItemQ() {
        return new PriorityQueue<>(3, new Comparator<ItemTask>() {
            @Override
            public int compare(ItemTask o1, ItemTask o2) {
                long t1 = o1.getCreateTime().getTime();
                long t2 = o2.getCreateTime().getTime();
                return Long.compare(t1, t2);
            }
        });
    }

    public void removeTask(ItemTask itemTask) {
        synchronized (itemIdsLock) {
            if (itemIds.contains(itemTask.getItemId())) {
                boolean remove = itemIdsQ.remove(itemTask);
                if (!remove) {
                    log.warn("remove task fail, task.id: {}", itemTask.getItemId());
                }
            }
        }
    }

    public void returnTask(ItemTask task) {
        assert task.isCachedTask();
        ItemManageDao itemManageDao = SpringUtils.getBean(ItemManageDao.class);
        Record itemManage = itemManageDao.getById(task.getItemId());
        if (itemManage.getStr("assign_audit_user_id") != null) {
            log.info("task already consumed, no need return, task.id:{}", task.getItemId());
            return;
        }
        synchronized (itemIdsLock) {
            if (itemIds.add(task.getItemId())) {
                itemIdsQ.add(task);
            }
        }
    }

    public ItemTask take(String provinces, String cities, ItemTakeCtl ctl, List<String> excludeIds) {
        ItemTask task = null;
        synchronized (itemIdsLock) {
            if (StringUtils.isNotBlank(provinces)) {
                task = tryMatchItemTaskByLoc(provinces, cities);
                if (task != null) {
                    itemIds.remove(task.getItemId());
                    removeTask(task);
                    return task;
                }
                ItemManageDao itemManageDao = SpringUtils.getBean(ItemManageDao.class);
                List<Record> tasks = itemManageDao.findUnAssignedItemsByLoc(this.partnerId, this.groupType, provinces, cities, 1, excludeIds);
                if (!tasks.isEmpty()) {
                    ItemTask itemTask = asItemTask(tasks.get(0));
                    itemTask.setCachedTask(false);
                    return itemTask;
                }
            }

            task = itemIdsQ.poll();
            if (task == null) {
                if (ctl == null || ctl.get()) {
                    int ret = loadMore(excludeIds);
                    if (ret == 0 && ctl != null) {
                        ctl.consume();
                    }
                    task = itemIdsQ.poll();
                }
            }
            if (task != null) {
                itemIds.remove(task.getItemId());
            }
        }
        return task;
    }

    private ItemTask tryMatchItemTaskByLoc(String provinces, String cities) {
        String[] provinceList = provinces.split(",");
        Set<String> provinceSet = new HashSet<>(Arrays.asList(provinceList));

        boolean hasCity = StringUtils.isNotBlank(cities);
        if (!hasCity) {
            for (ItemTask itemTask : itemIdsQ) {
                if (provinceSet.contains(itemTask.getProvince())) {
                    return itemTask;
                }
            }
        } else {
            String[] cityList = cities.split(",");
            Set<String> citySet = new HashSet<>(Arrays.asList(cityList));
            for (ItemTask itemTask : itemIdsQ) {
                if (provinceSet.contains(itemTask.getProvince()) && citySet.contains(itemTask.getCity())) {
                    return itemTask;
                }
            }
        }
        return null;
    }


//    private ItemTask tryMatchItemTasks(String provinces, String cities) {
//        String[] provinceList = provinces.split(",");
//        PriorityQueue<ItemTask> ret = createNewItemQ();
//        boolean hasCity = StringUtils.isNotBlank(cities);
//        if (!hasCity) {
//            for (String p : provinceList) {
//                PriorityQueue<ItemTask> itemTasks = locBasedTaskMap.get(p);
//                ItemTask task = itemTasks.poll();
//                if (task != null) {
//                    ret.add(task);
//                }
//            }
//        } else {
//            String[] cityList = cities.split(",");
//            Set<String> citySet = new HashSet<>(Arrays.asList(cityList));
//            for (String p : provinceList) {
//                PriorityQueue<ItemTask> itemTasks = locBasedTaskMap.get(p);
//                for (ItemTask itemTask : itemTasks) {
//                    if (citySet.contains(itemTask.getCity())) {
//                        ret.add(itemTask);
//                        break;
//                    }
//                }
//            }
//        }
//        return ret.poll();
//    }

    public int loadMore(List<String> excludeIds) {
        synchronized (itemIdsLock) {
            ItemManageDao itemManageDao = SpringUtils.getBean(ItemManageDao.class);
            CrmReviewGroupMemberDao memberDao = SpringUtils.getBean(CrmReviewGroupMemberDao.class);
            List<String> formKeys = ReviewGroupUtil.mapGroupType2FormKey(groupType);
            Long groupMemberCount = memberDao.groupMemberCount(groupId);
            List<Record> itemRecords = itemManageDao.findUnAssignedItemsByGroup(this.partnerId, formKeys, groupMemberCount * 3, excludeIds);
            int count = 0;
            for (Record item : itemRecords) {
                if (itemIds.add(item.getStr("id"))) {
                    count++;
                    ItemTask itemTask = asItemTask(item);
                    itemIdsQ.add(itemTask);
//                    PriorityQueue<ItemTask> locTaskItems = locBasedTaskMap.getOrDefault(itemTask.getProvince(), createNewItemQ());
//                    locTaskItems.add(itemTask);
                }
            }
            log.info("group {}: loaded items: {}", groupId, count);
            return count;
        }
    }

    private ItemTask asItemTask(Record item) {
        ItemTask ret = new ItemTask();
        ret.setItemId(item.getStr("id"));
        ret.setFormKey(item.getStr("form_key"));
        ret.setCreateTime(item.getDate("create_time"));
        ret.setProvince(item.getStr("province"));
        ret.setCity(item.getStr("city"));
        ret.setPreferUserId(item.getStr("prefer_user_id"));
        return ret;
    }

}
