class ComplianceServiceImpl implements ComplianceService, Constants {

    @SuppressWarnings("rawtypes")
    public ResponseWithOrder getRulecompliance(Request request) throws ServiceException {
        // Ignoring input as we need to return all.
    	logger.debug("getRulecompliance invoked with {}",request);
        int size = 0;
        int from = 0;
        String assetGroup = request.getAg();
        String searchText = request.getSearchtext();
        Map<String, String> filters = request.getFilter();
        String ruleCategory = "";
        if (null != filters.get(CommonUtils.convertAttributetoKeyword(RULE_CATEGORY))) {
            ruleCategory = filters.get(CommonUtils.convertAttributetoKeyword(RULE_CATEGORY));
        }
        List<LinkedHashMap<String, Object>> openIssuesByRuleList = new ArrayList<>();
        List<LinkedHashMap<String, Object>> openIssuesByRuleListFinal;
        ResponseWithOrder response = null;
        String rule = null;
        String ttypes = "";
        String resourceTypeFilter = null; 
        if(filters.containsKey(Constants.RESOURCE_TYPE) && StringUtils.isNotBlank(filters.get(Constants.RESOURCE_TYPE))) {
            ttypes = "'"+filters.get(Constants.RESOURCE_TYPE).trim()+"'"; 
            resourceTypeFilter = filters.get(Constants.RESOURCE_TYPE).trim();
        }else if(!Strings.isNullOrEmpty(filters.get(CommonUtils.convertAttributetoKeyword(TARGET_TYPE)))) {
            ttypes = "'"+filters.get(CommonUtils.convertAttributetoKeyword(TARGET_TYPE)).trim()+"'"; 
            resourceTypeFilter = filters.get(CommonUtils.convertAttributetoKeyword(TARGET_TYPE)).trim();
        } else {
            ttypes = repository.getTargetTypeForAG(assetGroup, filters.get(DOMAIN));
        }
        logger.debug("Types in scope for invocation {}",ttypes);
        final List <Map<String, String>> dataSourceTargetType = repository.getDataSourceForTargetTypeForAG(assetGroup, filters.get(DOMAIN), resourceTypeFilter);
        String application ;
        if(filters.containsKey(Constants.APPS)) {
        	application = filters.get(Constants.APPS);
        }else {
        	application = null;
        }
                            
        if (!Strings.isNullOrEmpty(ttypes)) {
            try {
                List<Map<String, Object>> rules = new ArrayList<>();
               
                /*--For filters we need to take rule Id's which match the filter condition--*/
                if (!Strings.isNullOrEmpty(filters.get(RULEID_KEYWORD))) {

                    rule = rule + "," + "'" + filters.get(RULEID_KEYWORD) + "'";
                    rules = repository.getRuleIdDetails(rule);
                    if(!rules.isEmpty())
                    	resourceTypeFilter = rules.get(0).get(TARGET_TYPE).toString();
                } else {
                	rules = repository.getRuleIdWithDisplayNameWithRuleCategoryQuery(
                            ttypes, ruleCategory);                 
                }
                
                logger.debug("Rules in scope {}",rules);
            
                if (!rules.isEmpty()) {
                    // Make map of rule severity,category

                    List<Map<String, Object>> ruleSevCatDetails = getRuleSevCatDetails(rules);
                    Map<String, Object> ruleCatDetails = ruleSevCatDetails.parallelStream().collect(
                            Collectors.toMap(c -> c.get(RULEID).toString(), c -> c.get(RULE_CATEGORY), (oldvalue,
                                    newValue) -> newValue));
                    Map<String, Object> ruleSevDetails = ruleSevCatDetails.parallelStream().collect(
                            Collectors.toMap(c -> c.get(RULEID).toString(), c -> c.get(SEVERITY),
                                    (oldvalue, newValue) -> newValue));
                    
                    Map<String, Object> ruleAutoFixDetails = ruleSevCatDetails.parallelStream().collect(
                            Collectors.toMap(c -> c.get(RULEID).toString(), c -> c.get("autofix"), (oldvalue,
                                    newValue) -> newValue));
                    
                    ExecutorService executor = Executors.newCachedThreadPool();
                   
                    
                    Map<String, Long> totalassetCount = new HashMap<>();
                         
                    totalassetCount.putAll(repository.getTotalAssetCount(assetGroup, filters.get(DOMAIN), application,resourceTypeFilter)); // Can't execute in thread as security context is not passed in feign.
					 
                    List<Map<String, Object>> ruleIdwithsScanDate  = new ArrayList<>();
                    executor.execute(()->{
                    	try {
							ruleIdwithsScanDate.addAll(repository.getRulesLastScanDate());
						} catch (DataException e) {
							logger.error("Error fetching rule Last scan date",e);
						}
						
                    });
                    
                    Map<String,Integer> exemptedAssetsCount = new HashMap<>(); 
                   // executor.execute(()->{
                    	 try {
                    		 if(filters.containsKey(Constants.RESOURCE_TYPE)) {// Currently exempted info is only used when resorucetype is passed. Temporary perf fix
                    			 exemptedAssetsCount.putAll(repository.getExemptedAssetsCountByRule(assetGroup,application,filters.get(Constants.RESOURCE_TYPE)));
                    		 }
						} catch (DataException e) {
							logger.error("Error fetching exempted asset count",e);
						}
                         
                         
                   // });
                   
                    Map<String, Object> untagMap =  new HashMap<>(); 
                    
                    List<Map<String, Object>> rulesTemp = rules;
                    String ttypesTemp = ttypes;
                    executor.execute(()->{
                    	
                    	boolean tagginPolicyExists = rulesTemp.stream().filter(ruleObj-> ruleObj.get(RULEID).toString().contains(TAGGIG_POLICY)).findAny().isPresent();
                      
                        if(tagginPolicyExists)
							try {
								untagMap.putAll(repository.getTaggingByAG(assetGroup,ttypesTemp,application));
							} catch (DataException e) {
								logger.error("Error fetching tagging information ",e);
							}                        
                   });
                    final Map<String, Long> openIssuesByRuleByAG = new HashMap<>();
                    executor.execute(()->{
                    	try {
							openIssuesByRuleByAG.putAll(repository.getNonCompliancePolicyByEsWithAssetGroup(
							         assetGroup, null, filters, from, size, ttypesTemp));
						} catch (DataException e) {
							logger.error("Error fetching rule issue aggregations ",e);

						}
                        
                   });
                    
                   executor.shutdown();
                    
                   while(!executor.isTerminated()) {
                	 
                	   
                   }
                                 
                    rules.forEach(ruleIdDetails -> {
                                Map<String, String> ruleIdwithsScanDateMap = new HashMap<>();
                                LinkedHashMap<String, Object> openIssuesByRule = new LinkedHashMap<>();
                                Long assetCount = 0l;
                                Long issuecountPerRuleAG = 0l;
                                double compliancePercentage;
                                double contributionPercentage = 0;
                                String resourceType = null;
                                String ruleId = null;
                             
                                if (!ruleIdwithsScanDate.isEmpty()) {
                                    ruleIdwithsScanDateMap = ruleIdwithsScanDate.stream().collect(
                                            Collectors.toMap(s -> (String) s.get(RULEID),
                                                    s -> (String) s.get(MODIFIED_DATE)));
                                }

                                ruleId = ruleIdDetails.get(RULEID).toString();
                                resourceType = ruleIdDetails.get(TARGET_TYPE).toString();
                                assetCount = (null != totalassetCount.get(resourceType)) ? totalassetCount
                                        .get(resourceType) : 0l;
                                if (null != openIssuesByRuleByAG.get(ruleId)) {
                                    issuecountPerRuleAG = (null != openIssuesByRuleByAG.get(ruleId)) ? openIssuesByRuleByAG
                                            .get(ruleId) : 0l;
                                
                                }
                                if (ruleId.contains(CLOUD_KERNEL_COMPLIANCE_POLICY)|| ruleId.equalsIgnoreCase(ONPREM_KERNEL_COMPLIANCE_RULE)) {
                                  
                                	try {
										assetCount = repository.getPatchabeAssetsCount(assetGroup, resourceType,application,null,null);
										issuecountPerRuleAG = repository.getUnpatchedAssetsCount(assetGroup, resourceType,application);
									} catch (DataException e) {
										logger.error("Error fetching patching info",e);
									}
                                
                                } else if (ruleId.contains(TAGGIG_POLICY)) {
                                	issuecountPerRuleAG = 0l;
                                    if (untagMap.get(resourceType) != null) {
                                        String totaluntaggedStr = untagMap.get(resourceType).toString()
                                                .substring(0, untagMap.get(resourceType).toString().length() - TWO);
                                        issuecountPerRuleAG = Long.parseLong(totaluntaggedStr);
                                    }
                                } else {
                                    if((ruleId.contains(CLOUD_QUALYS_POLICY) && qualysEnabled) || ruleId.equalsIgnoreCase(SSM_AGENT_RULE)){
                                        //qualys coverage require only running instances
                                    	logger.info("qualys coverage require only running instances {}",ruleId);
                                        try {
                                        	if(StringUtils.isNotBlank(filters.get(Constants.APPS))) {
                                        		assetCount = repository.getInstanceCountForQualys(assetGroup,"noncompliancepolicy",filters.get(Constants.APPS), "",resourceType);
                                        	} else {
                                        		assetCount = repository.getInstanceCountForQualys(assetGroup,"noncompliancepolicy","", "",resourceType);
                                        	}
                                            
                                        } catch (DataException e) {
                                            logger.error("Error fetching qualys data",e);
                                        }
                                    }
    
                                }
                                if (issuecountPerRuleAG > assetCount) {
                                    issuecountPerRuleAG = assetCount;
                                }
                                Long passed = assetCount - issuecountPerRuleAG;
                                compliancePercentage = Math
                                        .floor(((assetCount - issuecountPerRuleAG) * HUNDRED) / assetCount);
                                if(assetCount==0){
                                	compliancePercentage = 100;
                                	issuecountPerRuleAG = 0l;
                                	passed = 0l;
                                	contributionPercentage = 0.0;
                                }
                                openIssuesByRule.put(SEVERITY, ruleSevDetails.get(ruleId));
                                openIssuesByRule.put(NAME, ruleIdDetails.get(DISPLAY_NAME).toString());
                                openIssuesByRule.put(COMPLIANCE_PERCENT, compliancePercentage);
                                String lastScanDate = repository.getScanDate(ruleId, ruleIdwithsScanDateMap);
                                if(lastScanDate!=null){
                                	openIssuesByRule.put(LAST_SCAN, lastScanDate);
                                }else{
                                	openIssuesByRule.put(LAST_SCAN, "");	
                                }
								final String resourceTypeFinal = resourceType;
								openIssuesByRule.put(RULE_CATEGORY, ruleCatDetails.get(ruleId));
								openIssuesByRule.put(RESOURCE_TYPE, resourceType);
								openIssuesByRule.put(PROVIDER, dataSourceTargetType.stream()
										.filter(datasourceObj -> datasourceObj.get(TYPE).equals(resourceTypeFinal))
										.findFirst().get().get(PROVIDER));
								openIssuesByRule.put(RULEID, ruleId);
								openIssuesByRule.put(ASSETS_SCANNED, assetCount);
								openIssuesByRule.put(PASSED, passed);
								openIssuesByRule.put(FAILED, issuecountPerRuleAG);
								openIssuesByRule.put("contribution_percent", contributionPercentage);
								openIssuesByRule.put("autoFixEnabled", ruleAutoFixDetails.get(ruleId));
                                if(exemptedAssetsCount.containsKey(ruleId)) {
                                	openIssuesByRule.put("exempted", exemptedAssetsCount.get(ruleId));
                                	openIssuesByRule.put("isAssetsExempted", exemptedAssetsCount.get(ruleId).intValue()>0?true:false);
                	        	} else {
                	        		openIssuesByRule.put("exempted", 0);
                	        		openIssuesByRule.put("isAssetsExempted", false);
                	        	}
                                
                                if (!Strings.isNullOrEmpty(searchText)) {
									for (Map.Entry<String, Object> issueByRule : openIssuesByRule.entrySet()) {
										if (null != issueByRule.getValue() && issueByRule.getValue().toString().toLowerCase()
												.contains(searchText.toLowerCase())) {
											openIssuesByRuleList.add(openIssuesByRule);
											break;
										}

									}
                                } else {
                                    openIssuesByRuleList.add(openIssuesByRule);

                                }

                            });
                }
                openIssuesByRuleListFinal = openIssuesByRuleList;
                // sorting by #Violation in desencing order

                Collections.sort(openIssuesByRuleListFinal, Collections.reverseOrder(new Compare()));
                if (openIssuesByRuleList.isEmpty()) {
                    throw new DataException(NO_DATA_FOUND);
                } else {
                    response = new ResponseWithOrder(openIssuesByRuleListFinal, openIssuesByRuleListFinal.size());
                }
            } catch (DataException e) {
            	logger.error("Error @ getRulecompliance while getting the data from ES", e);
                throw new ServiceException(e);
            }
        }
        return response;
    }

}