public with sharing class MonthEndAutoJudgeNotUpdateBatch implements Database.Batchable<SObject>, Database.Stateful {
    public final List<Id> TEST_ID = null;
    private BatchIF_Log__c iflog;

    // 当前年月
    private Integer yearInteger = Date.today().year();
    private Integer monInteger = Date.today().month();

    public MonthEndAutoJudgeNotUpdateBatch(){}
    /**
     *
     *
     * @param testId 测试的Id 集合
     * @param flag  用于筛选数据的条件
     */
    public MonthEndAutoJudgeNotUpdateBatch(List<Id> testId) {
        TEST_ID = testId;
        System.debug('TEST_ID=' + TEST_ID);
        iflog = new BatchIF_Log__c();
        iflog.Type__c = 'MonthEndAutoJudgeNotUpdateBatch';
        iflog.Log__c = 'MonthEndAutoJudgeNotUpdateBatch start\n';
        iflog.ErrorLog__c = '';
        insert iflog;
    }

    /**
     * startには、queryを実行、引合全件
     */
    public Database.QueryLocator start(Database.BatchableContext BC) {
        System.debug('start====');
        String staSql = 'SELECT id,Name,Owner__c,CreatedDate,Special_Apply_Reason__c,';
        staSql += ' Shipping_Scheduled_Date__c,DeliveryDate__c,DeliveryStatus__c,';
        staSql += ' PaymentStatus__c,Monthly_forecast_shipping__c,NM_forecast_shipping__c,';
        staSql += ' End_User_contract__c,ContractReceivedDate__c FROM Statu_Achievements__c';
        staSql += ' where (( OverviewStatus__c <> \'无效合同\' and (OverviewStatus__c <> \'订单完成\' or (SS_backorderID__c <> null and OverviewStatus_SS_IsEqual__c = false))) or Forecast_Equal__c=false)  ';

        // 当前月的第一个工作日 +1便于做判断
        Date firstWorkDate = getFirstWorkDayOfMonth().addDays(1);
        // 当前月的最后五个工作日中最小的那天
        Date minOfLastFiveWorkDate = getLastWorkDay();

        // 付款状态
        List<String> paymentStatus = new List<String>();
        paymentStatus.add('尾款完成');
        paymentStatus.add('100%预付完成');
        paymentStatus.add('不需付款');
        paymentStatus.add('尾款支付');

        // 排除预测发货日为null的数据
        // 第一个工作日记录的预测对象的注残
        // 最后五个工作日前,提交用户合同并付全款
        staSql += ' AND Shipping_Scheduled_Date__c <> null' +
                '   AND ContractReceivedDate__c < :minOfLastFiveWorkDate' +
                '   AND End_User_contract__c = \'已提交\'' +
                '   AND PaymentStatus__c IN :paymentStatus';

        // 如果给定id值,跑指定id的数据
        if (TEST_ID != null && TEST_ID.size() > 0) {
            if (!Test.isRunningTest()) {
                staSql += ' AND CreatedDate < :firstWorkDate AND id IN :TEST_ID ';
            } else {
                staSql += ' AND id IN :TEST_ID';
            }
        // 如果没有给定值,默认跑全部数据,如果在正式环境还需添加创建日期的条件
        } else {
            if (!Test.isRunningTest()) {
                staSql += ' AND CreatedDate < :firstWorkDate ';
            }
        }

        // System.debug('staSql=' + staSql);
        return Database.getQueryLocator(staSql);
    }

    public void execute(Database.BatchableContext BC, List<Statu_Achievements__c> staList) {
        // System.debug('stalist=' + staList.size());

        // 迭代所有数据
        for (Statu_Achievements__c sa : staList) {

            // 有本月发货预测的标记
            if ('本月发货预测'.equals(sa.Monthly_forecast_shipping__c)) {
                // 本月没有发货
                if (!('部分交付'.equals(sa.DeliveryStatus__c)) && !('已完全交付'.equals(sa.DeliveryStatus__c)) && sa.DeliveryDate__c == null) {
                    clearMonthlyForecastShipping(sa);
                }

                // 如果发货了
                if (sa.DeliveryDate__c != null && !String.isBlank(sa.DeliveryStatus__c) && !('未交付'.equals(sa.DeliveryStatus__c))) {
                    // 但发货日不在本年本月 或者发货日根本不在本年 删除本月预测
                    if ((sa.DeliveryDate__c.year() == yearInteger && sa.DeliveryDate__c.month() != monInteger) || sa.DeliveryDate__c.year() != yearInteger) {
                        clearMonthlyForecastShipping(sa);
                    }
                }
            }

            // 如果符合本月预测的时间但没有做本月发货预测的标记
            if (sa.Shipping_Scheduled_Date__c.year() == yearInteger && sa.Shipping_Scheduled_Date__c.month() == monInteger) {
                // 如果之前做过次月预测的标记删除
                clearNMForecastShipping(sa);
                // 已发货
                if (!String.isBlank(sa.DeliveryStatus__c) && !('未交付'.equals(sa.DeliveryStatus__c))) {
                    // 已发货后说明有发货日期,查看是否本月发货 如果是本月发货添加发货标记
                    if (sa.DeliveryDate__c.year() == yearInteger && sa.DeliveryDate__c.month() == monInteger) {
                        // 添加本月发货预测
                        if (String.isBlank(sa.Monthly_forecast_shipping__c)){
                            sa.Monthly_forecast_shipping__c = '本月发货预测';
                             // System.debug('添加本月');
                            iflog.Log__c += 'Name: '+sa.Name+' 添加本月发货预测\n';
                            sa.Special_Apply_Reason__c += ' 系统自动,添加本月发货预测';
                        }
                       
                    }
                }
            }

            // 有次月发货预测标记 或者时间是次月预测
            if ('次月发货预测'.equals(sa.NM_forecast_shipping__c) || isNMForecastShipping(sa)) {
                // 并且在本月完成全部发货
                if ('已完全交付'.equals(sa.DeliveryStatus__c) && sa.DeliveryDate__c.year() == yearInteger && sa.DeliveryDate__c.month() == monInteger) {
                    clearMonthlyForecastShipping(sa);
                    clearNMForecastShipping(sa);
                }

                // 如果发货了
                if (sa.DeliveryDate__c != null && !String.isBlank(sa.DeliveryStatus__c) && !('未交付'.equals(sa.DeliveryStatus__c))) {
                    // 但发货日不在本年本月 或者发货日根本不在本年 删除次月预测
                    if ((sa.DeliveryDate__c.year() == yearInteger && sa.DeliveryDate__c.month() != monInteger) || sa.DeliveryDate__c.year() != yearInteger) {
                        clearNMForecastShipping(sa);
                    }
                }
            }

            //   当月没有完成全部发货
            if (!('已完全交付'.equals(sa.DeliveryStatus__c))) {
                // 如果月份是12月
                // 次月为 下一年的一月份
                // 如果没有次月发货预测的标记 判断是否符合条件
                if (isNMForecastShipping(sa)) {
                    // 添加次月时无论如何删除掉本月
                    clearMonthlyForecastShipping(sa);
                    if (String.isBlank(sa.NM_forecast_shipping__c)) {
                        sa.NM_forecast_shipping__c = '次月发货预测';
                        // System.debug('添加次月');
                        iflog.Log__c += 'Name: '+sa.Name+' 添加次月发货预测\n';
                        sa.Special_Apply_Reason__c += ' 系统自动,添加次月发货预测';
                    }
                    // 如果不是次月预测 但有标记 删除该标记
                } else {
                    clearNMForecastShipping(sa);
                }
            }

        }

        // 不会做修改
        // Database.update(staList);
    }

    public void finish(Database.BatchableContext BC) {
        iflog.Log__c += 'MonthEndAutoJudgeNotUpdateBatch end';
        String tmp = iflog.ErrorLog__c;
        if (tmp.length() > 65000) {
            tmp = tmp.substring(0, 65000);
            tmp += ' ...have more lines...';
            iflog.ErrorLog__c = tmp;
        }
        update iflog;
    }

    /**
     * 删除本月发货预测
     * @param sa
     */
    public void clearMonthlyForecastShipping(Statu_Achievements__c sa) {
        if (!String.isBlank(sa.Monthly_forecast_shipping__c)) {
            sa.Monthly_forecast_shipping__c = null;
            // System.debug('删除 本月');
            iflog.Log__c += 'Name: '+sa.Name+' 删除本月发货预测\n';
            sa.Special_Apply_Reason__c += ' 系统自动,删除本月发货预测';
        }
    }

    /**
     * 删除次月发货预测
     *
     * @param sa
     */
    public void clearNMForecastShipping(Statu_Achievements__c sa) {
        if (!String.isBlank(sa.NM_forecast_shipping__c)) {
            sa.NM_forecast_shipping__c = null;
            System.debug('删除 次月');
            iflog.Log__c += 'Name: '+sa.Name+' 删除次月发货预测\n';
            sa.Special_Apply_Reason__c += ' 系统自动,删除次月发货预测';
        }
    }

    /**
     * 判断是否是 次月预测
     *
     * @param sa
     *
     * @return
     */
    public boolean isNMForecastShipping(Statu_Achievements__c sa) {
        return (sa.Shipping_Scheduled_Date__c.year() == yearInteger && sa.Shipping_Scheduled_Date__c.month() == monInteger + 1) ||
                (sa.Shipping_Scheduled_Date__c.year() == yearInteger + 1 && sa.Shipping_Scheduled_Date__c.month() == 1 && monInteger == 12);
    }

    /**
     * 获取当前月份的第一个工作日
     *
     * @return
     */
    public Date getFirstWorkDayOfMonth() {
        // 获取当前月份的第一天
        Date firstDayOfMonth = Date.today().toStartOfMonth();
        // 将月份推到下一个月
        Date nextMonth = Date.newInstance(firstDayOfMonth.year(), firstDayOfMonth.month() + 1, 1);
        // 获取当前月份最后一天
        // System.debug('lastDay:' + nextMonth.addDays(-1));
        

        Date lastDayOfMonth = nextMonth.addDays(-1);
        // 此表会维护一个工作日的日历 其中排除掉了周末及节假日 此处通过排序加limit 1 只拿到第一个工作日
        List<OlympusCalendar__c> olympusCalendarDate = [
                Select Date__c
                From OlympusCalendar__c
                Where Date__c >= :firstDayOfMonth
                And Date__c <= :lastDayOfMonth
                And IsWorkDay__c = 1
                ORDER BY Date__c
                LIMIT 1
        ];
        Date firstWorkDayOfMonth = null;
        if (olympusCalendarDate.size() > 0) {
             firstWorkDayOfMonth = olympusCalendarDate.get(0).Date__c;
        }
        // System.debug('firstWorkDayOfMonth' + firstWorkDayOfMonth);
        return firstWorkDayOfMonth;
    }

    /**
     * 获取最后五个工作日中的最小的那天
     *
     * @return
     */
    public Date getLastWorkDay() {
        // 获取当前月份的第一天
        Date firstDayOfMonth = Date.today().toStartOfMonth();
        // 将月份推到下一个月
        Date nextMonth = Date.newInstance(firstDayOfMonth.year(), firstDayOfMonth.month() + 1, 1);
        // 获取当前月份最后一天
        // System.debug('lastDay:' + nextMonth.addDays(-1));
        Date lastDayOfMonth = nextMonth.addDays(-1);
        // 此表会维护一个工作日的日历 其中排除掉了周末及节假日 此处通过排序加limit 5 拿到最后五个工作日
        List<OlympusCalendar__c> olympusCalendarDates = [
                Select Date__c
                From OlympusCalendar__c
                Where Date__c >= :firstDayOfMonth
                And Date__c <= :lastDayOfMonth
                And IsWorkDay__c = 1
                ORDER BY Date__c DESC
                LIMIT 5
        ];
        // System.debug('List<OlympusCalendar__c> :' + olympusCalendarDates);
        Date result;
        if (olympusCalendarDates.size() > 0) {
            result = olympusCalendarDates.get(olympusCalendarDates.size() - 1).Date__c;
            // System.debug('result' + result);
        }
        return result;
    }

}