global class InventoryReportDtlArcRaesdBatch implements Database.Batchable<sObject>, Database.Stateful {
    private Inventory_Header__c ih_new = new Inventory_Header__c();
    private String docBodyRaesd = '';
    private Integer docBodyRaesdBlobSize = 0;
    private Id folderId = System.Label.InventoryFolder;
    private Boolean falg = false;   //保存或更新标识

    private Set<String> defaultAssetField;
    private Set<String> defaultRaesdField;
    private List<String> cListAssetField = new List<String>();
    private List<String> cListRaesdField = new List<String>();
    private Map<String, String> fieldAPIName = new Map<String, String>();

    global List<String> emailMessages;
    global Integer totalCount = 0; // 跨Batch 总件数
    global Integer totalCountDone = 0; // 跨Batch 成功件数
    global Integer executeCount = 0; // batch内的 总件数
    global Integer executeCountDone = 0; // batch内的 成功件数
    global Integer failedCount = 0; // batch内的 失败件数
    global Integer fileNo; // 生成多个文件时，文件名尾部追加序号
    private boolean overSizeFlg = false; // 当execute是否执行flag(false执行，true不执行)

    @TestVisible
    private static List<String> messagesForTest;

    /**
     * 批准后, Trigger 会来调
     */
    global InventoryReportDtlArcRaesdBatch(Inventory_Header__c ih_new ,Integer fileNo, Integer totalCount, Integer totalCountDone) {
        this.emailMessages = new List<String>();
        this.ih_new = ih_new;
        this.fileNo = fileNo;
        this.totalCount = totalCount;
        this.totalCountDone = totalCountDone;
    }

    global Database.QueryLocator start(Database.BatchableContext bc) {
        // bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults();
        if (String.isBlank(folderId)) {
            this.emailMessages.add('未设置CSV保存文件夹，不能开始Batch');
            failedCount = 0;
            return Database.getQueryLocator([SELECT Id FROM Inventory_Detail__c LIMIT 0]);
        }

        Inventory_Batch_Mapping__mdt columns;
        List<String> keyList;

        defaultAssetField = new Set<String>();
        defaultRaesdField = new Set<String>();

        columns = [select From_Columns__c, Inventory_Columns__c from Inventory_Batch_Mapping__mdt where DeveloperName = 'Asset'];
        keyList = columns.Inventory_Columns__c.split(',');
        keyList.addAll(new List<String> {
                'Inventory_Zaiku_Count_Jia__c',
                'Inventory_Shipan_Count_Jia__c',
                'Inventory_Weixiu_Count_Jia__c',
                'Inventory_Chujie_Count_Jia__c',
                'Inventory_Daibaofei_Diushi_Count_Jia__c',
                'Inventory_Quantity_Jia__c',
                'Inventory_Profit_Quantity__c',
                'Inventory_Loss_Quantity__c',
                'Inventory_Deviation_Jia__c',
                'Inventory_Remarks__c'
        });
        for (Integer i = 0; i < keyList.size(); i++) {
            if (!defaultAssetField.contains(keyList[i])) {
                cListAssetField.add(keyList[i]);
            }
            defaultAssetField.add(keyList[i]);
        }

        columns = [select From_Columns__c, Inventory_Columns__c from Inventory_Batch_Mapping__mdt where DeveloperName = 'RAESD'];
        keyList = columns.Inventory_Columns__c.split(',');
        for (Integer i = 0; i < keyList.size(); i++) {
            if (!defaultRaesdField.contains(keyList[i])) {
                cListRaesdField.add(keyList[i]);
            }
            defaultRaesdField.add(keyList[i]);
        }

        //获取label
        String objectType ='Inventory_Detail__c';
        Map<String, Schema.SObjectType> schemaMap = Schema.getGlobalDescribe();
        Schema.SObjectType leadSchema = schemaMap.get(objectType);
        Map<String, Schema.SObjectField> fieldMap = leadSchema.getDescribe().fields.getMap();

        for(String fieldName : fieldMap.keySet()){
            fieldAPIName.put(fieldName, fieldMap.get(fieldName).getDescribe().getLabel());
        }

        Id ih_new_Id = ih_new.Id;
        List<Id> assetIds = new List<Id>();

        String idcQuerysql = 'select ' + String.join(new List<String>(defaultAssetField), ', ')
                + ' from Inventory_Detail__c'
                + ' where Sync_Asset_Record_Flag__c = true'
                + ' and Inventory_Header__c = :ih_new_Id';

        for (Inventory_Detail__c idc : Database.query(idcQuerysql)) {
            assetIds.add(idc.Asset__c);
        }

        String querysql = 'select ' + String.join(new List<String>(defaultRaesdField), ', ') + ', Exported__c'
                + ' from Inventory_Detail__c where Asset__c IN :assetIds and Inventory_Header__c = :ih_new_Id and Asset_Status__c = \'出借中\''
                + ' and Exported__c = false';


        System.debug('InventoryReportDtlArcRaesdBatch Start 2>>>>>');

        return Database.getQueryLocator(querysql);
    }

    global void execute(Database.BatchableContext BC, List<Inventory_Detail__c> raesdList) {
        // flag为true时，跳过execute处理
        if (true == overSizeFlg) {
            //System.abortJob(BC.getJobId());
            return;
        }

        executeCount += raesdList.size();
        Integer DOC_MAX_SIZE = Integer.valueOf(System.Label.DocumentFileSize_KB.replaceAll(',', '')) * 1000;  // 1024 ? 1000 ? 暂时用 1000 了
        Savepoint sp = Database.setSavepoint();
        try {
            List<Inventory_Detail__c> inventoryLst = new List<Inventory_Detail__c>();
            String executeBodyRaesd = '';
            // 借出明细
            for (Integer i = 0; i < raesdList.size(); i++) {
                Inventory_Detail__c dl = raesdList[i];
                String dummyRaesd = setDocBodyObject(dl, 'Raesd', false);
                Integer dummyRaesdSize = Blob.valueOf(dummyRaesd).size();
                // 当前字符串大小未超过 DOC_MAX_SIZE
                if (dummyRaesdSize <= DOC_MAX_SIZE - docBodyRaesdBlobSize) {
                    executeCountDone++;
                    executeBodyRaesd += dummyRaesd;
                    docBodyRaesdBlobSize += dummyRaesdSize;
                    // 处理完毕对象Exported字段置true
                    dl.Exported__c = true;
                    inventoryLst.add(dl);
                }
                // 当前字符串大小超过 DOC_MAX_SIZE
                else {
                    overSizeFlg = true;
                    if (0 == fileNo) {
                        fileNo++;
                    }
                    executeCount = executeCount - raesdList.size();
                    executeCount = executeCount + i;
                    break;
                }
            }
            docBodyRaesd += executeBodyRaesd;
            system.debug('###executeCount : ' + executeCount);
            system.debug('###executeCountDone : ' + executeCountDone);
            // 处理完毕对象update
            update inventoryLst;
        } catch (Exception e) {
            Database.rollback(sp);
            failedCount += raesdList.size();
            System.debug(LoggingLevel.ERROR, e.getMessage() + '\n' + e.getStackTraceString());
            this.emailMessages.add(e.getMessage() + '\n' + e.getStackTraceString());
        }
    }

    private String setDocBodyObject(sObject obj, String typeStr, boolean label) {
        List<String> toCol;
        if (typeStr == 'Raesd') {
            toCol = cListRaesdField;
        }
        Schema.SObjectType targetType = Inventory_Detail__c.sObjectType;
        Schema.DescribeSObjectResult sobjResult = targetType.getDescribe();
        Map<string, Schema.SObjectField> m = sobjResult.fields.getMap();
        String docBody = '';
        for (Integer i = 0; i < toCol.size(); i++) {
            List<String> cols = toCol[i].split('\\.');
            if (obj == null) {
                if (label) {
                    docBody += fieldAPIName.get(toCol[i].toLowerCase()).escapeCsv();
                } else {
                    docBody += toCol[i].escapeCsv();
                }
            }
            else {
                Object val = CreateRelationListPagingCmpCtrl.getObjectValue(obj, cols);
                if (val != null) {
                    Schema.DescribeFieldResult descField = m.get(toCol[i].toLowerCase().replace('__r.name','__c')).getDescribe();
                    if(descField.getType() == DisplayType.DATETIME && String.isNotBlank('' + val)){
                        docBody += (''+((Datetime) val).addHours(8)).escapeCsv();
                    }
                    else{
                        docBody += ('' + val).escapeCsv();
                    }
                }
            }
            // 加 , 和 行末 的换行
            if (i < toCol.size() - 1) {
                docBody += ',';
            } else {
                docBody += '\r\n';
            }
        }
        return docBody;
    }

    // public Document getArchiveFilenameForInsert(String typeStr, Inventory_Header__c ih_new, Integer fileNo) {
    //     bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults();
    //     Id folderId = conf.InventoryDetailArchiveFolder__c;
    //     String fileNoStr = '';
    //     if (fileNo != 0) {
    //         fileNoStr = '_' + String.valueOf(fileNo);
    //     }
    //     String filename = ih_new.Inventory_Start_Date__c.year() + ('' + ih_new.Inventory_Start_Date__c.month()).leftPad(2, '0')
    //             + '_' + ih_new.Internal_asset_location__c + '_' + typeStr + fileNoStr +  '.csv';
    //     Document ret = new Document();
    //     ret.Name = filename;
    //     ret.FolderId = folderId;
    //     ret.Type = 'csv';
    //     insert ret;

    //     String docBody = setDocBodyObject(null, typeStr, false);
    //     docBody += setDocBodyObject(null, typeStr, true);
    //     ret.Body = Blob.valueOf(docBody);

    //     return ret;
    // }
    //20231017  ymh  修改文档存储  start
    public ContentVersion getArchiveFilenameForInsert(String typeStr, Inventory_Header__c ih_new, Integer fileNo) {
        bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults();
        
        String fileNoStr = '';
        if (fileNo != 0) {
            fileNoStr = '_' + String.valueOf(fileNo);
        }
        String filename = ih_new.Inventory_Start_Date__c.year() + ('' + ih_new.Inventory_Start_Date__c.month()).leftPad(2, '0')
                + '_' + ih_new.Internal_asset_location__c + '_' + typeStr + fileNoStr +  '.csv';

        String docBody = setDocBodyObject(null, typeStr, false);
        docBody += setDocBodyObject(null, typeStr, true);

        ContentVersion version = null;
        //20231103 ymh  修改优化sql查询  start
        List<ContentVersion> verlist = [SELECT Id, Title, VersionData,ContentDocumentId,IsMajorVersion FROM ContentVersion WHERE Title =:filename and FirstPublishLocationId =:folderId];
        // List<ContentVersion> verlist = [SELECT Id, Title, VersionData,ContentDocumentId,IsMajorVersion FROM ContentVersion WHERE Title =:filename];
        System.debug('verList是否存在----------->>>>>'+verlist);
        
        if( verlist.size() > 0 ){
            version = verlist[0];
            falg = true;

            // List<ContentDocumentLink> linkList = [SELECT ContentDocumentId FROM ContentDocumentLink WHERE ContentDocumentId =:verlist[0].ContentDocumentId];
            // if( linkList.size() > 0 ){
            //     version = verlist[0];
            //     falg = true;
            // }
        //20231103 ymh  修改优化sql查询  start
        }else{
            version = new ContentVersion();
            version.Title = filename;
            version.VersionData = Blob.valueOf(docBody);
            version.PathOnClient = filename+'.csv';
        }
        return version;
    }
    

     public String insertContentDocumentLink(String versionId){
        ContentVersion version = [select ContentDocumentId from ContentVersion where Id =: versionId];

        String res='';
        System.debug('存入link----------->>>>>'+version.ContentDocumentId);
        try{
            ContentDocumentLink link = new ContentDocumentLink(); 
            link.ContentDocumentId = version.ContentDocumentId;
            link.LinkedEntityId = folderId;
            link.ShareType = 'I';
            link.Visibility = 'AllUsers';
            StaticParameter.ContentDocumentLink = false;
            insert link;
            System.debug('link最后保存-------------'+link);
            res ='1';
        }catch(Exception e){
            res ='0';
            return res;
        }
        return res;
        
    }

     

    global void finish(Database.BatchableContext BC) {
        if (docBodyRaesd == '') {
            this.emailMessages.add('Raesd数据不存在');
        } else {
            if (this.emailMessages.size() == 0 && executeCount == executeCountDone) {
                // Document raesdDoc = getArchiveFilenameForInsert('Raesd', ih_new, fileNo);
                // raesdDoc.Body = Blob.valueOf(raesdDoc.Body.toString() + docBodyRaesd);
                // Database.SaveResult saveResult = Database.update(raesdDoc, false);
                ContentVersion raesdVersion;
                try{
                    raesdVersion = getArchiveFilenameForInsert('Raesd', ih_new, fileNo);
                    raesdVersion.VersionData = Blob.valueOf(raesdVersion.VersionData.toString() + docBodyRaesd);
                }catch(Exception e){
                    System.debug(LoggingLevel.ERROR, e.getMessage() + '\n' + e.getStackTraceString());
                    this.emailMessages.add(e.getMessage() + '\n' + e.getStackTraceString());
                }
                
                Database.SaveResult saveResult;
                String res ;
                if(falg){
                    // csv文件存在  update
                    saveResult = Database.update(raesdVersion, false);
                }else{
                    //csv文件不存在  insert
                    saveResult = Database.insert(raesdVersion, false);
                    res = insertContentDocumentLink(raesdVersion.Id);
                }
                if (!saveResult.isSuccess() || res =='0') {
                    failedCount = executeCountDone;     // Docuemnt 都算成 fail
                    this.emailMessages = FixtureUtil.setSaveError(new List<Database.SaveResult>{saveResult}, ContentVersion.sObjectType, new List<ContentVersion>{raesdVersion}, this.emailMessages);
                }
                //20231017  ymh  修改文档存储  end
            }
            totalCount += executeCount;
            totalCountDone += executeCountDone;
            // flag为true（字符串大小超过9.9M），且未发生错误时再次执行batch
            if (true == overSizeFlg && this.emailMessages.size() == 0 && executeCount == executeCountDone) {
                fileNo++;
                InventoryReportDtlArcRaesdBatch raesdBatch = new InventoryReportDtlArcRaesdBatch(ih_new, fileNo, totalCount, totalCountDone);
                Database.executeBatch(raesdBatch);
            } else {
                // TODO wwf email
                InventoryReportDtlArcRaesdBatch.messagesForTest = this.emailMessages;
                // 发 mail
                BatchEmailUtil be = new BatchEmailUtil();
                String[] toList = new String[]{};
                String title = '存档盘点CSV 盘点明细 #' + fileNo + ' ';
                if (false == overSizeFlg) {
                    title += '最后 ';
                }
                String[] ccList = new String[]{};

                Inventory_Header__c iheader = [select Id, Inventory_Status__c, Name, Internal_asset_location__c, Inventory_Start_Date__c from Inventory_Header__c where Id = :ih_new.Id];
                String text = '盘点报告书编号：' + iheader.Name + '  盘点地点：' + iheader.Internal_asset_location__c + '  盘点开始日：' + iheader.Inventory_Start_Date__c;

                if(this.emailMessages.size() == 0 && totalCount == totalCountDone) {
                    toList.add(UserInfo.getUserEmail());
                    be.successMail(toList, ccList, title, totalCountDone, text);
                } else {
                    for(String email : System.Label.Inventory_Result_Email.split(',')){
                        ccList.add(email);
                    }
                    be.failedMail(toList, ccList, title,
                            String.join(this.emailMessages, '\n'),
                            totalCount, totalCountDone, failedCount, text);
                }
                be.send();
            }
        }
    }
}