package com.bankaudit.rest.process.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.jboss.logging.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.bankaudit.dto.process.AuditObservationComplianceDto;
import com.bankaudit.dto.process.AuditRatingParameterScoreDto;
import com.bankaudit.process.model.IfcRatingData;
import com.bankaudit.rest.process.dao.AuditObservationDao;
import com.bankaudit.rest.process.dao.AuditRatingParameterScoreDao;
import com.bankaudit.rest.process.dao.RatingProcessingDao;

@Service("ratingProcessingService")
@Transactional("transactionManager")
public class RatingProcessingServiceImpl implements RatingProcessingService{
	
	static final Logger logger = Logger.getLogger(RatingProcessingServiceImpl.class);
	
	@Autowired(required=true)
	RatingProcessingDao ratingProcessingDao;
	
	@Autowired
	AuditRatingParameterScoreDao auditRatingParameterScoreDao;
	
	@Autowired
	AuditObservationDao auditObservationDao;

	@Override
	public Boolean executeParameterRating(Integer legalEntityCode, String auditId, String auditTypeCode, String unitId, String userId){
		boolean execute= true;
		logger.info("Inside executeParameterRating .. "+ auditId +" :: "+auditTypeCode+" :: "+unitId+" :: "+userId);
		try {			
			Map<String, String> inpMap = new HashMap<String, String>();
			List<IfcRatingData> ifcLst=ratingProcessingDao.getIfcRatingData(legalEntityCode, auditId);
			for(IfcRatingData ifc: ifcLst) {
				inpMap.put(ifc.getInputBaas(),ifc.getInputValue());
			}logger.info("inpMap .. "+inpMap);
			
			//To check if data present in Audit Parameter table
			boolean isDataPresentInParamScore =auditRatingParameterScoreDao.verifyDataPresentForParamScore(legalEntityCode, auditId);
			logger.info("isDataPresentInParamScore .. "+ isDataPresentInParamScore);
			
			//To get data from Maint Parameter table
			List<AuditRatingParameterScoreDto> scoreDtos=auditRatingParameterScoreDao.getRatingParamDataFromMaint(legalEntityCode, auditId, unitId, auditTypeCode, userId);
			logger.info("scoreDtos .. "+scoreDtos);
			
			// To check any Parameter mapped to COMPLIANCE calculation and then retrieve all Process Code along with Total Compliance and Non Compliance mark
			Map<String, AuditObservationComplianceDto> compMap = null;
			List<AuditRatingParameterScoreDto> scoreDtosQNComp =(List<AuditRatingParameterScoreDto>) scoreDtos.stream().
					filter(p -> p.getParamMesaurementType().equals("QN") && p.getParamBasis().equals("COMP")).collect(Collectors.toList());
			logger.info("scoreDtosQN COMP .. "+scoreDtosQNComp.size());
			if(!scoreDtosQNComp.isEmpty()) 
				compMap=auditObservationDao.getCompNonCompCountProcesswise(legalEntityCode, auditTypeCode, auditId);
			
			
			if(isDataPresentInParamScore) { // Updating the Score for QN with CALC & COMP records
				
				/****************Calculating Parameter with QN + CALC  **********************/
				List<AuditRatingParameterScoreDto> scoreDtosQN =(List<AuditRatingParameterScoreDto>) scoreDtos.stream().
						filter(p -> p.getParamMesaurementType().equals("QN") && p.getParamBasis().equals("CALC")).collect(Collectors.toList());
				logger.info("scoreDtosQN CALC .. "+scoreDtosQN.size());
				for(AuditRatingParameterScoreDto scoreDto: scoreDtosQN ) {
					//if("QN".equalsIgnoreCase(scoreDto.getParamMesaurementType()) && "CALC".equalsIgnoreCase(scoreDto.getParamBasis())) {
					logger.info("=============================="+scoreDto.getParamCode()+"===========================================");
					try {
						String processedScore=processedValue( scoreDto, inpMap);
						scoreDto.setProcessedScore(processedScore);
						String continuousDescreteScore=null;
						if("CNT".equalsIgnoreCase(scoreDto.getParamCalcType()))
							continuousDescreteScore=auditRatingParameterScoreDao.getContinuousScoreForParamCode(scoreDto);
						else continuousDescreteScore=auditRatingParameterScoreDao.getDescreteScoreForParamCode(scoreDto);
						logger.info("concreteDescreteScore .."+continuousDescreteScore);
						scoreDto.setScore(continuousDescreteScore);		//}				
						auditRatingParameterScoreDao.updateAuditParameterScore(scoreDto);
					}catch(Exception e) {
						e.printStackTrace();
					}
					logger.info("================END HERE, QN CALC => "+scoreDto.getParamCode()+"===========================");
				}
				/****** QN + CALC, end here *******/
				
				/**********************  Calculating Parameter with QN + COMPLIANCE ************************/								
				for(AuditRatingParameterScoreDto scoreDto: scoreDtosQNComp ) {
					logger.info("=============================="+scoreDto.getParamCode()+"===========================================");
					try {
						double compScore=0.0;
						if(compMap.containsKey(scoreDto.getInput01())) {
							AuditObservationComplianceDto cDto= (AuditObservationComplianceDto)compMap.get(scoreDto.getInput01());
							compScore= (Double.parseDouble(cDto.getCompliance())/ (Double.parseDouble(cDto.getCompliance()) + Double.parseDouble(cDto.getNonCompliance())))*100;
						}
						logger.info("compliance Score .."+ compScore);
						scoreDto.setScore(String.valueOf((double)Math.round(compScore * 100/100.00)));			
						auditRatingParameterScoreDao.updateAuditParameterScore(scoreDto);
					}catch(Exception e) {
						e.printStackTrace();
					}
					logger.info("================END HERE, QN COMP => "+scoreDto.getParamCode()+"===========================================");
				}/****** QN + COMP, end here *******/
				
			}else { // Insertion of data to AuditRatingParameterScore
				for(AuditRatingParameterScoreDto scoreDto: scoreDtos) {
					if("QN".equalsIgnoreCase(scoreDto.getParamMesaurementType())) {
						try{
							if("CALC".equalsIgnoreCase(scoreDto.getParamBasis())) {
								String processedScore=processedValue( scoreDto, inpMap);
								scoreDto.setProcessedScore(processedScore);
								String continuousDescreteScore=null;
								if("CNT".equalsIgnoreCase(scoreDto.getParamCalcType()))
									continuousDescreteScore=auditRatingParameterScoreDao.getContinuousScoreForParamCode(scoreDto);
								else continuousDescreteScore=auditRatingParameterScoreDao.getDescreteScoreForParamCode(scoreDto);
								logger.info("concreteDescreteScore .."+continuousDescreteScore);
								scoreDto.setScore(continuousDescreteScore);	
							}else if("COMP".equalsIgnoreCase(scoreDto.getParamBasis())) {
								double compScore=0.0;
								if(compMap.containsKey(scoreDto.getInput01())) {
									AuditObservationComplianceDto cDto= (AuditObservationComplianceDto)compMap.get(scoreDto.getInput01());
									compScore= (Double.parseDouble(cDto.getCompliance())/ (Double.parseDouble(cDto.getCompliance()) + Double.parseDouble(cDto.getNonCompliance())))*100;
								}
								logger.info("compliance Score .."+ compScore);
								scoreDto.setScore(String.valueOf((double)Math.round(compScore * 100/100.00)));			
							}
						}catch(Exception e) {
							e.printStackTrace(System.out);
						}
					}auditRatingParameterScoreDao.createAuditParameterScore(scoreDto);
				}			
			}			
			//execute=ratingProcessingService.executeParameterRating(legalEntityCode, auditId, auditTypeCode);
			
		}catch(Exception e) {
			execute=false;
			e.printStackTrace(System.out);
		}		
		return execute;		
	}
	
	public String processedValue(AuditRatingParameterScoreDto scDto, Map<String, String> inpMap){
		logger.info("Inside processedValue .. "+scDto.getParamCode()  +" :: "+scDto.getFormula());
		logger.info(" Inputs :: "+scDto.getInput01()  +" :: "+scDto.getInput02()+" :: "+scDto.getInput03()+" :: "+scDto.getInput04());
		String processedScore=null;
		double ip1=0.0, ip2=0.0, ip3=0.0, ip4=0.0, ip5=0.0, ip6=0.0, cal=0.0;
		try {
			if("Process_0".equalsIgnoreCase(scDto.getFormula())) {// as is
				processedScore= inpMap.get(scDto.getInput01());
			}else if("Process_01".equalsIgnoreCase(scDto.getFormula())) { 	// Round(a,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				processedScore = String.valueOf((Math.round(ip1*100)/100.00));
			}else if("Process_02".equalsIgnoreCase(scDto.getFormula())) {	// Round((a-b),2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = ip1- ip2;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_03".equalsIgnoreCase(scDto.getFormula())) {	// Round((b-a),2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = ip2- ip1;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_04".equalsIgnoreCase(scDto.getFormula())) {	// Round(a*100/b,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = ip1 *100 / ip2;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_05".equalsIgnoreCase(scDto.getFormula())) {	// Round(b*100/a,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = ip2 *100 / ip1;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_06".equalsIgnoreCase(scDto.getFormula())) {	// Round(100*(a-b)/a,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = 100 * (ip1-ip2) / ip1;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_07".equalsIgnoreCase(scDto.getFormula())) {	// Round(100*(a-b)/b,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = 100 * (ip1-ip2) / ip2;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_08".equalsIgnoreCase(scDto.getFormula())) {	// Round(100*(b-a)/b,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = 100 * (ip2-ip1) / ip2;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_09".equalsIgnoreCase(scDto.getFormula())) {	// Round(100*(b-a)/a,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = 100 * (ip2-ip1) / ip1;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_10".equalsIgnoreCase(scDto.getFormula())) {	// Round(100*Mod(a-b)/a,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = 100 * Math.abs(ip1-ip2) / ip1;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_11".equalsIgnoreCase(scDto.getFormula())) {	// Round(100*Mod(a-b)/b,2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = 100 * Math.abs(ip1-ip2) / ip2;
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_12".equalsIgnoreCase(scDto.getFormula())) {	// Round(Mod(a-b),2)
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= Double.parseDouble(inpMap.get(scDto.getInput02()));
				cal = Math.abs(ip1-ip2);
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}else if("Process_15".equalsIgnoreCase(scDto.getFormula())) {	// Round( Mod(100-a),2)  // for Industry benchmark
				ip1= Double.parseDouble(inpMap.get(scDto.getInput01()));
				ip2= 100.00;
				cal = Math.abs(ip2-ip1);
				processedScore = String.valueOf((Math.round(cal*100)/100.00));
			}
			else processedScore =null;
		
		}catch(Exception e) {
			e.printStackTrace(System.out);
		}
		return processedScore;
	}
}
