package com.tykj.dev.device.allot.service.impl;

import com.github.wenhao.jpa.PredicateBuilder;
import com.github.wenhao.jpa.Specifications;
import com.tykj.dev.blockcha.subject.entity.BcHash;
import com.tykj.dev.blockcha.subject.service.BlockChainUtil;
import com.tykj.dev.config.TaskBeanConfig;
import com.tykj.dev.config.base.FileName;
import com.tykj.dev.device.allot.repository.AllotBackBillDao;
import com.tykj.dev.device.allot.service.AllotBackBillService;
import com.tykj.dev.device.allot.subject.domin.AllotBackBill;
import com.tykj.dev.device.allot.subject.vo.AllotBillSelectVo;
import com.tykj.dev.device.file.entity.FileRet;
import com.tykj.dev.device.file.service.FilesUtil;
import com.tykj.dev.device.task.service.TaskService;
import com.tykj.dev.device.user.subject.service.UserPublicService;
import com.tykj.dev.device.user.util.UserUtils;
import com.tykj.dev.misc.exception.ApiException;
import com.tykj.dev.misc.utils.JacksonUtil;
import com.tykj.dev.misc.utils.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.persistence.Transient;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/**
 * @author dengdiyi
 */
@Service
public class AllotBackBillServiceImpl implements AllotBackBillService {

    @Autowired
    private AllotBackBillDao allotBackBillDao;

    @Autowired
    private BlockChainUtil blockChainUtil;

    @Autowired
    private UserPublicService userPublicService;

    @Autowired
    private UserUtils userUtils;

    @Autowired
    private TaskService taskService;

    @Override
    public AllotBackBill addEntity(AllotBackBill allotBackBill) {
        AllotBackBill allotBackBill1 = allotBackBillDao.save(allotBackBill);
        //异步上链
        CompletableFuture.runAsync(()->{
            //延迟2s防止同时写入
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获取最新数据
            AllotBackBill allotBackBill2 = getOne(allotBackBill1.getId());
            //上链获得recordId并存入表中
            BcHash bcText = blockChainUtil.sendHash(1000, JacksonUtil.toJSon(allotBackBill2));
            String recordId = bcText.getData().getRecordID();
            allotBackBill2.setRecordId(recordId);
            allotBackBillDao.save(allotBackBill2);
        }, TaskBeanConfig.getThreadPoolTaskScheduler());
        return allotBackBill1;
    }

    /**
     * @param allotBackBills 异步上链
     */
    @Override
    @Async
    public void sendHash(List<AllotBackBill> allotBackBills) {
        allotBackBills.forEach(allotBackBill1 -> {
            BcHash bcText = blockChainUtil.sendHash(1000, JacksonUtil.toJSon(allotBackBill1));
            String recordId = bcText.getData().getRecordID();
            allotBackBill1.setRecordId(recordId);
            allotBackBillDao.save(allotBackBill1);
        });
    }

    @Override
    public AllotBackBill update(AllotBackBill allotBackBill) {
        AllotBackBill allotBackBill1 = allotBackBillDao.save(allotBackBill);
        //根据recordId追加上链
        CompletableFuture.runAsync(()->blockChainUtil.appendHash(JacksonUtil.toJSon(allotBackBill),allotBackBill.getRecordId()),TaskBeanConfig.getThreadPoolTaskScheduler());
        return allotBackBill1;
    }

    @Override
    public AllotBackBill getOne(Integer id) {
        Optional<AllotBackBill> allotBackBill = allotBackBillDao.findById(id);
        if (allotBackBill.isPresent()) {
            return allotBackBill.get();
        } else {
            throw new ApiException(ResultUtil.failed("查询的Id不存在"));
        }
    }

    @Override
    public Page<AllotBackBill> getPage(AllotBillSelectVo allotBillSelectVo, Pageable pageable) {
        Page<AllotBackBill> page = allotBackBillDao.findAll(getSelectSpecification(allotBillSelectVo), pageable);
        //id转换为人名
        for (AllotBackBill a : page.getContent()) {
            if (a.getSendUseraId()!=null) {
                a.setSenderUserA(userPublicService.getOne(a.getSendUseraId()).getName());
            }
            if (a.getSendUserbId()!=null) {
                a.setSenderUserB(userPublicService.getOne(a.getSendUserbId()).getName());
            }
            if (a.getReceiveUseraId()!=null) {
                a.setReceiveUserA(userPublicService.getOne(a.getReceiveUseraId()).getName());
            }
            if (a.getReceiveUserbId()!=null) {
                a.setReceiveUserB(userPublicService.getOne(a.getReceiveUserbId()).getName());
            }
        }
        return page;
    }

    @Override
    public void deleteByAllotBackId(Integer id) {
        allotBackBillDao.deleteById(id);
    }

    @Override
    public Map<String, List<FileRet>> getFileList(Integer taskId) {
        Map<String,List<FileRet>> map = new HashMap<>();
        Integer billId = taskService.get(taskId).getBillId();
        AllotBackBill allotBackBill = getOne(billId);
        //收件方签收单集合
        map.put(FileName.SIGN.name, FilesUtil.stringFileToList(allotBackBill.getReceiveFiles()));
        //申请文号文件集合
        map.put(FileName.APPLY.name,FilesUtil.stringFileToList(allotBackBill.getApplyFiles()));
        //批复文号文件集合
        map.put(FileName.RECEIPT.name,FilesUtil.stringFileToList(allotBackBill.getReplyFiles()));
        //发件方回执单集合
        map.put(FileName.RECEIPT.name,FilesUtil.stringFileToList(allotBackBill.getSendFiles()));
        return map;
    }

    /**
     * @param allotBillSelectVo 查询vo
     * 查询统一筛选器
     */
    private Specification<AllotBackBill> getSelectSpecification(AllotBillSelectVo allotBillSelectVo) {
        PredicateBuilder<AllotBackBill> predicateBuilder = Specifications.and();
        //或预言器，判断当前单位是不是发件或收件单位
        PredicateBuilder<AllotBackBill> predicateBuilder1 = Specifications.or();
        predicateBuilder1.eq("sendUnit", userUtils.getCurrentUserUnitName());
        predicateBuilder1.eq("receiveUnit", userUtils.getCurrentUserUnitName());
        predicateBuilder.predicate(predicateBuilder1.build());
        if (allotBillSelectVo != null) {
            if (allotBillSelectVo.getReplayNumber() != null) {
                predicateBuilder.eq("replayNumber", allotBillSelectVo.getReplayNumber());
            }
            if (allotBillSelectVo.getReceiveUnit() != null) {
                predicateBuilder.eq("receiveUnit", allotBillSelectVo.getReceiveUnit());
            }
            if (allotBillSelectVo.getReceiveUseraId() != null) {
                predicateBuilder.eq("receiveUseraId", allotBillSelectVo.getReceiveUseraId());
            }
            if (allotBillSelectVo.getSendUnit() != null) {
                predicateBuilder.eq("sendUnit", allotBillSelectVo.getSendUnit());
            }
            if (allotBillSelectVo.getSendUseraId() != null) {
                predicateBuilder.eq("sendUseraId", allotBillSelectVo.getSendUseraId());
            }
            if (allotBillSelectVo.getContent() != null) {
                Class<AllotBackBill> allotBillEntityClass = AllotBackBill.class;
                Field[] declaredFields = allotBillEntityClass.getDeclaredFields();
                PredicateBuilder<AllotBackBill> p = Specifications.or();
                for (Field field : declaredFields) {
                    if (field.getType().equals(String.class) && field.getAnnotation(Transient.class) == null) {
                        p.like(field.getName(), "%" + allotBillSelectVo.getContent() + "%");
                    }
                }
                predicateBuilder.predicate(p.build());
            }
            if (allotBillSelectVo.getStartTime() != null) {
                predicateBuilder.gt("sendTime", allotBillSelectVo.getStartTime());
            }
            if (allotBillSelectVo.getEndTime() != null) {
                predicateBuilder.lt("sendTime", allotBillSelectVo.getEndTime());
            }
            predicateBuilder.notIn("backStatus", 1);
        }
        return predicateBuilder.build();
    }
}
