global class OPDQuarterPlanPhotographBatch implements Database.Batchable<sObject>, Database.Stateful {
    public static Integer FIELDMAX = 30;
    List<String> oPDPlanIds= null;
    private BatchIF_Log__c iflog;
    private String thismonth_Lastday;
    //自定义设置-SS_BatchColumnMapping
    //1.新创建SS_BatchColumnMapping(设置类型：层次结构)-
    //2.处理:方案设置-管理列表自定义设置类型(选择,需创建之前改好)-SS_BatchColumnMapping(设置类型：列表)
    //3.FIELDMAX = 30，SS_BatchColumnMapping这里只设置了30个字段，若想提高数量需在同时增加数量
    global OPDQuarterPlanPhotographBatch() {
        thismonth_lastday = String.valueOf(Date.toDay().addMonths(-1).toStartOfMonth());
        iflog = new BatchIF_Log__c();
        iflog.Type__c = 'PushNotification';
        iflog.Log__c  = 'OPDQuarterPlanPhotographBatch start\n';
        iflog.ErrorLog__c = '';
        insert iflog;
    }
    //  格式 为"2019-03-31"
    global OPDQuarterPlanPhotographBatch(string thismonth_Lastday) {
        this.thismonth_Lastday = thismonth_Lastday;
    }
    // 指定"OPD计划"的Id
    global OPDQuarterPlanPhotographBatch(List<String> oPDPlanIds) {
        this.oPDPlanIds = oPDPlanIds;
        iflog = new BatchIF_Log__c();
        iflog.Type__c = 'OPDQuarterPlanPhotographBatch';
        iflog.Log__c  = 'OPDQuarterPlanPhotographBatch start\n';
        iflog.ErrorLog__c = '';
        insert iflog;
    }


    global Database.QueryLocator start(Database.BatchableContext BC) {
        // select項目を opdpMapping より生成
        SS_BatchColumnMapping__c opdpMapping = SS_BatchColumnMapping__c.getValues('OPDPlan__c');
        Set<String> apiTempSet = new Set<String>();
        for (Integer i = 1; i <= FIELDMAX; i++) {
            String lpadI = ('00' + i).right(3);
            String fromColumn = 'From_Column_' + lpadI + '__c';
            String apiStr = String.valueOf(opdpMapping.get(fromColumn));
            if (String.isBlank(apiStr) == false) {
                String ssColumn = 'SS_Column_' + lpadI + '__c';
                String ssApiStr = String.valueOf(opdpMapping.get(ssColumn));
                if (ssApiStr.toLowerCase().startsWith('opdplan__c.')) {
                    // 商談の項目を更新、変更があるかどうか判断するため、元データも取得
                    apiTempSet.add(ssApiStr.substring(11));
                }
            }
        }
        String soql = makeSql(opdpMapping, apiTempSet);
        if (oPDPlanIds <> null) {
            soql += ' where Id IN: oPDPlanIds';
        } else {
            Date nowDay = Date.valueOf(thismonth_Lastday);
            soql += ' Where (FinalDate__c = Null or FinalDate__c >=  :nowDay) And OPDPlanMark__c != \'1\'';
        }
        System.debug('soql=' + soql);
        return Database.getQueryLocator(soql);

    }

    global void execute(Database.BatchableContext BC, List<OPDPlan__c> OPDPlanList) {
        List<SObject> updSelfList = new List<SObject>();
        List<SObject> insSSOpdpList = new List<SObject>();
        // 商談
        SS_BatchColumnMapping__c opdpMapping = SS_BatchColumnMapping__c.getValues('OPDPlan__c');
        Schema.SObjectType ssOppType = Schema.getGlobalDescribe().get(String.valueOf(opdpMapping.get('SS_TableName__c')));
        for (OPDPlan__c opdp : OPDPlanList) {
            Boolean updSelfFlg = false;
            SObject insSSOpdp = ssOppType.newSObject();
            for (Integer i = 1; i <= FIELDMAX; i++) {
                String lpadI = ('00' + i).right(3);
                String fromColumn = 'From_Column_' + lpadI + '__c';
                String apiStr = String.valueOf(opdpMapping.get(fromColumn));
                if (String.isBlank(apiStr) == false) {
                    String ssColumn = 'SS_Column_' + lpadI + '__c';
                    String ssApiStr = String.valueOf(opdpMapping.get(ssColumn));
                    if (ssApiStr.toLowerCase().startsWith('opdplan__c.')) {
                        // 項目更新、変更判断
                        if (opdp.get(ssApiStr.substring(11)) != opdp.get(apiStr)) {
                            try {
                                opdp.put(ssApiStr.substring(11), opdp.get(apiStr));
                                updSelfFlg = true;
                            } catch (Exception e) {
                                iflog.ErrorLog__c = 'ERROR [' + apiStr + '] => [' + ssApiStr + ']' + e.getMessage() + '\n';
                            }
                        }
                    } else {
                        try {
                            insSSOpdp.put(ssApiStr, getValue(opdp, apiStr));
                        } catch (Exception e) {
                            iflog.ErrorLog__c = 'ERROR ' + String.valueOf(opdpMapping.get('SS_TableName__c')) + ' [' + apiStr + '] => [' + ssApiStr + ']' + e.getMessage() + '\n';
                        }
                    }
                }
            }
            if (updSelfFlg) {
                updSelfList.add(opdp);
            }
            insSSOpdpList.add(insSSOpdp);
        }

        // 记下更新OPD计划错误log
        if (updSelfList.size() > 0) {
            Database.SaveResult[] lsr = Database.update(updSelfList, false);
            for (Integer tIdx = 0; tIdx < lsr.size(); tIdx++) {
                Database.SaveResult sr = lsr[tIdx];
                if (!sr.isSuccess()) {
                    Database.Error emsg = sr.getErrors()[0];
                    iflog.ErrorLog__c += 'ERROR ' + updSelfList[tIdx].Id + ' OPDPlan__c:' + emsg + '\n';
                }
            }
        }

        //插入备份表
        Database.SaveResult[] lsr2 = Database.insert(insSSOpdpList, false);
        for (Integer tIdx = 0; tIdx < lsr2.size(); tIdx++) {
            Database.SaveResult sr = lsr2[tIdx];
            if (!sr.isSuccess()) {
                Database.Error emsg = sr.getErrors()[0];
                iflog.ErrorLog__c += 'ERROR ' + insSSOpdpList[tIdx].get('SS_OPDQuarterPlan__c') + ' SS_opd:' + emsg + '\n';
            }
        }

    }

    public static String makeSql(SS_BatchColumnMapping__c setting, Set<String> apiTempSet) {
        apiTempSet.add('Id');
        for (Integer i = 1; i <= FIELDMAX; i++) {
            String fromColumn = 'From_Column_' + ('00' + i).right(3) + '__c';
            String apiStr = String.valueOf(setting.get(fromColumn));
            if (String.isBlank(apiStr) == false && apiTempSet.contains(apiStr) == false) {
                apiTempSet.add(apiStr);
            }
        }
        String soql = 'Select ' + String.join(new List<String>(apiTempSet), ',') + ' from ' + setting.Name;
        return soql;
    }
    public static Object getValue(SObject sobj, String field) {
        List<String> fieldPathList = field.split('\\.');
        Object rtn = null;
        for (Integer i = 0; i < fieldPathList.size(); i++) {
            String fieldPath = fieldPathList[i];
            if (i == fieldPathList.size() - 1) {
                rtn = sobj.get(fieldPath);
            } else {
                sobj = sobj.getSObject(fieldPath);
                if (sobj == null) {
                    break;
                }
            }
        }
        return rtn;
    }
    global void finish(Database.BatchableContext BC) {
        iflog.Log__c += '\nOPDQuarterPlanPhotographBatch 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;
    }
}