package com.nisum.mytime.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import com.nisum.mytime.exception.handler.MyTimeException;
import com.nisum.mytime.model.Account;
import com.nisum.mytime.model.EmployeeRoles;
import com.nisum.mytime.repository.AccountRepo;
import com.nisum.mytime.utils.CommomUtil;
import com.nisum.mytime.utils.MyTimeUtils;

@Service
public class AccountServiceImpl implements AccountService {

	@Autowired
	private MongoTemplate mongoTemplate;

	@Autowired
	private AccountRepo accountRepo;
	
    @Autowired
   	private RoleInfoService roleInfoService;
    
    @Autowired
    private RoleMappingService roleMappingService;

	@Override
	public String addAccount(Account account,String action) throws MyTimeException {
		String response = "";

		Account accountResult= null;
		
		final String roleId = roleInfoService.getRole(MyTimeUtils.ACCOUNT);
				
		if( action != null && action.trim().equals(MyTimeUtils.STRING_N)) {//N means adding New Account

			if(accountRepo.findByAccountNameIgnoreCase(account.getAccountName().trim()) != null) {
				response ="Account already exist";
				return response;
			}
			
			List<String> accDms = account.getDeliveryManagers();
			account.setAccountId(generateAccountId());
			account.setAccountName(account.getAccountName().trim());
			account.setClientAddress(account.getClientAddress().trim());
			account.setStatus(MyTimeUtils.STRING_Y);
			accountResult = accountRepo.save(account);

			if(accDms != null && !accDms.isEmpty() && accDms.size() > 0) {
				roleMappingService.saveUniqueEmployeeAndRole(accDms, roleId);
			}

			if(accountResult.getId() != null) {
				response="Saved succesfully";
			}else {
				response = "Error occured while account creating";
			}
		}else if(action != null && action.trim().equals("U")){// U means updating existing Account
			List<String> employeeIds = null;
			List<String> dmsAddedByUser = null;
	        List<String> dmsDeletedByUser = null;
	        
	        Map<String, Integer> dmsCount = new HashMap<String,Integer>();
	        
	        Account accountBeforeUpdate = accountRepo.findByAccountId(account.getAccountId());
	        
			boolean dupAccChkFlag = duplicateAccountCheck(account,accountBeforeUpdate);
			
			if(dupAccChkFlag) {
				response ="Account already exist";
				return response;
			}

			List<String> updatedAccDms = account.getDeliveryManagers();
			List<String> beforeAccUpdateDms = accountBeforeUpdate.getDeliveryManagers();
			
			dmsAddedByUser = CommomUtil.getAddedManagersList(beforeAccUpdateDms, updatedAccDms);
			dmsDeletedByUser = CommomUtil.getDeletedManagersList(beforeAccUpdateDms, updatedAccDms);
			
			List<Account> accountList = accountRepo.findAll();
			
			if(accountList != null && !accountList.isEmpty() && accountList.size() >0) {
		        for (Account acc : accountList) {
		            employeeIds = acc.getDeliveryManagers();
		            if(employeeIds != null && !employeeIds.isEmpty() && employeeIds.size() >0) {
			             for(String eId:employeeIds) {
			            	  if(dmsCount.get(eId) != null) 
			            		  dmsCount.put(eId, dmsCount.get(eId)+1);
			                  else
			                	  dmsCount.put(eId, 1);
			             	employeeIds = null;
			             }
		            }
		         }
			}
			
	       for (String empId : dmsDeletedByUser) {
	        	if(dmsCount.get(empId)==1) {   		
	                //Service call for RoleMapping 
	                roleMappingService.deleteRole(empId,roleId);
	        	}
	       } 
	       
	       account.setStatus(accountBeforeUpdate.getStatus());
	       account.setAccountName(account.getAccountName().trim());
	       accountResult = accountRepo.save(account);
		   roleMappingService.saveUniqueEmployeeAndRole(dmsAddedByUser, roleId);
		   response="Updated succesfully";
		   return response;
		}
		return response;
	}

	@Override
	public List<Account> getAccounts() throws MyTimeException {
		return accountRepo.findAll();
	}

	@Override
	public List<Map<Object, Object>> getAccountsList() throws MyTimeException {
		List<Map<Object, Object>> updatedAccountList = new ArrayList<>();
		List<Map<String, String>> updatedEmployeeList = null;
		for (Account account : accountRepo.findAll()) {
			updatedEmployeeList = new ArrayList<>();
			for (EmployeeRoles employeesRole : getEmployeeDetails(account)) {
				updatedEmployeeList.add(getEmployeeDetails(employeesRole));
			}
			updatedAccountList.add(getAccuntDetails(account, updatedEmployeeList));
		}
		return updatedAccountList;
	}

	// fetching the employee details using employeeId.
	private List<EmployeeRoles> getEmployeeDetails(Account account) {
		List<EmployeeRoles> employeeRoles = mongoTemplate.find(
				new Query(Criteria.where(MyTimeUtils.EMPLOYEE_ID).in(account.getDeliveryManagers())),
				EmployeeRoles.class);
		return employeeRoles;
	}

	private HashMap<String, String> getEmployeeDetails(EmployeeRoles employeesRole) {
		HashMap<String, String> employeeDetails = new HashMap<>();
		employeeDetails.put(MyTimeUtils.EMPLOYEE_ID, employeesRole.getEmployeeId());
		employeeDetails.put(MyTimeUtils.EMPLOYEE_NAME, employeesRole.getEmployeeName());
		return employeeDetails;
	}

	private Map<Object, Object> getAccuntDetails(Account account, List<Map<String, String>> updatedEmployeeList) {
		Map<Object, Object> accountDetails = new HashMap<>();
		accountDetails.put(MyTimeUtils.ID_, account.getId());
		accountDetails.put(MyTimeUtils.ACCOUNT_ID, account.getAccountId());
		accountDetails.put(MyTimeUtils.ACCOUNT_NAME, account.getAccountName());
		accountDetails.put(MyTimeUtils.STATUS, account.getStatus());
		accountDetails.put(MyTimeUtils.CLIENT_ADDRESS, account.getClientAddress());
		accountDetails.put(MyTimeUtils.INDUSTRY_TYPE, account.getIndustryType());
		accountDetails.put(MyTimeUtils.DELIVERY_MANAGERS, updatedEmployeeList);
		return accountDetails;
	}

	// updating the status to "InActive".
	public Account deleteAccount(String accountId) throws MyTimeException {
		 int occurrences = 0;
		List<Account> accountsList = null;
		List<String> accountDms = null;
		List<String> accountsDms = new ArrayList<String>();
		 
		String roleId = roleInfoService.getRole(MyTimeUtils.ACCOUNT);
		accountsList = accountRepo.findAll();
		
		List<String> deletedAccountDms = accountRepo.findByAccountId(accountId).getDeliveryManagers();
		
        for (Account account : accountsList) {
            accountDms = account.getDeliveryManagers();
            
            for(String accountDm:accountDms)
            	accountsDms.add(accountDm);
            
            accountDms = null;
        }
		 
        
        for (String dmId : deletedAccountDms) {
            occurrences = Collections.frequency(accountsDms, dmId);
            if(occurrences==1) {
                //Service call for RoleMapping 
                roleMappingService.deleteRole(dmId,roleId);
            }
        } 
        
		Query query = new Query(Criteria.where(MyTimeUtils.ACCOUNT_ID).is(accountId));
		Update update = new Update();
		update.set(MyTimeUtils.STATUS, MyTimeUtils.STRING_N);
		FindAndModifyOptions options = new FindAndModifyOptions();
		options.upsert(true);
		
		return mongoTemplate.findAndModify(query, update, options, Account.class);
	}
	
	// generating the account id.
	// accountId format is "Acc001"
	private String generateAccountId() throws MyTimeException {
		return (MyTimeUtils.ACC + MyTimeUtils.ZERO_) + (getAccounts().size() + MyTimeUtils.ONE);
	}
	
    private boolean duplicateAccountCheck(Account UpdatedAccount, Account accountBeforeUpdate  ) throws MyTimeException {
    	boolean accDuplicateflag = false;
    	
    	String updateAccName = UpdatedAccount.getAccountName().trim();
    	
   		if(accountBeforeUpdate.getAccountName().trim().equalsIgnoreCase(updateAccName)) {
   			accDuplicateflag = false;
   		}else {	
   			if(accountRepo.findByAccountNameIgnoreCase(updateAccName) != null) {
   				accDuplicateflag = true;
   			}
   		}
   	  return accDuplicateflag;
    }

}
