global class UpdateRentalApplyBatch implements Database.Batchable<sObject>, Database.Stateful {

    private static final Integer MAXERRORCNT = 20; // 邮件表单位最大错误信息显示数量
    private Date tdy = Date.today();
    private Date timeLine = Date.newInstance(2019, 7, 1);
    private ScBean scB;

    private ErrorBean eb = new ErrorBean(); // 邮件发送ERRORBEAN

    /**
     * startには、queryを実行、备品申请借出历史を検索
     */
    global Database.QueryLocator start(Database.BatchableContext BC) {
        // 因为报错时发邮件的逻辑太多，而发错误邮件做不了Assert，所以使用i++
        Integer i = 0;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        i ++;
        scB = UpdateRentalApplyBatch.setSc('UpdateRentalApplyBatchSchedule', 9, 23, 0, '0 30 2', null);
        if (System.Test.isRunningTest() == false) {
            for(CronTrigger ct : [SELECT Id FROM CronTrigger WHERE CronJobDetail.Name =: scB.scName]) {
                System.abortJob(ct.Id);
            }
            system.schedule(scB.scName, scB.scTime, new UpdateRentalApplyBatchSchedule());
        }
        return Database.getQueryLocator([SELECT Id
                  , Bollow_Date__c
                  , next_action_Text__c
                  , next_action__c
                  , Request_demo_day__c
                  , RA_Status__c
                  , QISRepair__c
                  , QIS_number__c
                  , QIS_Reply_day__c
                  , QIS_Cancel_Submit_day__c
                  , QIS_cancel_date__c
                  , demo_purpose2__c
                  , Bollow_Date_Add_7_WD__c
                  , Bollow_Date_Add_10_WD__c
                  , NewRepair__c
                  , NewRepair_Repair_Shipped_Date__c
                  , NewRepair__r.Repair_Shipped_Date__c
                  , QIS_Repair_Shipped_Date__c
                  , QIS_ReplaceDeliveryDate__c
                  , QIS_number__r.ReplaceDeliveryDate__c
                  , QISRepair__r.Repair_Shipped_Date__c
                  , QIS_number__r.QIS_cancel_date__c
                  , QIS_number__r.QIS_Reply_day__c
                  , QIS_number__r.QIS_Cancel_Submit_day__c
               FROM Rental_Apply__c
              WHERE (Request_demo_day__c >= :timeLine
                        AND Bollow_Date__c != null
                        AND (Bollow_Date_Add_7_WD__c = null OR Bollow_Date_Add_10_WD__c = null)
                    )
                 OR (
                     (
                        (demo_purpose2__c = '索赔QIS'
                            AND (QISRepair__c != null
                                OR QIS_number__c != null)
                        )
                        OR NewRepair__c != null
                     )

                    AND RA_Status__c != '取消'
                    AND RA_Status__c != '完了'
                    AND RA_Status__c != '删除'
                    )]);
    }

    public static ScBean setSc(String baseName, Integer minhour, Integer maxhour, Integer minMin, String spbefore, String spafter) {
        Datetime dt = Datetime.now();
        Integer year = Integer.valueOf(dt.format('yyyy'));//2021-12-29 you YYYY--->yyyy 是因为最后一周跨年了
        Integer month = Integer.valueOf(dt.format('MM'));
        Integer day = Integer.valueOf(dt.format('dd'));
        Integer hour = Integer.valueOf(dt.format('HH'));
        Integer min = Integer.valueOf(dt.format('mm'));
        ScBean b = new ScBean();
        if (hour == maxhour && min >= (minMin + 45)) {
            b.scName = baseName + '001';
            if (spafter != null && String.isNotBlank(spafter)) {
                b.scName = baseName + '005';
                b.scTime = spafter + ' ' + day + ' ' + month + ' ? ' + year;
                // system.schedule(sJobame,spafter + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
                return b;
            }
            else {
                dt = Datetime.now().addDays(1);
                year = Integer.valueOf(dt.format('yyyy'));//2021-12-29 you YYYY--->yyyy 是因为最后一周跨年了
                month = Integer.valueOf(dt.format('MM'));
                day = Integer.valueOf(dt.format('dd'));
                hour = Integer.valueOf(dt.format('HH'));
                min = Integer.valueOf(dt.format('mm'));
                if (spbefore != null && String.isNotBlank(spbefore)) {
                    b.scName = baseName + '006';
                    // '0 20 8
                    b.scTime = spbefore + ' ' + day + ' ' + month + ' ? ' + year;
                    // system.schedule(sJobame,spbefore + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
                }
                else {
                    b.scTime = '0 ' + minMin + ' ' + minhour + ' ' + day + ' ' + month + ' ? ' + year;
                    // system.schedule(sJobame,'0 ' + minMin + ' ' + minhour + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
                }
            }
        }
        else if (min < minMin) {
            b.scName = baseName + '001';
            b.scTime = '0 ' + minMin + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year;
            // system.schedule(sJobame,'0 ' + minMin + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
        }
        else if (min < minMin + 15) {
            b.scName = baseName + '002';
            b.scTime = '0 ' + (minMin + 15) + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year;
            // system.schedule(sJobame,'0 ' + (minMin + 15) + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
        }
        else if (min < minMin + 30) {
            b.scName = baseName + '003';
            b.scTime = '0 ' + (minMin + 30) + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year;
            // system.schedule(sJobame,'0 ' + (minMin + 30) + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
        }
        else if (min < minMin + 45) {
            b.scName = baseName + '004';
            b.scTime = '0 ' + (minMin + 45) + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year;
            // system.schedule(sJobame,'0 ' + (minMin + 45) + ' ' + hour + ' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());
        }
        else {
            b.scName = baseName + '001';
            b.scTime = '0 ' + minMin + ' ' + (hour + 1) +' ' + day + ' ' + month + ' ? ' + year;
            // system.schedule(sJobame,'0 ' + minMin + ' ' + (hour + 1) +' ' + day + ' ' + month + ' ? ' + year, new AgencyShareUpdateBatchSchedule());

        }
        return b;
    }

    public static void removeOtherSc(String likeName, String needName) {
        String likeStr = likeName + '00%';
        for(CronTrigger ct : [SELECT Id,CronJobDetailId,CronExpression,CronJobDetail.Name
                                FROM CronTrigger
                               WHERE CronJobDetail.Name Like :likeStr
                                 AND CronJobDetail.Name != :needName]){
            System.abortJob(ct.Id);
        }
    }


    global void execute(Database.BatchableContext BC, List<Rental_Apply__c> allRaList) {
        Map<Id, Rental_Apply__c> needUpdateMap = new Map<Id, Rental_Apply__c>();

        List<Rental_Apply__c> raList = new List<Rental_Apply__c>();
        List<Rental_Apply__c> raList1 = new List<Rental_Apply__c>();
        for (Rental_Apply__c ra : allRaList) {
            if (ra.Request_demo_day__c >= timeLine
                && ra.Bollow_Date__c != null
                && (ra.Bollow_Date_Add_7_WD__c == null || ra.Bollow_Date_Add_10_WD__c == null) ) {
                raList.add(ra);
            }
            if (
                (ra.demo_purpose2__c == '索赔QIS'
                    && (ra.QISRepair__c != null
                        || ra.QIS_number__c != null))
                    || ra.NewRepair__c != null

                ) {
                raList1.add(ra);
            }
        }

        // List<Rental_Apply__c> raList = [SELECT Id
        //           , Bollow_Date__c
        //           , next_action_Text__c
        //           , next_action__c
        //           , QIS_Repair_Shipped_Date__c
        //           , QIS_ReplaceDeliveryDate__c
        //           , QIS_number__r.ReplaceDeliveryDate__c
        //           , QISRepair__r.Repair_Shipped_Date__c
        //        FROM Rental_Apply__c
        //       WHERE Request_demo_day__c >= :timeLine
        //         AND Bollow_Date__c != null
        //         AND Bollow_Date_Add_7_WD__c = null];

        if (raList.size() > 0) {
            Date minDate = Date.newInstance(4000, 12, 31);
            Date maxDate = Date.newInstance(1700, 1, 1);
            for (Rental_Apply__c ra : raList) {
                if (minDate > ra.Bollow_Date__c) {
                    minDate = ra.Bollow_Date__c;
                }
                if (maxDate < ra.Bollow_Date__c) {
                    maxDate = ra.Bollow_Date__c;
                }
            }
            List<OlympusCalendar__c> ocList = [SELECT Id
                        , Date__c
                     FROM OlympusCalendar__c
                    WHERE Date__c >= :minDate
                      AND Date__c <= :maxDate.addDays(30)
                      AND IsWorkDay__c = 1
                    ORDER BY Date__c ASC];
            Map<Date, Date> dateadd7Map = new Map<Date, Date>();
            Map<Date, Date> dateadd10Map = new Map<Date, Date>();
            Integer ocListSize = ocList.size();
            if (ocListSize > 7) {
                for (Integer i = 0; i < ocListSize - 7; i ++) {
                    dateadd7Map.put(ocList[i].Date__c, ocList[i + 7].Date__c);
                    if(i + 10 < ocListSize) {
                        dateadd10Map.put(ocList[i].Date__c, ocList[i + 10].Date__c);
                    }
                }
                List<Rental_Apply__c> hDayRaesList = new List<Rental_Apply__c>();
                for (Rental_Apply__c ra : raList) {
                    if (dateadd7Map.containsKey(ra.Bollow_Date__c)) {
                        Rental_Apply__c ra1 = ra;
                        if (needUpdateMap.containsKey(ra1.Id)) {
                            ra1 = needUpdateMap.get(ra1.Id);
                        }
                        else {
                            needUpdateMap.put(ra1.Id, ra1);
                        }
                        ra1.Bollow_Date_Add_7_WD__c = dateadd7Map.get(ra.Bollow_Date__c);
                        if(dateadd10Map.containsKey(ra.Bollow_Date__c)) {
                            ra1.Bollow_Date_Add_10_WD__c = dateadd10Map.get(ra.Bollow_Date__c);
                        }
                    }
                    else {
                        hDayRaesList.add(ra);
                    }
                }
                if (hDayRaesList.size() > 0) {
                    for (Integer i = 0; i < hDayRaesList.size(); i ++) {
                        for (Integer j = 0; j < ocListSize; j ++) {
                            if (j != ocListSize - 1
                                    && (ocList[j].Date__c <  hDayRaesList[i].Bollow_Date__c
                                            || (j == 0 && ocList[j].Date__c >  hDayRaesList[i].Bollow_Date__c)
                                        )
                                    && ocList[j + 1].Date__c > hDayRaesList[i].Bollow_Date__c) {
                                Rental_Apply__c ra1 = hDayRaesList[i];
                                if (needUpdateMap.containsKey(ra1.Id)) {
                                    ra1 = needUpdateMap.get(ra1.Id);
                                }
                                else {
                                    needUpdateMap.put(ra1.Id, ra1);
                                }
                                ra1.Bollow_Date_Add_7_WD__c = dateadd7Map.get(ocList[j].Date__c);
                                if(dateadd10Map.containsKey(ra1.Bollow_Date__c)) {
                                    ra1.Bollow_Date_Add_10_WD__c = dateadd10Map.get(ra1.Bollow_Date__c);
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        // raList = [SELECT Id
        //           , next_action_Text__c
        //           , next_action__c
        //           , QIS_Repair_Shipped_Date__c
        //           , QIS_ReplaceDeliveryDate__c
        //           , QIS_number__r.ReplaceDeliveryDate__c
        //           , QISRepair__r.Repair_Shipped_Date__c
        //        FROM Rental_Apply__c
        //       WHERE demo_purpose2__c = '索赔QIS'
        //         AND (QISRepair__c != null
        //             OR QIS_number__c != null)];
        if (raList1.size() > 0) {
            List<Rental_Apply__c> needUpdateRa = new List<Rental_Apply__c>();
            for (Rental_Apply__c ra : raList1) {
                if (needUpdateMap.containsKey(ra.Id)) {
                    setValue(needUpdateMap.get(ra.Id));
                }
                else {
                    Boolean haveChange = setValue(ra);
                    if (haveChange) {
                        needUpdateRa.add(ra);
                    }
                }
            }

            if (needUpdateRa.size() > 0) {
                Database.SaveResult[] saveRes = Database.update(needUpdateRa, false);
                // 备品借出申请更新保存Error
                eb.setError(saveRes, MAXERRORCNT, Rental_Apply__c.sObjectType);
            }
        }

        if (!needUpdateMap.isEmpty()) {
            Database.SaveResult[] saveRes = Database.update(needUpdateMap.values(), false);
            // 备品借出申请更新保存Error
            eb.setError(saveRes, MAXERRORCNT, Rental_Apply__c.sObjectType);
        }
    }

    private Boolean setValue(Rental_Apply__c ra) {
        Boolean haveChange = false;
        if (ra.QIS_Repair_Shipped_Date__c != ra.QISRepair__r.Repair_Shipped_Date__c) {
            ra.QIS_Repair_Shipped_Date__c = ra.QISRepair__r.Repair_Shipped_Date__c;
            haveChange = true;
        }
        if (ra.QIS_ReplaceDeliveryDate__c != ra.QIS_number__r.ReplaceDeliveryDate__c) {
            ra.QIS_ReplaceDeliveryDate__c = ra.QIS_number__r.ReplaceDeliveryDate__c;
            haveChange = true;
        }
        if (ra.next_action_Text__c != ra.next_action__c) {
            ra.next_action_Text__c = ra.next_action__c;
            haveChange = true;
        }
        if (ra.QIS_Reply_day__c != ra.QIS_number__r.QIS_Reply_day__c) {
            ra.QIS_Reply_day__c = ra.QIS_number__r.QIS_Reply_day__c;
            haveChange = true;
        }
        if (ra.QIS_cancel_date__c != ra.QIS_number__r.QIS_cancel_date__c) {
            ra.QIS_cancel_date__c = ra.QIS_number__r.QIS_cancel_date__c;
            haveChange = true;
        }
        if (ra.QIS_Cancel_Submit_day__c != ra.QIS_number__r.QIS_Cancel_Submit_day__c) {
            ra.QIS_Cancel_Submit_day__c = ra.QIS_number__r.QIS_Cancel_Submit_day__c;
            haveChange = true;
        }
        if (ra.NewRepair_Repair_Shipped_Date__c != ra.NewRepair__r.Repair_Shipped_Date__c) {
            ra.NewRepair_Repair_Shipped_Date__c = ra.NewRepair__r.Repair_Shipped_Date__c;
            haveChange = true;
        }
        return haveChange;
    }

    global void finish(Database.BatchableContext BC) {
        Boolean haveError = false;
        String body = '';
        for (Id objId : eb.messageMap.keySet()) {
            haveError = true;
            body += eb.messageMap.get(objId) + '<br/>';
        }
        if (eb.overMax) {
            body += ':Over ' + MAXERRORCNT + 'Record<br/>';
        }
        UpdateRentalApplyBatch.removeOtherSc('UpdateRentalApplyBatchSchedule', scB.scName);
        if (haveError == true) {
            // BatchユーザId
            String batchUserId = System.Label.Batch_User_Id;
            List<User> us = [Select Id,NAme,Email From User Where Id =: batchUserId];
            if (!us.isEmpty()) {
                User use = us[0];
                if (String.isNotBlank(use.Email)) {
                    List<String> MailCc;
                    if (System.Label.Batch_Error_Send_To_CC != 'null') {
                        MailCc = System.Label.Batch_Error_Send_To_CC.split(',');
                    }
                    FixtureUtil.sendMessage(batchUserId,
                                            MailCc,
                                            'UpdateRentalApplyBatch Error',
                                             body
                                            );
                }
            }
        }
    }

    /************************* Inner Class ******************************/
    public class ErrorBean{
        // public String objectName;
        // public String objectLabel;
        public Map<Id, String> messageMap;
        public Boolean overMax;
        // public ErrorBean(Schema.sObjectType obj) {
        //     objectName = obj.getDescribe().getName();
        //     objectLabel = obj.getDescribe().getLabel();
        //     messageMap = new Map<Id, String>();
        //     overMax = false;
        // }
        public ErrorBean() {
          messageMap = new Map<Id, String>();
          overMax = false;
        }
        public void setError (Database.SaveResult[] saveRes, Integer maxCut, Schema.sObjectType obj) {
            if (messageMap.keySet().size() <= maxCut && overMax == false) {
                String objectName = obj.getDescribe().getName();
                String objectLabel = obj.getDescribe().getLabel();
                for (Database.SaveResult saveRe : saveRes) {
                    if (!saveRe.isSuccess()) {
                        if (!messageMap.containsKey(saveRe.getId())) {
                            if (messageMap.keySet().size() >= maxCut) {
                                overMax = true;
                                break;
                            }
                            for (Database.Error err : saveRe.getErrors()) {

                                String message = objectName + ':'
                                               + objectLabel + ':'
                                               + err.getStatusCode() + ':'
                                               + err.getFields() + ':'
                                               + err.getMessage();
                                // 数据里面有复数错误信息的话只获取第一条
                                messageMap.put(saveRe.getId(), message);
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    public static void run() {
        Database.executeBatch(new UpdateRentalApplyBatch(), 200);
    }

    public Class ScBean {
        public String scName;
        public String scTime;
    }
}