package nckd.jimin.jyyy.fi.task;

import kd.bos.algo.DataSet;
import kd.bos.context.RequestContext;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.exception.KDException;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.ORM;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.schedule.executor.AbstractTask;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.QueryServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.bos.servicehelper.org.OrgUnitServiceHelper;
import kd.bos.servicehelper.org.OrgViewType;
import nckd.jimin.jyyy.fi.common.constant.BillTypeConstants;
import nckd.jimin.jyyy.fi.common.constant.travelcheck.TravelFinanDraftConstant;
import nckd.jimin.jyyy.fi.common.constant.travelcheck.TripReqBillConstant;
import nckd.jimin.jyyy.fi.common.util.CommonUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 商旅财务对账底稿创建任务
 * 每个月月初同步尚未对账的出差申请单、差旅报销单创建底稿
 *
 */
public class TravelFinanDraftCreateTask extends AbstractTask {
    private static final Log logger = LogFactory.getLog(TravelFinanDraftCreateTask.class);
    @Override
    public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
        logger.info("TravelFinanDraftCreateTask begin");

        DynamicObject tripExpenseItem = QueryServiceHelper.queryOne("er_tripexpenseitem", "nckd_travelbzstd", new QFilter("longnumber", QCP.equals, "004").toArray());
        if(tripExpenseItem == null){
            logger.error("没有配置补助的差旅标准。");
            return;
        }
        BigDecimal bzAmount = tripExpenseItem.getBigDecimal("nckd_travelbzstd");
        if(bzAmount == null || bzAmount.compareTo(BigDecimal.ZERO) <= 0){
            logger.error("差旅补助金额不能为0。");
            return;
        }
        // 获取本月月初
        LocalDateTime firstDayStart = LocalDate.now().withDayOfMonth(1).atStartOfDay();
        // 转换为Date对象
        Date firstDay = Date.from(firstDayStart.atZone(ZoneId.systemDefault()).toInstant());

        // 查询出差申请单
        DynamicObjectCollection tripReqBillCol = QueryServiceHelper.query(TripReqBillConstant.ENTITYID, TripReqBillConstant.BILLNO, new QFilter[]{
                // 状态为审核通过、已付款、关闭
                new QFilter(TripReqBillConstant.KEY_BILLSTATUS, QFilter.in, Arrays.asList("E", "G", "I")),
                new QFilter(TripReqBillConstant.NCKD_TRAVEL_ISDRAFT, QFilter.equals, Boolean.FALSE),
                new QFilter(TripReqBillConstant.RENDDATE, QFilter.less_than, firstDay),
        });

        List<String> tripReqBillNos = tripReqBillCol.stream().map(dyx -> dyx.getString(TripReqBillConstant.BILLNO)).collect(Collectors.toList());

        createTravelFinanDraft(tripReqBillNos,bzAmount);
        logger.info("TravelFinanDraftCreateTask end");
    }

    protected void createTravelFinanDraft(List<String> billNos,BigDecimal bzAmount) {
        // 创建商旅财务对账底稿
        List<Long> tripReqBillList = QueryServiceHelper.query(TripReqBillConstant.ENTITYID, TripReqBillConstant.ID, new QFilter(TripReqBillConstant.BILLNO, QCP.in, billNos).toArray())
                .stream().map(r -> r.getLong(TripReqBillConstant.ID)).collect(Collectors.toList());
        DynamicObject[] tripReqBillArray = BusinessDataServiceHelper.load(tripReqBillList.toArray(), EntityMetadataCache.getDataEntityType(TripReqBillConstant.ENTITYID_F7));

        // 获取关联的火车订单
        Map<String, List<DynamicObject>> trainBillMap = QueryServiceHelper.query("er_trainbill", "oabillnum,ordernum,departtime,arrivetime,departcity,arrivecity,totalamount", new QFilter("oabillnum", QCP.in, billNos).toArray())
                .stream()
                .collect(Collectors.groupingBy(r -> r.getString("oabillnum")));

        // 获取机票订单
        Map<String, List<DynamicObject>> planBillMap = QueryServiceHelper.query("er_planebill", "oabillnum,ordernum,takeofftime,landingtime,fromcityname,tocityname,totalamount", new QFilter("oabillnum", QCP.in, billNos).toArray())
                .stream()
                .collect(Collectors.groupingBy(r -> r.getString("oabillnum")));

        // 获取关联的差旅报销发票分录 -》 发票与费用明细分录 联查 行程信息分录 通过源单行程分录ID 联查出差申请
        Map<String, List<DynamicObject>> tripReimInvoiceData = getTripReimInvoiceData(billNos);


        for(DynamicObject tripReqBill : tripReqBillArray){
            String billno = tripReqBill.getString(TripReqBillConstant.BILLNO);
            DynamicObject travelFinanDraftInfo = BusinessDataServiceHelper.newDynamicObject(TravelFinanDraftConstant.ENTITYID);
            travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_TRIPREQBILL, tripReqBill);
            travelFinanDraftInfo.set(TravelFinanDraftConstant.ORG, tripReqBill.get(TripReqBillConstant.COMPANY));
            travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_DEPT, tripReqBill.get(TripReqBillConstant.ORG));
            travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_CONFIRMSTATUS, "10");
            travelFinanDraftInfo.set(TravelFinanDraftConstant.KEY_BILLNO,tripReqBill.getString(TripReqBillConstant.BILLNO));

            DynamicObjectCollection draftEntryCol = travelFinanDraftInfo.getDynamicObjectCollection(TravelFinanDraftConstant.NCKD_DETAILENTITY.ENTITYID);
            if(trainBillMap.containsKey(billno) && trainBillMap.get(billno) != null){
                List<DynamicObject> trainBillList = trainBillMap.get(billno);
                trainBillList.stream().forEach(trainBill -> getDraftData(draftEntryCol.addNew(),"6",trainBill.getString("ordernum"),
                        trainBill.getDate("departtime"),trainBill.getDate("arrivetime"),
                        trainBill.getString("departcity"),trainBill.getString("arrivecity"), trainBill.getBigDecimal("totalamount")));
            }
            if(planBillMap.containsKey(billno) && planBillMap.get(billno) != null){
                List<DynamicObject> planBillList = planBillMap.get(billno);
                planBillList.stream().forEach(planBill -> getDraftData(draftEntryCol.addNew(),"2",planBill.getString("ordernum"),
                        planBill.getDate("takeofftime"),planBill.getDate("landingtime"),
                        planBill.getString("fromcityname"),planBill.getString("tocityname"),planBill.getBigDecimal("totalamount")));
            }
            if(tripReimInvoiceData.containsKey(billno) && tripReimInvoiceData.get(billno) != null){
                List<DynamicObject> tripReimInvoiceList = tripReimInvoiceData.get(billno);
                tripReimInvoiceList.stream().forEach(tripReimInvoice -> getDraftData(draftEntryCol.addNew(),"7",
                        String.join("-",tripReimInvoice.getString("tripBillno"),tripReimInvoice.getString("invoiceentry.invoiceno")),
                        tripReimInvoice.getDate("invoiceentry.carrierdate"),null,
                        tripReimInvoice.getString("invoiceentry.istartcity"),tripReimInvoice.getString("invoiceentry.idestcity"),tripReimInvoice.getBigDecimal("invoiceentry.totalamount")));
            };

            // 获取draftEntryCol中开始时间的额最早时间  和结束时间的最晚时间
            Date earliestTime = draftEntryCol.stream()
                    .filter(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_STARTDATE) != null)
                    .map(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_STARTDATE))
                    .min(Comparator.comparing(Date::getTime)).orElse(null);

            Date latestTime = draftEntryCol.stream()
                    .filter(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FINISHDATE) != null)
                    .map(d -> d.getDate(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FINISHDATE))
                    .max(Comparator.comparing(Date::getTime)).orElse(null);
            //获取latestTime与earliestTime 的日期相差天数，加上1
            if(earliestTime != null && latestTime != null){
                int days = getDays(earliestTime, latestTime, tripReqBill.getDynamicObject(TripReqBillConstant.COMPANY));
                travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_SUMDAYS, days);
                // 查询补助金额
                travelFinanDraftInfo.set(TravelFinanDraftConstant.NCKD_AMOUNT, bzAmount.multiply(BigDecimal.valueOf(days)));
            }
            tripReqBill.set(TripReqBillConstant.NCKD_TRAVEL_ISDRAFT, Boolean.TRUE);

            SaveServiceHelper.save(new DynamicObject[]{ travelFinanDraftInfo });
            SaveServiceHelper.save(new DynamicObject[]{ tripReqBill });
        }
    }

    protected int getDays(Date startTime, Date endTime,DynamicObject org){
        // 获取组织下的工作日历
        DynamicObject workCalendar = getWorkCalandar(org.getLong("id"));
        List<Date> notWorkDayList = new ArrayList<>();
        if(workCalendar != null){
            notWorkDayList = QueryServiceHelper.query("bd_workcalendar", "dateentry.workdate", new QFilter[]{
                    new QFilter("dateentry.datetype", QCP.equals, "3"),
                    new QFilter("dateentry.workdate", QCP.large_equals, startTime),
                    new QFilter("dateentry.workdate", QCP.less_equals, endTime),
                    new QFilter("id", QCP.equals, workCalendar.getPkValue())
            }).stream().map(r -> r.getDate("dateentry.workdate")).collect(Collectors.toList());
        }
        
        // 计算开始日期和结束日期之间总天数
        long diffInMillis = endTime.getTime() - startTime.getTime();
        int totalDays = (int) (diffInMillis / (24 * 60 * 60 * 1000)) + 1;
        
        // 计算工作日天数（总天数减去非工作日天数）
        int workDays = totalDays - notWorkDayList.size();
        
        return workDays;
    }

    protected DynamicObject getWorkCalandar(Long orgId){
        DynamicObject workCalendar = QueryServiceHelper.queryOne("bd_workcalendar", "id", new QFilter("org", QCP.equals, orgId).toArray());
        if(workCalendar == null){
            Map<Long, Long> parentOrgMap = OrgUnitServiceHelper.getDirectSuperiorOrg(OrgViewType.Admin, Collections.singletonList(orgId));
            if(!parentOrgMap.containsKey(orgId)){
                return null;
            }else{
                return getWorkCalandar(parentOrgMap.get(orgId));
            }
        }
        return workCalendar;
    }


    protected DynamicObject getDraftData( DynamicObject draftEntry , String source, String orderNum, Date startTime,
                                          Date endTime , String from , String to , BigDecimal amount){
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_SOURCE, source);
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_ORDER_NUMBER, orderNum);
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_STARTDATE, startTime);
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FINISHDATE, endTime);
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_FROM, from);
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_TO, to);
        draftEntry.set(TravelFinanDraftConstant.NCKD_DETAILENTITY.NCKD_ORDER_AMOUNT, amount);
        return draftEntry;
    }



    protected Map<String, List<DynamicObject>> getTripReimInvoiceData(List<String> billNos){

        // 查询关联的行程分录ID
        DataSet queryTripReqEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), TripReqBillConstant.ENTITYID,
                "billno,tripentry.id", new QFilter("billno", QCP.in, billNos).toArray(), "");
        Set tripEntryIdSet = CommonUtils.getFiledFromDataSet(queryTripReqEntryDataSet, "tripentry.id");
        // 通过行程分录ID 联查差旅报销行程
        // 差旅报销单行程分录ID -> 出差申请分录ID映射
        DataSet tripReimEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), BillTypeConstants.ER_TRIPREIMBURSEBILL, "tripentry.tripentrysourceid,tripentry.entryentity.id",
                new QFilter("tripentry.tripentrysourceid", QCP.in, tripEntryIdSet).toArray(), "");
        Set tripReimEntryIdSet = CommonUtils.getFiledFromDataSet(tripReimEntryDataSet, "tripentry.entryentity.id");

        // 获取行程信息分录下的发票信息
        DataSet queryReimInvoiceEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), BillTypeConstants.ER_TRIPREIMBURSEBILL, "invoiceitementry.itementryid,invoiceitementry.invoiceheadentryid",
                new QFilter("invoiceitementry.itementryid", QCP.in, tripReimEntryIdSet).toArray(), "");
        Set invoiceEntryIdSet = CommonUtils.getFiledFromDataSet(queryReimInvoiceEntryDataSet, "invoiceitementry.invoiceheadentryid");


        // 查询发票相关信息
        DataSet queryInvoiceEntryDataSet = QueryServiceHelper.queryDataSet(this.getClass().getName(), BillTypeConstants.ER_TRIPREIMBURSEBILL, "billno,invoiceentry.id,invoiceentry.invoiceno,invoiceentry.carrierdate,invoiceentry.istartcity,invoiceentry.idestcity,invoiceentry.totalamount",
                new QFilter("invoiceentry.id", QCP.in, invoiceEntryIdSet).toArray(), "");

        // 建议出差申请和发票明细的关联关系
        DataSet queryReimInvoiceDataSet = queryTripReqEntryDataSet.leftJoin(tripReimEntryDataSet)
                .on("tripentry.id", "tripentry.tripentrysourceid").select("billno", "tripentry.entryentity.id").finish()
                .leftJoin(queryReimInvoiceEntryDataSet)
                .on("tripentry.entryentity.id", "invoiceitementry.itementryid").select("billno", "invoiceitementry.invoiceheadentryid").finish()
                .leftJoin(queryInvoiceEntryDataSet)
                .on("invoiceitementry.invoiceheadentryid", "invoiceentry.id").select(new String[]{ "billno" }, new String[]{ "billno tripBillno","invoiceentry.invoiceno", "invoiceentry.carrierdate", "invoiceentry.istartcity","invoiceentry.idestcity", "invoiceentry.totalamount" }).finish();
        DynamicObjectCollection reimInvoiceCol = ORM.create().toPlainDynamicObjectCollection(queryReimInvoiceDataSet);
        return reimInvoiceCol.stream().filter(r -> !Objects.isNull(r.getString("invoiceentry.invoiceno"))).collect(Collectors.groupingBy(r -> r.getString("billno")));

    }

}
