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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.controller.AccountController;
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 AccountService implements IAccountService {

	@Autowired
	private AccountRepo accountRepo;

	@Autowired
	private MongoTemplate mongoTemplate;

	@Autowired
	private RoleInfoService roleInfoService;

	@Autowired
	private RoleMappingService roleMappingService;

	private static final Logger logger = LoggerFactory.getLogger(AccountService.class);

	@Override
	public Account createAccount(Account accountReq) throws MyTimeException {

		accountReq.setAccountId(generateAccountId());
		accountReq.setStatus(MyTimeUtils.STRING_Y);
		Account accountPersisted = accountRepo.save(accountReq);
		if (logger.isInfoEnabled()) {
			logger.info("Account has been persisted in database with account details::" + accountPersisted);
		}
		if (accountPersisted != null) {
			List<String> accountDmsList = accountReq.getDeliveryManagers();
			if (accountDmsList != null && !accountDmsList.isEmpty() && accountDmsList.size() > 0) {
				String roleId = roleInfoService.getRole(MyTimeUtils.ACCOUNT);
				logger.info("Going to add DM role id for account delivery managers::::" + accountDmsList);
				roleMappingService.saveUniqueEmployeeAndRole(accountDmsList, roleId);
				logger.info("Added roleids for delivery managers in rolemapping collection");
			}

		}
		return accountPersisted;
	}

	@Override
	public Account updateAccount(Account accountUpdating) throws MyTimeException {

		Account accountBeforeUpdate = accountRepo.findByAccountId(accountUpdating.getAccountId());
		accountUpdating.setStatus(accountBeforeUpdate.getStatus());
		accountUpdating.setAccountName(accountUpdating.getAccountName().trim());
		
		logger.info("Updating the roleids of DeliveryManagers in RoleMapping Collection");
		final String roleId = roleInfoService.getRole(MyTimeUtils.ACCOUNT);
		updateRoleIdsForDeliveryManager(accountUpdating, roleId);
		
		logger.info("Deleting the roleids of DeliveryManagers in RoleMapping Collection");
		deleteRoleIdsForDeliveryManager(accountUpdating, roleId);
		
		Account accountUpdated = accountRepo.save(accountUpdating);
		logger.info("Account updated::"+accountUpdated);
		
		return accountUpdated;
	}

	@Override
	public boolean isAccountExists(Account account) {

		Account accountFetched = accountRepo.findByAccountNameIgnoreCase(account.getAccountName());
		return (accountFetched != null) ? true : false;

	}

	@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;
	}

	@Override
	public Account deleteAccount(String accountId) throws MyTimeException {

		// delete the documents for deliveryManagers in rolemapping collection.
		logger.info("After updation:: Deleting the Roleids for DeliveryManagers in RoleMapping collection");
		deleteRoleidsForDeliveryManagers(accountId);

		// updating the status to "InActive".
		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);

		Account updatedAccount = mongoTemplate.findAndModify(query, update, options, Account.class);
		logger.info("The account updated::" + updatedAccount);

		return updatedAccount;
	}

	// generating the account id.
	// accountId format is "Acc001"
	private String generateAccountId() throws MyTimeException {
		return (MyTimeUtils.ACC + MyTimeUtils.ZERO_) + (getAccounts().size() + MyTimeUtils.ONE);
	}

	private void updateRoleIdsForDeliveryManager(Account accountReq, String roleId) throws MyTimeException {

		List<String> updatingDmsList = accountReq.getDeliveryManagers();
		List<String> persistedDmsList = accountRepo.findByAccountId(accountReq.getAccountId()).getDeliveryManagers();

		List<String> dmsAddedByUser = CommomUtil.getAddedManagersList(persistedDmsList, updatingDmsList);
		roleMappingService.saveUniqueEmployeeAndRole(dmsAddedByUser, roleId);
	}

	private void deleteRoleIdsForDeliveryManager(Account accountUpdating, String roleId) throws MyTimeException {
		List<String> dmIdList = null;
		Map<String, Integer> dmsCountMap = new HashMap<String, Integer>();

		List<String> updatingDmsList = accountUpdating.getDeliveryManagers();
		List<String> persistedDmsList = accountRepo.findByAccountId(accountUpdating.getAccountId())
				.getDeliveryManagers();
		List<String> dmsListDeletedByUser = CommomUtil.getDeletedManagersList(persistedDmsList, updatingDmsList);

		List<Account> allAccountList = accountRepo.findAll();
		if (allAccountList != null && !allAccountList.isEmpty() && allAccountList.size() > 0) {
			for (Account acc : allAccountList) {
				dmIdList = acc.getDeliveryManagers();
				if (dmIdList != null && !dmIdList.isEmpty() && dmIdList.size() > 0) {
					for (String dmId : dmIdList) {
						if (dmsCountMap.get(dmId) != null)
							dmsCountMap.put(dmId, dmsCountMap.get(dmId) + 1);
						else
							dmsCountMap.put(dmId, 1);
						dmIdList = null;
					}
				}
			}
		}

		for (String empId : dmsListDeletedByUser) {
			if (dmsCountMap.get(empId) == 1) {
				// Service call for RoleMapping
				roleMappingService.deleteRole(empId, roleId);
			}
		}
	}

	// 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;
	}

	private void deleteRoleidsForDeliveryManagers(String accountId) throws MyTimeException {
		int occurrences = 0;
		List<Account> allAccountsList = null;
		List<String> accountDms = null;
		List<String> tempDmsList = new ArrayList<String>();

		String roleId = roleInfoService.getRole(MyTimeUtils.ACCOUNT);
		allAccountsList = accountRepo.findAll();

		List<String> accountDmsList = accountRepo.findByAccountId(accountId).getDeliveryManagers();

		for (Account account : allAccountsList) {
			accountDms = account.getDeliveryManagers();

			for (String accountDm : accountDms)
				tempDmsList.add(accountDm);

			accountDms = null;
		}

		for (String dmId : accountDmsList) {
			occurrences = Collections.frequency(tempDmsList, dmId);
			if (occurrences == 1) {
				// Service call for RoleMapping
				roleMappingService.deleteRole(dmId, roleId);
			}
		}
	}

}
