public without sharing class SSOpportunityBatch implements Database.Batchable<SObject>, Database.Stateful {
    public static Integer FIELDMAX = 200;
    private final List<Id> TEST_ID = null;
    private BatchIF_Log__c iflog;

    /**
     * コンスタント、パラメータを受け取る
     */
    public SSOpportunityBatch(List<Id> testId) {
        TEST_ID = testId;
        System.debug('TEST_ID=' + TEST_ID);
        iflog = new BatchIF_Log__c();
        iflog.Type__c = 'PushNotification';
        iflog.Log__c  = 'SSOpportunityBatch start\n';
        iflog.ErrorLog__c = '';
        insert iflog;
    }
    
    public static String makeSql(SS_Batch_Column_Mapping__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);
System.debug(rtn + '= sobj.get(' + fieldPath + ')');
            } else {
                sobj = sobj.getSObject(fieldPath);
                if (sobj == null) {
                    break;
                }
System.debug(sobj + '= sobj.getSObject(' + fieldPath + ')');
            }
        }
        return rtn;
    }

    /**
     * startには、queryを実行、引合全件
     */
    public Database.QueryLocator start(Database.BatchableContext BC) {
        // select項目を oppMapping より生成
        SS_Batch_Column_Mapping__c oppMapping = SS_Batch_Column_Mapping__c.getValues('Opportunity');
        Set<String> apiTempSet = new Set<String>();
        apiTempSet.add('StageName');
        for (Integer i = 1; i <= FIELDMAX; i++) {
            String lpadI = ('00' + i).right(3);
            String fromColumn = 'From_Column_' + lpadI + '__c';
            String apiStr = String.valueOf(oppMapping.get(fromColumn));
            if (String.isBlank(apiStr) == false) {
                String ssColumn = 'SS_Column_' + lpadI + '__c';
                String ssApiStr = String.valueOf(oppMapping.get(ssColumn));
                if (ssApiStr.toLowerCase().startsWith('opportunity.')) {
                    // 商談の項目を更新、変更があるかどうか判断するため、元データも取得
                    System.debug('ssApiStr=' + ssApiStr + ', substring(12)=' + ssApiStr.substring(12));
                    apiTempSet.add(ssApiStr.substring(12));
                }
            }
        }
        String soql = makeSql(oppMapping, apiTempSet);
        if (TEST_ID <> null) {
            soql += ' where Id IN: TEST_ID';
        } else {
// SS_Prospect_5days_list__c  の対象かどうか、loopの中単独判断
//            soql += ' where StageName = \'引合\'';

            // CHAN-AUE3TB Batch SSOpportunity的执行数据筛选
            // 20230414 DB202304001752 start
            Date nowDay = Date.today();
            nowDay = nowDay.addDays(-15);
            // Integer iyear = nowDay.year();
            Integer iyear = nowDay.year()-1;
            if (nowDay.month() < 4) {
                iyear -= 1;
            }
            // Date thisYd = Date.valueOf(iyear + '-4-1');
            Date thisYd = Date.valueOf(iyear + '-10-1');
            // 20230414 DB202304001752 end 
            soql += ' where StageName = \'引合\' or StageName = \'注残\' or StageName = \'出荷\' or ((StageName = \'完了\' or StageName = \'削除\' or StageName = \'敗戦\') and SFDCLast_Process_Date__c >= :thisYd)';
            soql += '  or (StageName = \'完了\' and Shipping_Finished_Day_Func__c >= :thisYd)';    
        }
        System.debug('soql=' + soql);
        return Database.getQueryLocator(soql);
    }
    
    public void execute(Database.BatchableContext BC, List<Opportunity> oppList) {
        List<Opportunity> updSelfList = new List<Opportunity>();
        List<SObject> insSSOppList = new List<SObject>();
        List<Id> insSSOppId = new List<Id>();
        List<SObject> insSSOliList = new List<SObject>();
        // 商談
        SS_Batch_Column_Mapping__c oppMapping = SS_Batch_Column_Mapping__c.getValues('Opportunity');
        Schema.SObjectType ssOppType = Schema.getGlobalDescribe().get(String.valueOf(oppMapping.get('SS_TableName__c')));
        for (Opportunity opp : oppList) {
            Boolean updSelfFlg = false;
            SObject insSSOpp = 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(oppMapping.get(fromColumn));
                if (String.isBlank(apiStr) == false) {
                    String ssColumn = 'SS_Column_' + lpadI + '__c';
                    String ssApiStr = String.valueOf(oppMapping.get(ssColumn));
                    if (ssApiStr.toLowerCase().startsWith('opportunity.')) {
                        // 商談の項目を更新、変更があるかどうか判断する
                        if (opp.get(ssApiStr.substring(12)) != opp.get(apiStr)) {
                            try {
                                opp.put(ssApiStr.substring(12), opp.get(apiStr));
                                updSelfFlg = true;
                            } catch (Exception e) {
                                iflog.ErrorLog__c = 'ERROR [' + apiStr + '] => [' + ssApiStr + ']' + e.getMessage() + '\n';
                            }
                        }
                    } else {
                        try {
                            insSSOpp.put(ssApiStr, getValue(opp, apiStr));
                        } catch (Exception e) {
                            iflog.ErrorLog__c = 'ERROR ' + String.valueOf(oppMapping.get('SS_TableName__c')) + ' [' + apiStr + '] => [' + ssApiStr + ']' + e.getMessage() + '\n';
                        }
                    }
                }
            }
            if (updSelfFlg) {
                updSelfList.add(opp);
            }
            // SS_Prospect_5days_list__c  の対象かどうか、loopの中単独判断
            //if (opp.StageName == '引合') {
            if (opp.StageName == '引合' || opp.StageName == '注残' || opp.StageName == '出荷' || opp.StageName == '完了' || opp.StageName == '削除' || opp.StageName == '敗戦') {
                insSSOppList.add(insSSOpp);
                insSSOppId.add(opp.Id);
            }
        }

        // 商談商品
        SS_Batch_Column_Mapping__c oliMapping = SS_Batch_Column_Mapping__c.getValues('OpportunityLineItem');
        // select項目を oliMapping より生成
        Set<String> apiTempSet = new Set<String>();
        String oliSql = makeSql(oliMapping, apiTempSet);
        oliSql += ' where OpportunityId IN :insSSOppId';
        Schema.SObjectType ssOliType = Schema.getGlobalDescribe().get(String.valueOf(oliMapping.get('SS_TableName__c')));
        for (OpportunityLineItem oli : Database.query(oliSql)) {
            SObject insSSOli = ssOliType.newSObject();
            insSSOliList.add(insSSOli);
            for (Integer i = 1; i <= FIELDMAX; i++) {
                String lpadI = ('00' + i).right(3);
                String fromColumn = 'From_Column_' + lpadI + '__c';
                String apiStr = String.valueOf(oliMapping.get(fromColumn));
                if (String.isBlank(apiStr) == false) {
                    String ssColumn = 'SS_Column_' + lpadI + '__c';
                    String ssApiStr = String.valueOf(oliMapping.get(ssColumn));
                    try {
                        insSSOli.put(ssApiStr, getValue(oli, apiStr));
                    } catch (Exception e) {
                        iflog.ErrorLog__c = 'ERROR ' + String.valueOf(oppMapping.get('SS_TableName__c')) + ' [' + apiStr + '] => [' + ssApiStr + ']' + e.getMessage() + '\n';
                    }
                }
            }
        }

        if (Oly_TriggerHandler.isBypassed('PowerBIBaseHandler') == false) {
            Oly_TriggerHandler.bypass('PowerBIBaseHandler');
        }

        // エラーをlogに書き出す
        if (updSelfList.size() > 0) {
System.debug('updSelfList:' + updSelfList);
            // SWAG-CE55BX 预测优化 start
            StaticParameter.EscapeOppandStaTrigger = true;
            // SWAG-CE55BX 预测优化 end
            Database.SaveResult[] lsr = Database.update(updSelfList, false);
            List<Opportunity> reuseUpdateList = new List<Opportunity>();
            for (Integer tIdx = 0; tIdx < lsr.size(); tIdx++) {
                Database.SaveResult sr = lsr[tIdx];
System.debug('sr.isSuccess:' + sr.isSuccess());
                if (!sr.isSuccess()) {
                    //wangweipeng   20210618   获取没有update的数据
                    reuseUpdateList.add(updSelfList[tIdx]);


                    /*Database.Error emsg = sr.getErrors()[0];
                    //update by Gzw 2020-10-30 start
                    if (!String.valueOf(emsg.getStatusCode()).contains('INACTIVE_OWNER_OR_USER')) {
                        iflog.ErrorLog__c += 'ERROR ' + updSelfList[tIdx].Opportunity_No__c + ' Opportunity:' + emsg + '\n';
                    }
                        // iflog.ErrorLog__c += 'ERROR ' + updSelfList[tIdx].Opportunity_No__c + ' Opportunity:' + emsg + '\n';
                    //update by Gzw 2020-10-30 end
                    */
                }
            }

            //wangweipeng  20210618  如果update失败,重复更新，最多3次，如果失败记录日志  start 
            if(reuseUpdateList != null && reuseUpdateList.size() > 0){
                reuseUpdate(reuseUpdateList,1);
            }
            //wangweipeng  20210618  如果update失败,重复更新，最多3次，如果失败记录日志  end 
        }

        //insert insSSOppList;
        Database.SaveResult[] lsr2 = Database.insert(insSSOppList, false);
        for (Integer tIdx = 0; tIdx < lsr2.size(); tIdx++) {
            Database.SaveResult sr = lsr2[tIdx];
            if (!sr.isSuccess()) {
                Database.Error emsg = sr.getErrors()[0];
                //update by rentx 2020-10-27 start
                    if (!String.valueOf(emsg.getStatusCode()).contains('INACTIVE_OWNER_OR_USER')) {
                        iflog.ErrorLog__c += 'ERROR ' + insSSOppList[tIdx].get('Opportunity_No__c') + ' SS_opp:' + emsg + '\n';
                    }
                //update by rentx 2020-10-27 end                 
                // iflog.ErrorLog__c += 'ERROR ' + insSSOppList[tIdx].get('Opportunity_No__c') + ' SS_opp:' + emsg + '\n';
            }
        }
        //insert insSSOliList;
        Database.SaveResult[] lsr3 = Database.insert(insSSOliList, false);
        for (Integer tIdx = 0; tIdx < lsr3.size(); tIdx++) {
            Database.SaveResult sr = lsr3[tIdx];
            if (!sr.isSuccess()) {
                Database.Error emsg = sr.getErrors()[0];
                iflog.ErrorLog__c += 'ERROR  ' + insSSOliList[tIdx].get('Opportunity_number__c') + '  SS_oli:' + emsg + '\n';
            }
        }
    }
    
    public void finish(Database.BatchableContext BC) {

        if (Test.isRunningTest() == false && TEST_ID == null) {
            Database.executeBatch(new SSBackorderBatch(null), 100);
        }

        iflog.Log__c += '\nSSOpportunityBatch 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;
    }

    //wangweipeng  20210618  如果update失败,重复更新，最多3次，如果失败记录日志  start 
    //参数：updSelfList   要重新插入的数据     indexUpdate   第几次插入
    public void reuseUpdate(List<Opportunity> updSelfList,Integer indexUpdate){
        System.debug('这是此方法走的第：'+indexUpdate+' 次了。');
        // SWAG-CE55BX 预测优化 start
        StaticParameter.EscapeOppandStaTrigger = true;
        // SWAG-CE55BX 预测优化 end
        Database.SaveResult[] lsr = Database.update(updSelfList, false);
        List<Opportunity> reuseUpdateList = new List<Opportunity>();
        for (Integer tIdx = 0; tIdx < lsr.size(); tIdx++) {
            Database.SaveResult sr = lsr[tIdx];
            if (!sr.isSuccess()) {
                if(indexUpdate >= 3){
                    Database.Error emsg = sr.getErrors()[0];
                    //update by Gzw 2020-10-30 start
                    if (!String.valueOf(emsg.getStatusCode()).contains('INACTIVE_OWNER_OR_USER')) {
                        iflog.ErrorLog__c += 'ERROR ' + updSelfList[tIdx].Opportunity_No__c + ' Opportunity:' + emsg + '\n';
                    }
                        // iflog.ErrorLog__c += 'ERROR ' + updSelfList[tIdx].Opportunity_No__c + ' Opportunity:' + emsg + '\n';
                    //update by Gzw 2020-10-30 end
                }else{
                    //获取没有update的数据
                    reuseUpdateList.add(updSelfList[tIdx]);
                }
            }
        }
        if(indexUpdate < 3){
            indexUpdate ++;
            if(reuseUpdateList != null && reuseUpdateList.size() > 0){
                reuseUpdate(reuseUpdateList,indexUpdate);
            }
        }
    }
    //wangweipeng  20210618  如果update失败,重复更新，最多3次，如果失败记录日志  end
}