public without sharing class AssetHandler extends Oly_TriggerHandler {

    public static Map<String , List<Rental_Apply_Equipment_Set_Detail__c>> queueRaesdMap;
    private Map<Id, Asset> newMap;
    private Map<Id, Asset> oldMap;
    private List<Asset> newList;
    private List<Asset> oldList;
    @TestVisible private static Set<String> testTargetDepts;
    @TestVisible private static Id Account_Asset_Id;
     public static Boolean disabled = false;

    public AssetHandler() {
        this.newMap = (Map<Id, Asset>) Trigger.newMap;
        this.oldMap = (Map<Id, Asset>) Trigger.oldMap;
        this.newList = (List<Asset>) Trigger.new;
        this.oldList = (List<Asset>) Trigger.old;
    }

    protected override void beforeInsert() {
        beforeSetValue();
        //动态更新 Asset的 AccountId的 Logic
        beforeSetAccountId();

        //第一次不加变更的时候不知道第一次是多少
        updateChangeQuantityReason();
    }
    protected override void beforeUpdate() {
        if(!disabled){
            beforeSetValue();
            //动态更新 Asset的 AccountId的 Logic
            beforeSetAccountId();

            updateChangeQuantityReason();

            // 虚拟维修保修合同保修开始结束日期更新
            updateMaintenance_Contract_Asset();

            updateRentalCount();
        }
        
    }

    protected override void afterInsert() {
        //有新的Asset的时候 暂定分配
        queueRedistribution();
    }

    protected override void afterUpdate() {
        if(!disabled){
            mainAssetUpdateed_OneToOneRecord();
            // 已经改进 Must_Input_ChangeQuantityReason 的 valida before可以完成 ChangeQuantityReason__c = ''
            ////入力規則によってbeforeクリアできません,
            cleanChangeQuantityReason();
            // 清理字段「待废弃数（丢失/盘亏）修改理由」
            cleanGiveupReturnReason();
            //当主体参照的Asset的Last断开的时候重新寻找暂定分配
            queueRedistribution();
            // before では数式項目がnullの場合があります
            formulaToTextCheck();
            // check一对一link表
            checkOneToOneLink();
            // 虚拟维修保修合同保修开始结束日期更新
            updateMaintenance_Contract_Asset();
        }
    }
    // 借出明细更新借出_分配_数(Sys)和已借出数(Sys)
    private void updateRentalCount() {
        Set<Id> idSet = new Set<Id>();
        for(Id assId : this.newMap.keySet()) {
            if(this.newMap.get(assId).AssetManageConfirm__c &&
                this.newMap.get(assId).Account_Management_Code__c != System.Label.Account_Asset_FJZ ) {
                idSet.add(assId);
                this.newMap.get(assId).Out_of_wh_Sys__c = 0;
                this.newMap.get(assId).Rental_Count_Sys__c = 0;
            }
        }
        if(!idSet.isEmpty()) {
            for (Rental_Apply_Equipment_Set_Detail__c raesd : [
                SELECT Id, Shippment_loaner_time2__c, Asset__c
                FROM Rental_Apply_Equipment_Set_Detail__c
                WHERE Detail_Finish__c = false
                AND Asset__c IN :idSet
            ]) {
                this.newMap.get(raesd.Asset__c).Out_of_wh_Sys__c += 1;
                if(raesd.Shippment_loaner_time2__c != null) {
                    this.newMap.get(raesd.Asset__c).Rental_Count_Sys__c += 1;
                }
                idSet.remove(raesd.Asset__c);
            }
            if(!idSet.isEmpty()) {
                // 没有字段可以完全准确地区分备品设备与耗材设备，只好把备品里没出现过的设备当成耗材来查
                for (Consum_Apply_Equipment_Set_Detail__c caesd : [
                 SELECT Id, Shippment_loaner_time2__c, Asset__c
                  FROM Consum_Apply_Equipment_Set_Detail__c
                    WHERE Detail_Finish__c = false
                    AND Asset__c IN :idSet
                ]) {
                    this.newMap.get(caesd.Asset__c).Out_of_wh_Sys__c += 1;
                    if(caesd.Shippment_loaner_time2__c != null) {
                        this.newMap.get(caesd.Asset__c).Rental_Count_Sys__c += 1;
                    }
                }
            }

        }
    }
    //多年保修 start
    private void updateMaintenance_Contract_Asset(){
        StaticParameter.EscapeMaintenanceContractAfterUpdateTrigger = true;
        Set<id> AssetIDSet = new Set<id>();
        for(Asset tempAsset : newList){
            if(String.isNotBlank(tempAsset.warrantyType__c)){
                AssetIDSet.add(tempAsset.id);
            }
        }
        if(AssetIDSet.size() > 0 ){
            list<Maintenance_Contract_Asset__c> MCAList =
            [select id,startDateGurantee__c,
             startDateGurantee_Text__c,endDateGurantee__c,
             endDateGurantee_Text__c,Return_Flag__c,ChangeAcc_flag__c,
             Maintenance_Price_Amount__c,Maintenance_Price_Amount_text__c,
             GuaranteePrice_Amount__c,GuaranteePrice_Amount_text__c
             from Maintenance_Contract_Asset__c
             where Asset__c in : AssetIDSet
             and Maintenance_Contract__r.RecordType.DeveloperName = 'VM_Contract'
             ];
            if(MCAList.size() > 0){
                list<Maintenance_Contract_Asset__c> UpdateMcaList =
                new list<Maintenance_Contract_Asset__c> ();
                for(Maintenance_Contract_Asset__c tempMCA : MCAList ){
                    if(tempMCA.startDateGurantee__c != tempMCA.startDateGurantee_Text__c
                    || tempMCA.endDateGurantee__c != tempMCA.endDateGurantee_Text__c
                    || tempMCA.Maintenance_Price_Amount__c != tempMCA.Maintenance_Price_Amount_Text__c
                    || tempMCA.GuaranteePrice_Amount__c != tempMCA.GuaranteePrice_Amount_text__c){
                        UpdateMcaList.add(tempMCA);
                    }
                }
                Oly_TriggerHandler.bypass('SetContractEstimatePriceHandler');
                update UpdateMcaList;
                Oly_TriggerHandler.clearBypass('SetContractEstimatePriceHandler');
            }
        }
    }
    //多年保修 end

    private void beforeSetValue() {
        for (Asset nObj : newList) {
            // 有线下数据, 所以不需要下面的logic
            //if (nObj.Quantity == null) {
            //    nObj.Quantity = 1;
            //}
            if (Trigger.isInsert) {
                nObj.Last_Reserve_RAES_Detail__c = null;
                nObj.Last_Reserve_TAES_Detail__c = null;
                nObj.Pre_Reserve_RAES_Detail__c  = null;
                nObj.Pre_Reserve_TAES_Detail__c  = null;
                nObj.Fixture_OneToOne_Link__c    = null;
            }

            if (Trigger.isUpdate) {
                Asset oObj = oldMap.get(nObj.Id);
                if (oObj.Last_Reserve_RAES_Detail__c != null && nObj.Last_Reserve_RAES_Detail__c == null
                        && oObj.Last_Reserve_RAES_Detail_Flag__c == true) {
                    nObj.Pre_Reserve_RAES_Detail__c = oObj.Last_Reserve_RAES_Detail__c;
                }
                if (oObj.Last_Reserve_TAES_Detail__c != null && nObj.Last_Reserve_TAES_Detail__c == null
                        // TODO 由于__r超20，字段建不出来
                        // && oObj.Last_Reserve_TAES_Detail_Flag__c == true
                ) {
                    nObj.Pre_Reserve_TAES_Detail__c = oObj.Last_Reserve_TAES_Detail__c;
                }
            }

            // 必ず最後で置く
            nObj.Fixture_Status2_text__c = nObj.Fixture_Status2__c;
            nObj.Fixture_Status3_text__c = nObj.Fixture_Status3__c;
            nObj.BTreeIndexKey__c = nObj.Asset_Owner__c
                                    + ':' + nObj.Asset_loaner_category__c
                                    + ':' + String.valueOf(nobj.Freeze_sign_Abandoned_Flag__c)
                                    + ':' + String.valueOf(nObj.Delete_Flag__c)
                                    + ':' + String.valueOf(nObj.AssetManageConfirm__c);

            if (nObj.Status == FixtureUtil.assetStatusMap.get(FixtureUtil.AssetStatus.Dai_Fei_Qi.ordinal())) {
                nObj.Freeze_sign__c = false;
            }
        }
    }

    // かならず before
    private void updateChangeQuantityReason() {
        for (Asset nObj : newList) {
            Asset oObj;
            String oQuantity = '';
            String oAbandoned_Inventory = '';
            if (Trigger.isUpdate) {
                oObj = oldMap.get(nObj.Id);
                oQuantity = oObj.Quantity == null ? '' : String.valueOf(oObj.Quantity);
                oAbandoned_Inventory = oObj.Abandoned_Inventory__c == null ? '' : String.valueOf(oObj.Abandoned_Inventory__c);
            }
            String nQuantity = nObj.Quantity == null ? '' : String.valueOf(nObj.Quantity);
            String nAbandoned_Inventory = nObj.Abandoned_Inventory__c == null ? '' : String.valueOf(nObj.Abandoned_Inventory__c);
            if ((oQuantity != nQuantity && String.isNotBlank(nObj.ChangeQuantityReason__c)) || Trigger.isInsert) {
                String rs = nObj.ChangeQuantityReason__c;
                nObj.ChangeQuantityHistory__c = String.format(System.Label.ChangeQuantityHistory,
                         new String[]{oQuantity, nQuantity, rs});
                // before里面入立规则过不了改到
                // 已经改进 Must_Input_ChangeQuantityReason 的 valida before可以完成 ChangeQuantityReason__c = ''
                // nObj.ChangeQuantityReason__c = '';
            }

            if (oAbandoned_Inventory != nAbandoned_Inventory || Trigger.isInsert) {
                String rs = nObj.Giveup_Return__c;
                nObj.ChangeAbandoned_InventoryHistory__c = String.format(System.Label.ChangeAbandoned_InventoryHistory,
                         new String[]{oAbandoned_Inventory, nAbandoned_Inventory, rs});
            }
        }
    }

    //// かならず afterUpdate
    private void cleanChangeQuantityReason() {
       // TODO 更新自己table 原则需要Static的Class变量(因为会有复数的Method去更新自己的)
       List<Asset> assList = new List<Asset>();
       for (Asset nObj : newList) {
           if (String.isBlank(nObj.ChangeQuantityReason__c) == false) {
               assList.add(new Asset(Id = nObj.Id,
                       ChangeQuantityReason__c = ''));
           }
       }
       if (!assList.isEmpty()) {
           update assList;
       }
    }

    // afterUpdate
    // 清理字段「待废弃数（丢失/盘亏）修改理由」
    private void cleanGiveupReturnReason() {
        List<Asset> assList = new List<Asset>();
        for (Asset nObj : newList) {
            if (String.isBlank(nObj.Giveup_Return__c) == false) {
                assList.add(new Asset(
                        Id = nObj.Id,
                        Giveup_Return__c = ''));
            }
        }
        if (!assList.isEmpty()) {
            update assList;
        }
    }

    // かならず after, oObj.Id と nObj.Id を使うため
    // Last_Reserve_RAES_Detail__c (个体管理) のlink 変わった場合、
    // Fixture_OneToOne_Link__c より Asset に積み上げしている数字も連動になる、Fixture_OneToOne_Link__c を空更新
    // 更新項目Fixture_OneToOne_Link__c.In_wh_Fu_Shu_Pin_You_Xiao_Ku_Cun__c = Fixture_OneToOne_Link__c.In_wh_Fu_Shu_Pin_You_Xiao_Ku_Cun_F__c
    private void mainAssetUpdateed_OneToOneRecord() {
        Set<Id> aSetIdSet = new Set<Id>();
        for (Integer i = 0; i < newList.size(); i++) {
            Asset nObj = newList[i];
            Asset oObj = oldList[i];
            if (oObj.Last_Reserve_RAES_Detail__c != nObj.Last_Reserve_RAES_Detail__c) {
                if (String.isNotBlank(oObj.Id)) {
                    aSetIdSet.add(oObj.Id);
                }
                if (String.isNotBlank(nObj.Id)) {
                    aSetIdSet.add(nObj.Id);
                }
            }
        }
        if (!aSetIdSet.isEmpty()) {
            List<Fixture_OneToOne_Link__c> oneToOneList = [
                    SELECT Id FROM Fixture_OneToOne_Link__c WHERE Main_Asset__c IN :aSetIdSet];
            if (!oneToOneList.isEmpty()) { update oneToOneList; }
        }
    }

    // * 不会有暂定分配后再拿去修理的备品走重新设置暂定分配的逻辑(queueRedistribution方法)
    // * 理由：明细暂定分配的时候创建修理的话会报以下错误
    // * 借出_分配_数 不能大于 设备的总数 ------> Asset 入力规则 You_Xiao_Ku_Cun_chk
    // * 暂定分配-1
    // * 修理-1
    // * 数量1
    // * 1-1-1 = -1
    // *
    // * 如果暂定分配想修理的话分配暂定分配的那一条明细
    // * 然后再出出库前检测的时候选择NG修理
    // かならず after
    private void queueRedistribution() {
        if (queueRaesdMap == null) {
            queueRaesdMap = new Map<String , List<Rental_Apply_Equipment_Set_Detail__c>>();
        }
        Map<String, Set<Id>> modelNoAssetIdMap = new Map<String, Set<Id>>();
        Set<Id> raesdIdSet = new Set<Id>();
        // List<Rental_Apply_Equipment_Set_Detail__c> updList = new List<Rental_Apply_Equipment_Set_Detail__c>();
        String whkey = '';
        for (Asset nObj : newList) {
            //ToDo現在排队できるのは主体だけなので，今後个体管理の附属品も排队できるの場合があったらこのIFぶんを直す必要があります
            if (!(nObj.Loaner_accsessary__c == false
                    && nObj.Asset_Owner__c == 'Olympus'
                    && nObj.Equipment_Type__c != '检测用备品'
                    && nObj.Delete_Flag__c == False
                    && nObj.You_Xiao_Ku_Cun__c > 0
                    && nObj.Freeze_sign_Abandoned_Flag__c == False)
            ) {
                continue;
            }

            String key = nObj.Fixture_Model_No_F__c;
            //暂定分配不看存放地 OLY_OCM-722 恢复存放地的条件
            if (String.isNotBlank(nObj.Internal_asset_location__c)) {
                key += nObj.Internal_asset_location__c;
            }

            if (String.isNotBlank(nObj.Salesdepartment__c)) {
                key += nObj.Salesdepartment__c;
            }
            if (String.isNotBlank(nObj.Equipment_Type__c)) {
                key += nObj.Equipment_Type__c;
            }
            if (String.isNotBlank(nObj.Product_category__c)) {
                key += nObj.Product_category__c;
            }

            
            System.debug(LoggingLevel.INFO, '*** key: ' + key);
            //有新的Asset的时候 暂定分配
            // You_Xiao_Ku_Cun__c > 0 在上面已经做了判断
            if (Trigger.isInsert && String.isBlank(nObj.Last_Reserve_RAES_Detail__c)) {
                if (String.isBlank(nObj.Fixture_Model_No_F__c) == false) {
                    // modelNoAssetIdMap.put(nObj.Fixture_Model_No_F__c, nObj.Id);
                    if (modelNoAssetIdMap.containsKey(key) == false) {
                        modelNoAssetIdMap.put(key, new Set<Id>());
                    }
                    modelNoAssetIdMap.get(key).add(nObj.Id);
                }
            }
            //当主体参照的Asset的Last断开的时候重新寻找暂定分配
            //ToDo状況確認
            /*
            || (nObj.Asset_Owner__c != oObj.Asset_Owner__c && nObj.Asset_Owner__c == 'Olympus')
            || (nObj.Equipment_Type__c != oObj.Equipment_Type__c && oObj.Equipment_Type__c == '检测用备品')
            || (nObj.Delete_Flag__c != oObj.Delete_Flag__c && nObj.Delete_Flag__c == false)
            || (nObj.Freeze_sign__c != oObj.Freeze_sign__c && nObj.Delete_Flag__c == false)
            */
            // You_Xiao_Ku_Cun__c > 0 在上面已经做了判断
            else {
                Asset oObj = oldMap.get(nObj.Id);
                if (nObj.Loaner_accsessary__c == false && String.isBlank(nObj.Last_Reserve_RAES_Detail__c)
                        && (nObj.Last_Reserve_RAES_Detail__c != oObj.Last_Reserve_RAES_Detail__c
                            || (nObj.Asset_Owner__c != oObj.Asset_Owner__c && nObj.Asset_Owner__c == 'Olympus')
                            //备品存放地 OLY_OCM-722 恢复存放地的条件
                            || (nObj.Internal_asset_location__c != oObj.Internal_asset_location__c)
                            //备品分类并且不是 检测用备品
                            || (nObj.Equipment_Type__c != oObj.Equipment_Type__c && nObj.Equipment_Type__c != '检测用备品')
                            //销售本部
                            || (nObj.Salesdepartment__c != oObj.Salesdepartment__c)
                            //产品分类(GI/SP)
                            || (nObj.Product_category__c != oObj.Product_category__c)
                            //备品配套明细型号
                            || (nObj.Fixture_Model_No_F__c != oObj.Fixture_Model_No_F__c)
                            || (nObj.Delete_Flag__c != oObj.Delete_Flag__c && nObj.Delete_Flag__c == true)
                            || (nObj.Freeze_sign_Abandoned_Flag__c != oObj.Freeze_sign_Abandoned_Flag__c && nObj.Freeze_sign_Abandoned_Flag__c == false)
                        )
                ) {
                    if (String.isBlank(nObj.Fixture_Model_No_F__c) == false) {
                        if (modelNoAssetIdMap.containsKey(key) == false) {
                            modelNoAssetIdMap.put(key, new Set<Id>());
                        }
                        modelNoAssetIdMap.get(key).add(nObj.Id);
                    }
                    if (String.isNotBlank(oObj.Last_Reserve_RAES_Detail__c)) {
                        raesdIdSet.add(oObj.Last_Reserve_RAES_Detail__c);
                    }
                }
            }
        }
        if (modelNoAssetIdMap.isEmpty()) {
            return;
        }
        System.debug(LoggingLevel.INFO, '*** raesdIdSet: ' + JSON.serialize(raesdIdSet));
        //modified by denny 2021-11-17
        List<String> keyList = new List<String>();
        keyList.addAll(modelNoAssetIdMap.keySet());
        System.debug(LoggingLevel.INFO, '*** keyList: ' + keyList);
       List<Rental_Apply_Sequence__c> targetSequence = [SELECT Id,Apply_Set_Detail__c,  
                                                                Apply_Set_Detail_ExternalKey__c, 
                                                                Demo_Purpose2__c, Equipment_Type__c, ExternalKey__c, 
                                                                Fixture_Model_No__c,
                                                                Internal_asset_location__c, Invalid_Flag__c, 
                                                                Product_category__c, 
                                                                Rental_Apply__c,Series_No__c, 
                                                                Series_Unequal_Queue_Flag__c, Salesdepartment__c  
                                                        FROM Rental_Apply_Sequence__c 
                                                        WHERE Series_No__c > 0 AND Invalid_Flag__c =false
                                                        AND Apply_Set_Detail__c NOT IN:raesdIdSet
                                                        AND ExternalKey__c IN:keyList
                                                        ORDER BY Series_No__c ];
        System.debug(LoggingLevel.INFO, '*** targetSequence: ' + targetSequence);
        Map<String,List<Rental_Apply_Sequence__c>> sequenceMap = new Map<String,List<Rental_Apply_Sequence__c>>();
        for(Rental_Apply_Sequence__c sequeuece:targetSequence){
            if(!sequenceMap.containsKey(sequeuece.Externalkey__c)){
                sequenceMap.put(sequeuece.Externalkey__c,new List<Rental_Apply_Sequence__c>());
            }
            sequenceMap.get(sequeuece.Externalkey__c).add(sequeuece);
        }
        Map<String,Rental_Apply_Equipment_Set_Detail__c> updateMap = new Map<String,Rental_Apply_Equipment_Set_Detail__c>();
        for(String key:sequenceMap.keySet()){
            for(Rental_Apply_Sequence__c sequeuece:sequenceMap.get(key)){
                if(!updateMap.containsKey(sequeuece.Apply_Set_Detail__c)){
                    System.debug(LoggingLevel.INFO, '*** modelNoAssetIdMap: ' + JSON.serialize(modelNoAssetIdMap));
                    if(modelNoAssetIdMap.containsKey(key)){
                        List<Id> assIdList = new List<Id>();
                        assIdList.addAll(modelNoAssetIdMap.get(key));
                        String assIdbk = assIdList.get(0);
                        Rental_Apply_Equipment_Set_Detail__c raesd = new Rental_Apply_Equipment_Set_Detail__c();
                        raesd.Id = sequeuece.Apply_Set_Detail__c;
                        raesd.Asset__c = assIdbk;
                        System.debug('raesd.Asset__c' + raesd.Asset__c);
                        raesd.Queue_Number__c = 0;      // 暂定分配
                        updateMap.put(raesd.Id,raesd);


                        modelNoAssetIdMap.get(key).remove(assIdbk);
                        if (modelNoAssetIdMap.get(key).isEmpty()) {
                            modelNoAssetIdMap.remove(key);
                        }
                        System.debug(LoggingLevel.INFO, '*** modelNoAssetIdMap: ' + JSON.serialize(modelNoAssetIdMap));
                    }
                }
                
            }
            
        }
        System.debug(LoggingLevel.INFO, '*** updateMap.values(): ' + JSON.serialize(updateMap.values()));
        update updateMap.values();

            // Fixture_Model_No_F__c を見ることができない、データがおおいので。
            // Fixture_Set_Detail__r.Fixture_Model_No_F__c をみるか Fixture_Model_No_text__c をみるか
        //     whkey += ' (';
        //     whkey += '   ((Fixture_Set_Detail__c != null and Fixture_Set_Detail__r.Product2__r.Fixture_Model_No_T__c =\'' + String.escapeSingleQuotes(nObj.Fixture_Model_No_F__c) + '\')';
        //     whkey += '      OR Fixture_Model_No_text__c =\'' + String.escapeSingleQuotes(nObj.Fixture_Model_No_F__c) + '\'';
        //     whkey += '   )';

        //     String wher = '';
        //     //暂定分配不看存放地 OLY_OCM-722 恢复存放地的条件
        //     if (String.isNotBlank(nObj.Internal_asset_location__c)) {
        //         wher += ' and Internal_asset_location_before__c =\'' + String.escapeSingleQuotes(nObj.Internal_asset_location__c) + '\'';
        //     }

        //     if (String.isNotBlank(nObj.Salesdepartment__c)) {
        //         wher += ' and Salesdepartment_before__c =\'' + String.escapeSingleQuotes(nObj.Salesdepartment__c) + '\'';
        //     }

        //     if (String.isNotBlank(nObj.Product_category__c)) {
        //         wher += ' and Product_category_text__c =\'' + String.escapeSingleQuotes(nObj.Product_category__c) + '\'';
        //     }

        //     if (String.isNotBlank(nObj.Equipment_Type__c)) {
        //         wher += ' and Equipment_Type_text__c =\'' + String.escapeSingleQuotes(nObj.Equipment_Type__c) + '\'';
        //     }

        //     whkey += wher + ' ) OR';
        // }

        

        // if (String.isNotBlank(whkey)) {
        //     whkey = '( ' + whkey.removeEnd('OR') + ')';
        // }



        // String soql = 'SELECT Fixture_Model_No_F__c, Substitute_flag__c, Asset__c,'
        //             + ' Queue_Number__c, Fixture_Model_No_text__c, Salesdepartment_before__c,'
        //             + ' Product_category_text__c, Equipment_Type_text__c, Internal_asset_location_before__c'
        //             + ' FROM Rental_Apply_Equipment_Set_Detail__c'
        //             + ' where Id != :raesdIdSet and '
        //             +  whkey
        //             + ' and Queue_Number__c > 0'
        //             + ' and Cancel_Select__c = false'
        //             + ' order by Queue_Number__c ASC';
        // System.debug('whkey' + whkey);
        // System.debug('soql' + soql);
        // List<Rental_Apply_Equipment_Set_Detail__c> targetRaesd = Database.query(soql);
        // System.debug('targetRaesd' + targetRaesd);
        // for (Rental_Apply_Equipment_Set_Detail__c raesd : targetRaesd) {
        //     // TODO Fixture_Set_Detail__c に チェック追加 Rental_Apply_Equipment_Set_Detail__c 新数据, 有 没有分配(分配时间) 的话, 不能 Delete Fixture_Set_Detail__c
        //     String key = '';
        //     if (raesd.Substitute_flag__c) {
        //         key = raesd.Fixture_Model_No_text__c;
        //     } else {
        //         key = raesd.Fixture_Model_No_F__c;
        //     }
        //     //暂定分配不看存放地 OLY_OCM-722 恢复存放地的条件
        //     if (String.isNotBlank(raesd.Internal_asset_location_before__c)) {
        //         key += '_' + raesd.Internal_asset_location_before__c;
        //     }

        //     if (String.isNotBlank(raesd.Salesdepartment_before__c)) {
        //         key += '_' + raesd.Salesdepartment_before__c;
        //     }

        //     if (String.isNotBlank(raesd.Product_category_text__c)) {
        //         key += '_' + raesd.Product_category_text__c;
        //     }

        //     if (String.isNotBlank(raesd.Equipment_Type_text__c)) {
        //         key += '_' + raesd.Equipment_Type_text__c;
        //     }
        //     if (queueRaesdMap.containsKey(key) == false) {
        //         queueRaesdMap.put(key, new List<Rental_Apply_Equipment_Set_Detail__c>());
        //     }
        //     queueRaesdMap.get(key).add(raesd);
        // }

        // System.debug('queueRaesdMap' + queueRaesdMap);
        // System.debug('modelNoAssetIdMap' + modelNoAssetIdMap);

        // List<Rental_Apply_Equipment_Set_Detail__c> uplist = new List<Rental_Apply_Equipment_Set_Detail__c>();
        // for (String raesdkey : queueRaesdMap.keySet()) {
        //     for (Rental_Apply_Equipment_Set_Detail__c raesd : queueRaesdMap.get(raesdkey)) {
        //         System.debug(raesdkey);
        //         // 这里的queueRaesdMap在用完后没有清空，导致与最新查出的targetRaesd不一致
        //         // 担心直接清空会造成其它问题，所以加or来保证明细的Asset__c只会被更新一次
        //         if (modelNoAssetIdMap.containsKey(raesdkey) == false || String.isNotBlank(raesd.Asset__c)) {
        //             // 基本発生しない、queueRaesdMap <= modelNoAssetIdMap
        //             continue;
        //         }
        //         Id assIdbk;
        //         for (Id assId : modelNoAssetIdMap.get(raesdkey)) {
        //             assIdbk = assId;
        //             raesd.Asset__c = assId;
        //             System.debug('raesd.Asset__c' + raesd.Asset__c);
        //             raesd.Queue_Number__c = 0;      // 暂定分配
        //             uplist.add(raesd);
        //             System.debug('raesd Idis' + raesd.Id);
        //             break;
        //         }
        //         modelNoAssetIdMap.get(raesdkey).remove(assIdbk);
        //         if (modelNoAssetIdMap.get(raesdkey).isEmpty()) {
        //             modelNoAssetIdMap.remove(raesdkey);
        //         }
        //     }
        // }

        // //排队更新
        // if (!uplist.isEmpty()) {
        //     update uplist;
        // }
    }

    //before 数式の値がnullになる可能性がありますのでここでも一回チェックします
    private void formulaToTextCheck() {
        List<Asset> assets = new List<Asset>();
        for (Asset nObj : newList) {
            if (nObj.Fixture_Status2_text__c != nObj.Fixture_Status2__c) {
                Asset ass = new Asset(Id = nObj.Id);
                ass.Fixture_Status2_text__c = nObj.Fixture_Status2__c;
                assets.add(ass);
            }
        }
        if (!assets.isEmpty()) {
            update assets;
        }
    }

    private void checkOneToOneLink() {
        Set<Id> otoIds = new Set<Id>(); // 待删除link的Id
        Set<Id> assetIds = new Set<Id>();
        Set<String> centers = new Set<String> {'北京 备品中心', '上海 备品中心', '广州 备品中心'};
        for (Asset nObj : newList) {
            //待废弃，废弃，和数量0的备品需要断开一对一link （目前只处理个体管理的）
            if ((nObj.Fixture_Status__c == FixtureUtil.assetFixtureStatusMap.get(FixtureUtil.AssetFixtureStatus.Fei_Qi.ordinal())
                || nObj.Fixture_Status__c == FixtureUtil.assetFixtureStatusMap.get(FixtureUtil.AssetFixtureStatus.Dai_Fei_Qi.ordinal())
                || nObj.Quantity == 0) && nObj.Manage_type__c == FixtureUtil.managetypeMap.get(FixtureUtil.Managetype.Ge_Ti_Guan_Li)){
                //是一对一主体或者是一对一个体附属品
                if (nObj.Main_OneToOne__c == true || nObj.Fixture_OneToOne_Link__c <> null) {
                    if(centers.contains(nObj.Internal_asset_location__c)) {
                        assetIds.add(nObj.Id);
                    }
                }
            }
        }

        if (assetIds.size() > 0) {
            Map<Id, Fixture_OneToOne_Link__c> linkMap = new Map<Id, Fixture_OneToOne_Link__c>([
                SELECT Id
                  FROM Fixture_OneToOne_Link__c
                 WHERE Main_Asset__c in :assetIds
                    OR Accessory_Asset__c IN: assetIds
            ]);
            if(!linkMap.isEmpty()){
                delete linkMap.values();
            }
        }
    }

    /**
     * 动态设定 Asset.AccountId (备品共享_杭州_华东营业本部)
     * Condition:
     *    - before insert, before update
     *    - 确认字段变化 （保有设备的备品存放地（Internal_asset_location__c）+所在地区(本部)（Salesdepartment__c））
     *    - Salesdepartment__c IN ('1.华北营业本部','2.东北营业本部','3.西北营业本部','4.华东营业本部','5.华南营业本部','6.西南营业本部')
     *    - Asset 是 办事处备品
     * Execute:
     *    - 找 Label: OCM_Management_Province_Mapping, 的 办事处, 设定 AccountId
     *    - 没有找的话, 不更新AccountId, 自动做 Account, Group, ApexShare
     */
    private void beforeSetAccountId() {
        Set<String> targetDepts = new Set<String> {'1.华北营业本部','2.东北营业本部','3.西北营业本部','4.华东营业本部','5.华南营业本部','6.西南营业本部'};
        Set<String> targetCenters = new Set<String> {'北京 备品中心', '上海 备品中心', '广州 备品中心'};
        if (Test.isRunningTest() && testTargetDepts != null) {
            targetDepts = testTargetDepts;
        }
        // 存放地 → 省 的 Map
        Map<String, String> targetProvs = AssetWebService.getOcmMgtProvMap();

        // 收集对象 Asset
        Map<Asset, String> assetAccShareNameMap = new Map<Asset, String>();
        Datetime execNow = System.now();
        for (Asset nObj : newList) {
            Asset oObj = (null == this.oldMap) ? null : this.oldMap.get(nObj.Id);

            // 作成, 确认字段有没有变化
            if (Trigger.isInsert
                || (oObj != null
                    && (oObj.RecordTypeId != nObj.RecordTypeId
                        || oObj.Asset_loaner_category__c != nObj.Asset_loaner_category__c
                        || oObj.AssetManageConfirm__c != nObj.AssetManageConfirm__c
                        || oObj.Internal_asset_location__c != nObj.Internal_asset_location__c
                        || oObj.Salesdepartment__c != nObj.Salesdepartment__c
                        || oObj.OlympusAccShareName__c != nObj.OlympusAccShareName__c
                    )
                    && (targetCenters.contains(nObj.Internal_asset_location__c)
                        || targetProvs.containsKey(nObj.Internal_asset_location__c)
                    )
                    && !nObj.TransferToOther__c
                )
            ) {
                // 是不是备品的确认
                if (System.Label.Asset_RecordType == nObj.RecordTypeId
                    && true == nObj.AssetManageConfirm__c
                    && nObj.Asset_loaner_category__c != '耗材'
                ) {
                    // 办事处备品 → 动态设定 Asset.AccountId 的対象
                    if (targetProvs.containsKey(nObj.Internal_asset_location__c)
                        && targetDepts.contains(nObj.Salesdepartment__c)
                    ) {
                        String accShareName = '备品共享_'
                                + targetProvs.get(nObj.Internal_asset_location__c) + '_' + nObj.Salesdepartment__c.right(6);
                        nObj.OlympusAccShareName__c = accShareName;
                        assetAccShareNameMap.put(nObj, accShareName);
                    }
                    else {
                        if (AssetHandler.Account_Asset_Id == null) {
                            // Static 只会执行一遍
                            AssetHandler.Account_Asset_Id = [SELECT Id FROM Account WHERE AgentCode_Ext__c =: System.Label.Account_Asset].Id;
                        }
                        nObj.OlympusAccShareName__c = '';
                        nObj.AccountId = AssetHandler.Account_Asset_Id;
                    }
                } else {
                    // 更新成 不是备品 的时候, 更新的程序里 要 care AccountId
                    nObj.OlympusAccShareName__c = '';
                }
            }
        }

        // check 是不是 accShareName 都有
        Set<String> insertAccShareNameSet = new Set<String>();
        if (assetAccShareNameMap.size() > 0) {
            Map<String, Id> accShareId = new Map<String, Id>();
            for (Account accShare : [SELECT Id
                        , Department_Name__c
                     FROM Account
                    WHERE RecordTypeId =: System.Label.Department_OTH
                      AND Department_Name__c IN: assetAccShareNameMap.values()]) {
                accShareId.put(accShare.Department_Name__c, accShare.Id);
            }

            // assetAccShareNameMap 的 Key会有变化
            // 这个 for loop后 就不能使用 assetAccShareNameMap.get(XXXX) 了
            for (Asset nObj : assetAccShareNameMap.keySet()) {
                String accShareName = assetAccShareNameMap.get(nObj);
                if (accShareId.containsKey(accShareName)) {
                    // 找到 Account 设定 AccountId, "Olympus社内 其他 备品" => "Olympus社内 其他 备品共享_XXX"
                    nObj.AccountId = accShareId.get(accShareName);
                }
                else {
                    // 找不到 AccountId, 新建 Account, Group, ApexShare
                    insertAccShareNameSet.add(accShareName);
                }
            }
        }
        if (insertAccShareNameSet.size() > 0) {
            // AssetHandler.futureInsertAccShare(JSON.serialize(insertAccShareNameSet), execNow); 
            System.enqueueJob(new MyQueueableClass(JSON.serialize(insertAccShareNameSet), execNow));
        }
    }

    /**
     * 新建 备品共享_XXX 客户数据
     * @param insertAccShareNameJson Json String of List<String> ["备品共享_XXX", "备品共享_YYY"]
     */
    private static void insertAccShare(String insertAccShareNameJson, Datetime execNow) {
        Set<String> insertAccShareNameSet = (Set<String>) JSON.deserialize(insertAccShareNameJson, Set<String>.class);
        if (insertAccShareNameSet.isEmpty()) { return; }

        //  Olympus社内 其他
        List<Account> olympusAccount_OtherDC;
        try {
            olympusAccount_OtherDC = [SELECT Id, ParentId
                     FROM Account
                    WHERE Parent.AgentCode_Ext__c = '9999999' AND Department_Class_Name__c = '其他'
                      FOR Update];
        } catch (Exception e) {
            System.enqueueJob(new MyQueueableClass(insertAccShareNameJson, execNow));
            return;
        }

        List<Account> isInsertedList = [SELECT Id, Department_Name__c
                 FROM Account
                WHERE ParentId =: olympusAccount_OtherDC[0].Id
                  AND Department_Name__c IN: insertAccShareNameSet];
        if (isInsertedList.size() > 0) {
            Map<String, Account> insertedAccount = new Map<String, Account>();
            for (Account insertedAcc : isInsertedList) {
                insertedAccount.put(insertedAcc.Department_Name__c, insertedAcc);
                insertAccShareNameSet.remove(insertedAcc.Department_Name__c);
            }
            // Update Asset.AccountId
            AssetHandler.updateAssetAccount(insertedAccount, execNow);
        }


        if (insertAccShareNameSet.size() > 0) {
            Map<String, Account> insertAccount = new Map<String, Account>();
            for (String accShareName : insertAccShareNameSet) {
                insertAccount.put(accShareName, new Account(
                    Name = '*',
                    Hospital__c = olympusAccount_OtherDC[0].ParentId,
                    ParentId = olympusAccount_OtherDC[0].Id,
                    Department_Class__c = olympusAccount_OtherDC[0].Id,
                    Other_dept_category__c = '其他科室',
                    Department_Name__c = accShareName,
                    OwnerId = System.Label.Batch_User_Id,
                    RecordTypeId = System.Label.Department_OTH
                ));
            }
            AssetHandler.insertAccGrantShare(insertAccount, execNow);
        }
    }

    private static void insertAccGrantShare(Map<String, Account> insertAccountMap, Datetime execNow) {
        if (insertAccountMap.isEmpty()) { return; }
        Set<String> accShareNameSet = insertAccountMap.keySet();
        // insert Account的时候, 跳过 NFM001
        // OLY_OCM-1230 需要执行NFM001Trigger
        // ControllerUtil.EscapeNFM001Trigger = true;
        insert insertAccountMap.values();

        Map<String, Group> insertGroup = new Map<String, Group>();
        Map<String, Group> groupNameMap = new Map<String, Group>();
        for (String accShareName : accShareNameSet) {
            insertGroup.put(accShareName, new Group(
                Name = accShareName
            ));
        }

        // Check 有没有 Group
        for (Group grp : [SELECT Id, Name FROM Group WHERE Type = 'Regular' AND Name IN: accShareNameSet]) {
            // 已经有Group 的话 不用 insert
            insertGroup.remove(grp.Name);
            groupNameMap.put(grp.Name, grp);
        }
        if (insertGroup.size() > 0) {
            insert insertGroup.values();
            // groupNameMap 含 所有 accShareNameSet 的 Group, 并且 都有 Id
            groupNameMap.putAll(insertGroup);
        }

        // 赋 Account 共享权限
        List<AccountShare> insertShareList = new List<AccountShare>();
        for (String accShareName : accShareNameSet) {
            insertShareList.add(new AccountShare(
                    UserOrGroupId = groupNameMap.get(accShareName).Id,
                    AccountId = insertAccountMap.get(accShareName).Id,
                    AccountAccessLevel = 'Edit',
                    OpportunityAccessLevel = 'None'
            ));
        }
        insert insertShareList;
        // Update Asset.AccountId
        AssetHandler.updateAssetAccount(insertAccountMap, execNow);
    }

    private static void updateAssetAccount(Map<String, Account> accShareNameAccountMap, Datetime execNow) {
        if (accShareNameAccountMap.isEmpty()) { return; }
        Set<String> accShareNameSet = accShareNameAccountMap.keySet();

        List<Asset> updAssetAccount = new List<Asset>();
        for (Asset nObj : [SELECT Id, OlympusAccShareName__c, AccountId
                 FROM Asset
                WHERE OlympusAccShareSameFlag__c = '0'
                  AND OlympusAccShareName__c IN: accShareNameSet
                  AND LastModifiedDate >=: execNow]) {
            nObj.AccountId = accShareNameAccountMap.get(nObj.OlympusAccShareName__c).Id;
            updAssetAccount.add(nObj);
        }
        update updAssetAccount;
    }

    @TestVisible
    class MyQueueableClass implements Queueable {
        String insertAccShareNameJson;
        Datetime execNow;

        public MyQueueableClass(String insertAccShareNameJson, Datetime execNow) {
            this.insertAccShareNameJson = insertAccShareNameJson;
            this.execNow = execNow;
        }

        public void execute(QueueableContext context) {
            AssetHandler.insertAccShare(insertAccShareNameJson, execNow);
        }
    }
}