package com.nisum.mytime.service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang.time.DateUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
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 org.springframework.util.CollectionUtils;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.nisum.mytime.controller.DomainController;
import com.nisum.mytime.exception.handler.MyTimeException;
import com.nisum.mytime.model.Account;
import com.nisum.mytime.model.BillingDetails;
import com.nisum.mytime.model.Domains;
import com.nisum.mytime.model.EmpLoginData;
import com.nisum.mytime.model.EmpShiftDetails;
import com.nisum.mytime.model.EmployeeDashboardVO;
import com.nisum.mytime.model.EmployeeRoles;
import com.nisum.mytime.model.Project;
import com.nisum.mytime.model.ProjectTeamMate;
import com.nisum.mytime.repository.AccountRepo;
import com.nisum.mytime.repository.DomainRepo;
import com.nisum.mytime.repository.EmpShiftDetailsRepo;
import com.nisum.mytime.repository.EmployeeRolesRepo;
import com.nisum.mytime.repository.ProjectRepo;
import com.nisum.mytime.repository.ProjectTeamMatesRepo;
import com.nisum.mytime.repository.TeamMatesBillingRepo;
import com.nisum.mytime.utils.MyTimeUtils;
import com.nisum.mytime.utils.PdfReportGenerator;

@Service("projectService")
public class ProjectServiceImpl implements ProjectService {

    @Autowired
    private EmployeeRolesRepo employeeRolesRepo;

    @Autowired
    private ProjectRepo projectRepo;
    @Autowired
    private ProjectTeamMatesRepo projectTeamMatesRepo;
    @Autowired
    private TeamMatesBillingRepo teamMatesBillingRepo;

    @Autowired
    private EmployeeDataService employeeDataBaseService;

    @Autowired
    private PdfReportGenerator pdfReportGenerator;

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private EmpShiftDetailsRepo empShiftDetailsRepo;

    @Autowired
    private DomainController domainController;

    @Autowired
    private RoleInfoService roleInfoService;

    @Autowired
    private RoleMappingService roleMappingService;

    @Autowired
    private AccountRepo accountRepo;

    @Autowired
    private DomainRepo domainRepo;
    
	@Autowired
	private ProjectService projectService;

    @Override
    public List<EmpLoginData> employeeLoginsBasedOnDate(long id,
            String fromDate, String toDate) throws MyTimeException {
        return employeeDataBaseService.fetchEmployeeLoginsBasedOnDates(id,
                fromDate, toDate);
    }

    @Override
    public String generatePdfReport(long id, String fromDate, String toDate)
            throws MyTimeException {
        return pdfReportGenerator.generateEmployeeReport(id, fromDate, toDate);
    }

    @Override
    public List<HashMap<Object, Object>> getProjects() throws MyTimeException {
        /*
         * MT-79:returns all project info. This will get implemented once
         * managers are maintained as List and not attributes return
         * projectRepo.findAll();
         */
        List<HashMap<Object, Object>> projectList = new ArrayList<>();
        List<HashMap<String, String>> EmployeeList = null;
        List<HashMap<String, String>> managerList = null;
        List<Project> projects = projectRepo.findAll();
        for (Project p : projects) {
            HashMap<Object, Object> projectMap = new HashMap<>();
            projectMap.put("id", p.getId());
            projectMap.put("projectId", p.getProjectId());
            projectMap.put("projectName", p.getProjectName());
            Account account = accountRepo.findByAccountId(p.getAccountId());
            Domains domain = domainRepo.findByDomainId(p.getDomainId());
            if (domain != null)
                projectMap.put("domain", domain.getDomainName());
            if (account != null)
                projectMap.put("account", account.getAccountName());
            projectMap.put("accountId", p.getAccountId());
            projectMap.put("domainId", p.getDomainId());
            projectMap.put("status", p.getStatus());
            projectMap.put("employeeIds", p.getEmployeeIds());
            projectMap.put("projectStartDate", p.getProjectStartDate());
            projectMap.put("projectEndDate", p.getProjectEndDate());
            // projectMap.put("deliveryLeadIds", p.getDeliveryLeadIds());

            /*
             * if (p.getManagerIds() == null && p.getManagerId() != null) {
             * EmployeeList = getEmployeeData(Arrays.asList(p.getManagerId()));
             * } else
             */ if (p.getDeliveryLeadIds() != null) {

                List<String> deliverLeadsId = p.getDeliveryLeadIds();
                EmployeeList = getEmployeeData(deliverLeadsId);
                projectMap.put("deliveryLeadIds", EmployeeList);
            }
            if (p.getManagerIds() != null) {

                List<String> managerid = p.getManagerIds();
                managerList = getEmployeeData(managerid);
                projectMap.put("managerIds", managerList);
            }
            projectList.add(projectMap);
        }
        return projectList;
    }

    @Override
    public Project addProject(Project project) throws  MyTimeException {
        if (project.getDomainId() == null) {
            Domains domain = new Domains();
            domain.setAccountId(project.getAccountId());
            domain.setDomainName(project.getDomain());
            domain.setDeliveryManagers(project.getManagerIds());
            domain.setStatus(project.getStatus());
            domainController.addDomain(domain);
            List<Domains> domainsList = domainRepo
                    .findByAccountId(project.getAccountId());
            Domains savedDomain = domainsList.get(0);
            project.setDomainId(savedDomain.getDomainId());
            project.setDomain(savedDomain.getDomainName());
        }
    
        return projectRepo.save(project);
    }

    @Override
    public EmployeeRoles getEmployeesRole(String emailId) {
        return employeeRolesRepo.findByEmailId(emailId);

    }

    @Override
    public void deleteProject(String projectId) {
        Project project = projectRepo.findByProjectId(projectId);
        projectRepo.delete(project);
        Query query = new Query(Criteria.where("projectId").is(projectId));
        List<ProjectTeamMate> list = mongoTemplate.find(query,
                ProjectTeamMate.class);
        projectTeamMatesRepo.delete(list);
    }

    @Override
    public Project updateProject(Project project) throws  MyTimeException {   	
    	//Inactivate the Project based on endDate
		Project existingProject = projectRepo.findByProjectId(project.getProjectId());
		List<ProjectTeamMate> existingTeammates = projectTeamMatesRepo.findByProjectId(project.getProjectId());

		if (project.getProjectEndDate().compareTo(new Date()) <= 0
				&& project.getProjectEndDate().compareTo(existingProject.getProjectEndDate()) != 0) {

			existingProject.setStatus(MyTimeUtils.IN_ACTIVE);
			existingProject.setProjectEndDate(project.getProjectEndDate());
			projectRepo.save(existingProject);
			for (ProjectTeamMate existingTeammate : existingTeammates) {
				existingTeammate.setActive(false);
				existingTeammate.setEndDate(project.getProjectEndDate());

				BillingDetails billingDetails = new BillingDetails();
				billingDetails.setBillableStatus(MyTimeUtils.BENCH_BILLABILITY_STATUS);
				List<BillingDetails> listBD = getEmployeeActiveBillingDetails(existingTeammate.getEmployeeId(),
						existingTeammate.getProjectId());
				if (listBD != null && !listBD.isEmpty()) {
					BillingDetails billingDetailsExisting = listBD.get(0);
					billingDetailsExisting.setBillingEndDate(project.getProjectEndDate());
					billingDetailsExisting.setActive(false);
					updateEmployeeBilling(billingDetailsExisting);
				}
				Date sd = project.getProjectEndDate();
				billingDetails.setBillingStartDate(sd);
				billingDetails.setAccount(MyTimeUtils.BENCH_ACCOUNT);
				billingDetails.setActive(true);
				billingDetails.setEmployeeId(existingTeammate.getEmployeeId());
				billingDetails.setEmployeeName(existingTeammate.getEmployeeName());
				billingDetails.setCreateDate(new Date());
				billingDetails.setProjectId(MyTimeUtils.BENCH_PROJECT_ID);
				billingDetails.setProjectName(MyTimeUtils.FREE_POLL);
				addEmployeeBillingDetails(billingDetails);

				projectTeamMatesRepo.save(existingTeammate);
				ProjectTeamMate newBenchAllocation = new ProjectTeamMate();
				Project benchProject = projectRepo.findByProjectId(MyTimeUtils.BENCH_PROJECT_ID);
				if (benchProject != null)
					newBenchAllocation.setAccountId(benchProject.getAccountId());
				newBenchAllocation.setBillableStatus(MyTimeUtils.BENCH_BILLABILITY_STATUS);
				newBenchAllocation.setDesignation(existingTeammate.getDesignation());
				newBenchAllocation.setEmailId(existingTeammate.getEmailId());
				newBenchAllocation.setEmployeeId(existingTeammate.getEmployeeId());
				newBenchAllocation.setActive(true);
				newBenchAllocation.setEmployeeName(existingTeammate.getEmployeeName());
				newBenchAllocation.setProjectId(MyTimeUtils.BENCH_PROJECT_ID);
				newBenchAllocation.setStartDate(sd);
				newBenchAllocation.setProjectName(benchProject.getProjectName());
				projectTeamMatesRepo.save(newBenchAllocation);
				 updateShiftDetails(existingTeammate);
			}
			return existingProject;

		}
		else
	   {
        Query query = new Query(
                Criteria.where("projectId").is(project.getProjectId()));
        Update update = new Update();
        update.set("projectName", project.getProjectName());
        if (project.getDeliveryLeadIds() != null) {
            update.set("deliveryLeadIds", project.getDeliveryLeadIds());
        }
        /*
         * if(project.getManagerIds()!= null) { update.set("managerIds",
         * project.getManagerIds()); }
         *//*
            * else{
            * update.set("managerIds",Arrays.asList(project.getManagerId())); }
            */
        update.set("domain", project.getDomain());
        update.set("domainId", project.getDomainId());
        update.set("accountId", project.getAccountId());
        update.set("status", project.getStatus());
        update.set("projectStartDate", project.getProjectStartDate());
        update.set("projectEndDate", project.getProjectEndDate());
        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);
        options.upsert(true);
        Project projectDB = mongoTemplate.findAndModify(query, update, options,
                Project.class);
        List<ProjectTeamMate> employeeDetails = projectTeamMatesRepo
                .findByProjectId(project.getProjectId());
        if (employeeDetails != null && projectDB != null) {
            for (ProjectTeamMate emp : employeeDetails) {
                // emp.setManagerId(projectDB.getManagerId());
                // emp.setManagerName(projectDB.getManagerName());
                emp.setAccountId(projectDB.getAccountId());
                emp.setProjectName(projectDB.getProjectName());
                // MT-79:Maintatin the managerids in List
                // emp.setManagerIds(projectDB.getManagerIds());
                projectTeamMatesRepo.save(emp);
            }
        }
        return projectDB;
	   }
    }

    @Override
    public EmployeeRoles getEmployeesRoleData(String employeeId) {
        return employeeRolesRepo.findByEmployeeId(employeeId);
    }

    @Override
    public List<ProjectTeamMate> getTeamDetails(String empId) {
        List<String> projectsId = new ArrayList<>();
        List<ProjectTeamMate> listOfTeamMates = new ArrayList<>();
        // return projectTeamMatesRepo.findById(empId);
        // Query query = new Query(Criteria.where("deliveryLeadIds").("empId"));

        List<Project> projects = projectRepo.findByDeliveryLeadIds(empId);// mongoTemplate.find(query,
                                                                          // Project.class);
        for (Project project : projects)
            projectsId.add(project.getProjectId());
        Query query1 = new Query(Criteria.where("projectId").in(projectsId));
        List<ProjectTeamMate> projectMateList = mongoTemplate.find(query1,
                ProjectTeamMate.class);
        for (ProjectTeamMate projectTeamMate : projectMateList) {
            if (!projectTeamMate.getEmployeeId().equals(empId))
                listOfTeamMates.add(projectTeamMate);
        }
        return listOfTeamMates;

    }

    // MT-72: fetching the projectteammate details.
    @Override
    public List<ProjectTeamMate> getProjectInfo(String employeeId) {
        return mongoTemplate.find(getQuery(employeeId, MyTimeUtils.START_DATE),
                ProjectTeamMate.class);
    }

    @Override
    public ProjectTeamMate addProjectTeamMate(ProjectTeamMate projectTeamMate)
            throws MyTimeException {
        if (projectTeamMate.getRole() != null && projectTeamMate.getRole().equals("Lead")) {
            Project project = projectRepo.findByProjectId(projectTeamMate.getProjectId());
            // project.getManagerIds().add(projectTeamMate.getEmployeeId());
            if (project.getManagerIds() != null)
                project.getManagerIds().add(projectTeamMate.getEmployeeId());
            else
                project.setManagerIds(Arrays.asList(projectTeamMate.getEmployeeId()));
            projectRepo.save(project);
        }

        updateProjectEndDateInProjectTeamMate(projectTeamMate);
        ProjectTeamMate pT = projectTeamMatesRepo.save(projectTeamMate);
        List<BillingDetails> listBD = getEmployeeActiveNisumBench(pT.getEmployeeId());
        for (BillingDetails b : listBD) {
            Date d = pT.getStartDate() != null ? pT.getStartDate() : new Date();
            b.setBillingEndDate(DateUtils.truncate(DateUtils.addDays(d, -1),
                    Calendar.DATE));
            b.setActive(false);
            updateEmployeeBilling(b);
        }
        BillingDetails billings = new BillingDetails();
        billings.setEmployeeId(pT.getEmployeeId());
        billings.setEmployeeName(pT.getEmployeeName());
        billings.setProjectId(pT.getProjectId());
        billings.setProjectName(pT.getProjectName());
        billings.setBillableStatus(pT.getBillableStatus());
        billings.setAccount(pT.getAccount());
        billings.setActive(pT.isActive());
        if(pT.getProjectId().equals(MyTimeUtils.BENCH_PROJECT_ID))
        {
           billings.setBillingStartDate(pT.getStartDate() == null
                ? DateUtils.truncate(new Date(), Calendar.DATE)
                : DateUtils.truncate(pT.getStartDate(), Calendar.DATE));
        }else
        {
        
          billings.setBillingStartDate(pT.getNewBillingStartDate() == null
                ? DateUtils.truncate(new Date(), Calendar.DATE)
                : DateUtils.truncate(pT.getNewBillingStartDate(), Calendar.DATE));
        }
        
        if (pT.getEndDate() != null) {
            billings.setBillingEndDate(
                    DateUtils.truncate(pT.getEndDate(), Calendar.DATE));
        }
        billings.setCreateDate(new Date());
        updateProjectEndDateInBillingDetails(
                projectTeamMate.getNewBillingStartDate(), billings);
        addEmployeeBillingDetails(billings);
        addShiftDetails(projectTeamMate);
        if (projectTeamMate.getProjectId() != null && !projectTeamMate
                .getProjectId().equalsIgnoreCase("Nisum0000")) {
            List<ProjectTeamMate> activeBenchProject = projectTeamMatesRepo
                    .findByEmployeeIdAndProjectIdAndActive(
                            projectTeamMate.getEmployeeId(), "Nisum0000", true);
            if (activeBenchProject != null || activeBenchProject.size() != 0) {
                for (ProjectTeamMate pteamMate : activeBenchProject) {
                    Date d = pT.getStartDate() != null ? pT.getStartDate()
                            : new Date();
                    pteamMate.setActive(false);
                    pteamMate.setEndDate(DateUtils
                            .truncate(DateUtils.addDays(d, -1), Calendar.DATE));
                    projectTeamMatesRepo.save(pteamMate);
                }
            }
        }
        if (projectTeamMate.getRole() != null
                && projectTeamMate.getRole().equals("Lead")) {
            Query query = new Query(Criteria.where("employeeId")
                    .is(projectTeamMate.getEmployeeId()).and("role")
                    .ne("Lead"));
            List<ProjectTeamMate> projectMates = mongoTemplate.find(query,
                    ProjectTeamMate.class);
            if (projectMates.size() == 0) {
                String roleId = roleInfoService.getRole(MyTimeUtils.LEAD);
                roleMappingService.saveUniqueEmployeeAndRole(
                        Arrays.asList(projectTeamMate.getEmployeeId()), roleId);
            }
        }
        return pT;
    }

    @Override
    public ProjectTeamMate updateTeammate(ProjectTeamMate projectTeamMate) {
    	ProjectTeamMate existingTeammate = projectTeamMatesRepo
                .findByEmployeeIdAndProjectId(projectTeamMate.getEmployeeId(),
                        projectTeamMate.getProjectId());
    	List<BillingDetails> listBD = getEmployeeActiveBillingDetails(projectTeamMate.getEmployeeId(),
				projectTeamMate.getProjectId());
    	// past or present endDate (Inactive the Resource)
    	if (projectTeamMate.getEndDate().compareTo(new Date()) <= 0 &&projectTeamMate.getEndDate().compareTo(existingTeammate.getEndDate())!=0) {
			
			existingTeammate.setActive(false);
			existingTeammate.setEndDate(projectTeamMate.getEndDate());
			BillingDetails billingDetails = new BillingDetails();
			billingDetails.setBillableStatus(MyTimeUtils.BENCH_BILLABILITY_STATUS);
			if (listBD != null && !listBD.isEmpty()) {
				BillingDetails billingDetailsExisting = listBD.get(0);
				Date actualEndDate=projectTeamMate.getEndDate();
				billingDetailsExisting.setBillingEndDate(actualEndDate);
				billingDetailsExisting.setActive(false);
				updateEmployeeBilling(billingDetailsExisting);
			}
			Date sd=projectTeamMate.getEndDate();
			billingDetails.setBillingStartDate(sd);
			billingDetails.setAccount(MyTimeUtils.BENCH_ACCOUNT);
			billingDetails.setActive(true);
			billingDetails.setEmployeeId(existingTeammate.getEmployeeId());
			billingDetails.setEmployeeName(existingTeammate.getEmployeeName());
			billingDetails.setCreateDate(new Date());
			billingDetails.setProjectId(MyTimeUtils.BENCH_PROJECT_ID);
			billingDetails.setProjectName(MyTimeUtils.FREE_POLL);
			addEmployeeBillingDetails(billingDetails);
			
			projectTeamMatesRepo.save(existingTeammate);
				ProjectTeamMate newBenchAllocation = new ProjectTeamMate();
				Project project=projectRepo.findByProjectId(MyTimeUtils.BENCH_PROJECT_ID);
				if(project!=null)
				newBenchAllocation.setAccountId(project.getAccountId());
				newBenchAllocation.setBillableStatus(MyTimeUtils.BENCH_BILLABILITY_STATUS);
				newBenchAllocation.setDesignation(existingTeammate.getDesignation());
				newBenchAllocation.setEmailId(existingTeammate.getEmailId());
				newBenchAllocation.setEmployeeId(existingTeammate.getEmployeeId());
				newBenchAllocation.setActive(true);
				newBenchAllocation.setEmployeeName(existingTeammate.getEmployeeName());
				newBenchAllocation.setProjectId(MyTimeUtils.BENCH_PROJECT_ID);
				newBenchAllocation.setStartDate(sd);
				Project p = projectRepo.findByProjectId(MyTimeUtils.BENCH_PROJECT_ID);
				newBenchAllocation.setProjectName(p.getProjectName());
				projectTeamMatesRepo.save(newBenchAllocation);
			   updateShiftDetails(existingTeammate);
			   return existingTeammate;

		}else
		{

        existingTeammate.setProjectId(projectTeamMate.getProjectId());
        existingTeammate.setProjectName(projectTeamMate.getProjectName());
        existingTeammate.setRole(projectTeamMate.getRole());
        // changing the Billability status
        if (existingTeammate.getBillableStatus() == null
                || projectTeamMate.getBillableStatus() != null
                        && existingTeammate.getBillableStatus() != null
                        && !existingTeammate.getBillableStatus()
                                .equalsIgnoreCase(
                                        projectTeamMate.getBillableStatus())) {
            if (listBD != null && !listBD.isEmpty()) {
                BillingDetails billingDetails = listBD.get(0);
                Date d = projectTeamMate.getNewBillingStartDate();
                Calendar cal = Calendar.getInstance();
                cal.setTime(d);
                cal.add(Calendar.DAY_OF_MONTH, -1);
                Date onedayless = cal.getTime();
              
                if(existingTeammate.getNewBillingStartDate().getDate()==projectTeamMate.getNewBillingStartDate().getDate())
                	billingDetails.setBillingEndDate(
                            DateUtils.truncate(projectTeamMate.getNewBillingStartDate(), Calendar.DATE));
                else
                	
                billingDetails.setBillingEndDate(
                        DateUtils.truncate(onedayless, Calendar.DATE));
                billingDetails.setActive(false);
                updateEmployeeBilling(billingDetails);
            }
            BillingDetails billings = new BillingDetails();
            billings.setEmployeeId(projectTeamMate.getEmployeeId());
            billings.setEmployeeName(projectTeamMate.getEmployeeName());
            billings.setProjectId(projectTeamMate.getProjectId());
            billings.setAccount(existingTeammate.getAccount());
            billings.setProjectName(projectTeamMate.getProjectName());
            billings.setBillableStatus(projectTeamMate.getBillableStatus());
            billings.setActive(true);
            Date d = projectTeamMate.getNewBillingStartDate();
            billings.setBillingStartDate(DateUtils.truncate(d, Calendar.DATE));
            existingTeammate.setNewBillingStartDate(
                    DateUtils.truncate(d, Calendar.DATE));
            billings.setBillingEndDate(DateUtils
                    .truncate(projectTeamMate.getEndDate(), Calendar.DATE));
            billings.setCreateDate(new Date());
            addEmployeeBillingDetails(billings);
        }else   //without changing the Billability status
        {
              if (listBD != null && !listBD.isEmpty()) {
                  BillingDetails billingDetails = listBD.get(0);
                  billingDetails.setBillingEndDate(projectTeamMate.getEndDate());
                  billingDetails.setBillingStartDate(projectTeamMate.getStartDate());
                  addEmployeeBillingDetails(billingDetails);
              }
                  existingTeammate.setNewBillingStartDate(projectTeamMate.getNewBillingStartDate());
         
        }

        existingTeammate.setBillableStatus(projectTeamMate.getBillableStatus());
        existingTeammate.setStartDate(projectTeamMate.getStartDate());
        existingTeammate.setEndDate(projectTeamMate.getEndDate());
        existingTeammate.setShift(projectTeamMate.getShift());
        ProjectTeamMate teamMate = projectTeamMatesRepo.save(existingTeammate);
        EmployeeRoles employeeDB = employeeRolesRepo
                .findByEmployeeId(teamMate.getEmployeeId());
        employeeDB.setShift(teamMate.getShift());
        employeeRolesRepo.save(employeeDB);
		
        return teamMate;
		}
    }

    public void updateShiftDetails(ProjectTeamMate existingTeammate) {
        Query getQuery = new Query();
        getQuery.addCriteria(new Criteria().andOperator(
                Criteria.where("active").is(true), Criteria.where("employeeId")
                        .is(existingTeammate.getEmployeeId())));
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1);
        EmpShiftDetails existingShift = mongoTemplate.findOne(getQuery,
                EmpShiftDetails.class);
        if (existingShift != null) {
            existingShift.setActive(false);
            existingShift.setUpdatedDate(new Date());
            mongoTemplate.save(existingShift);
        }
    }

    public void addShiftDetails(ProjectTeamMate projectTeamMate) {
        EmpShiftDetails empShiftDetails = new EmpShiftDetails();
        empShiftDetails.setCreateDate(new Date());
        empShiftDetails.setUpdatedDate(new Date());
        empShiftDetails.setEmployeeName(projectTeamMate.getEmployeeName());
        empShiftDetails.setEmployeeId(projectTeamMate.getEmployeeId());
        empShiftDetails.setShift(projectTeamMate.getShift());
        empShiftDetails.setActive(projectTeamMate.isActive());
        empShiftDetailsRepo.save(empShiftDetails);
    }

    @Override
    public void deleteTeammate(String empId, String projectId, ObjectId id) {
        ProjectTeamMate existingTeammate = projectTeamMatesRepo.findById(id);
        existingTeammate.setActive(false);
        existingTeammate.setEndDate(new Date());
        BillingDetails billingDetails = new BillingDetails();
        billingDetails.setBillableStatus("Non-Billable");
        billingDetails.setBillingStartDate(new Date());
        billingDetails.setAccount("Nisum");
        billingDetails.setActive(true);
        billingDetails.setEmployeeId(existingTeammate.getEmployeeId());
        billingDetails.setEmployeeName(existingTeammate.getEmployeeName());
        billingDetails.setCreateDate(new Date());
        billingDetails.setProjectId("Nisum0000");
        billingDetails.setProjectName(MyTimeUtils.FREE_POLL);
        addEmployeeBillingDetails(billingDetails);
        List<BillingDetails> listBD = getEmployeeActiveBillingDetails(empId,
                projectId);
        if (listBD != null && !listBD.isEmpty()) {
            BillingDetails billingDetailsExisting = listBD.get(0);
            Date d = new Date();
            d.setDate(d.getDate() - 1);
            billingDetailsExisting.setBillingEndDate(d);
            billingDetailsExisting.setActive(false);
            updateEmployeeBilling(billingDetailsExisting);
        }
        projectTeamMatesRepo.save(existingTeammate);
        ProjectTeamMate newBenchAllocation = new ProjectTeamMate();
        newBenchAllocation.setAccount("Nisum");
        newBenchAllocation.setBillableStatus("Non-Billable");
        newBenchAllocation.setDesignation(existingTeammate.getDesignation());
        newBenchAllocation.setEmailId(existingTeammate.getEmailId());
        newBenchAllocation.setEmployeeId(existingTeammate.getEmployeeId());
        newBenchAllocation.setActive(true);
        newBenchAllocation.setEmployeeName(existingTeammate.getEmployeeName());
        newBenchAllocation.setProjectId("Nisum0000");
        newBenchAllocation.setStartDate(new Date());
        Project p = projectRepo.findByProjectId("Nisum0000");
        newBenchAllocation.setProjectName(p.getProjectName());
        // newBenchAllocation.setManagerId(p.getManagerId());
        // newBenchAllocation.setManagerName(p.getManagerName());
        projectTeamMatesRepo.save(newBenchAllocation);
        updateShiftDetails(existingTeammate);
    }

    @Override
    public List<Project> getProjects(String managerId) throws MyTimeException {
        /*
         * Query query = new Query(Criteria.where("managerId").is(managerId)
         * .and("status").ne("Completed")); return mongoTemplate.find(query,
         * Project.class);
         */
        List<Project> projectList = new ArrayList<>();
        List<Project> projects = projectRepo.findByDeliveryLeadIds(managerId);
        for (Project project : projects) {
            if (!project.getStatus().equals("Completed")) {
                projectList.add(project);
            }

        }
        return projectList;
    }

    @Override
    public List<ProjectTeamMate> getMyTeamDetails(String empId) {
        List<ProjectTeamMate> teamMates = new ArrayList<>();
        List<ProjectTeamMate> empRecords = projectTeamMatesRepo
                .findByEmployeeId(empId);
        for (ProjectTeamMate pt : empRecords) {
            if (pt.isActive()) {
                teamMates.addAll(projectTeamMatesRepo
                        .findByProjectId(pt.getProjectId()));
            }
        }
        return teamMates;
    }

    @Override
    public List<EmployeeRoles> getUnAssignedEmployees() {
        List<EmployeeRoles> allEmployees = employeeRolesRepo.findAll();
        List<EmployeeRoles> notAssignedEmployees = new ArrayList<>();
        List<String> teamMates = new ArrayList<>();
        List<ProjectTeamMate> empRecords = projectTeamMatesRepo.findAll();
        for (ProjectTeamMate pt : empRecords) {
            Project project = projectRepo.findByProjectId(pt.getProjectId());
            if (project != null && project.getStatus() != null
                    && !"Completed".equalsIgnoreCase(project.getStatus())) {
                teamMates.add(pt.getEmployeeId());
            }
        }
        for (EmployeeRoles emp : allEmployees) {

            if (!teamMates.contains(emp.getEmployeeId())) {
                notAssignedEmployees.add(emp);
            }
        }

        return notAssignedEmployees;
    }

    @Override
    public List<ProjectTeamMate> getShiftDetails(String shift) {
        List<Project> projects = projectRepo.findAll();
        List<ProjectTeamMate> shiftEmpDetails = new ArrayList<>();
        for (Project pt : projects) {
            if ("Active".equalsIgnoreCase(pt.getStatus())) {
                List<ProjectTeamMate> employeeDetails = projectTeamMatesRepo
                        .findByProjectId(pt.getProjectId());
                for (ProjectTeamMate emp : employeeDetails) {
                    if (emp.getShift() != null
                            && emp.getShift().equalsIgnoreCase(shift)) {
                        shiftEmpDetails.add(emp);
                    } else if (emp.getShift() == null
                            && "Shift 1(09:00 AM - 06:00 PM)"
                                    .equalsIgnoreCase(shift))
                        shiftEmpDetails.add(emp);
                }
            }
        }
        return shiftEmpDetails;
    }

    @Override
    public List<ProjectTeamMate> getAllProjectDetails() {
        List<Project> projects = projectRepo.findAll();
        List<ProjectTeamMate> allprojectMates = new ArrayList<>();
        for (Project pt : projects) {
            if (!"Completed".equalsIgnoreCase(pt.getStatus())) {
                List<ProjectTeamMate> employeeDetails = projectTeamMatesRepo
                        .findByProjectId(pt.getProjectId());
                allprojectMates.addAll(employeeDetails);
            }

        }
        return allprojectMates;
    }

    @Override
    public List<ProjectTeamMate> getProjectDetails(String projectId,String statusFlag) {
		List<ProjectTeamMate> teamMatesList = new ArrayList<>();
		List<ProjectTeamMate> teamMates = projectTeamMatesRepo.findByProjectId(projectId);
		for (ProjectTeamMate projectTeamMate : teamMates) {
			if (projectTeamMate.getEndDate() != null) {
				// Active
				if (statusFlag.equals(MyTimeUtils.ACTIVE) && projectTeamMate.getEndDate().compareTo(new Date()) >= 0)
					teamMatesList.add(projectTeamMate);
				else if (statusFlag.equals(MyTimeUtils.IN_ACTIVE)
						&& projectTeamMate.getEndDate().compareTo(new Date()) < 0)
					teamMatesList.add(projectTeamMate);
				else if (statusFlag.equals(MyTimeUtils.BOTH))
					teamMatesList.add(projectTeamMate);
			}
		}
		return teamMatesList;
    }

    @Override
    public List<ProjectTeamMate> getMyProjectAllocations(String empId) {
        return projectTeamMatesRepo.findByEmployeeId(empId);
    }

    @Override
    public List<BillingDetails> getEmployeeBillingDetails(String empId,
            String projectId) {

        List<BillingDetails> billings = teamMatesBillingRepo
                .findByEmployeeIdAndProjectId(empId, projectId);
        List<BillingDetails> billingsSorted = billings;
        try {
            billingsSorted = (billings == null || billings.size() == 0)
                    ? billings
                    : billings.stream()
                            .sorted(Comparator
                                    .comparing(BillingDetails::getCreateDate)
                                    .reversed())
                            .collect(Collectors.toList());
        } catch (Exception e) {
            // TODO: handle exception
        }
        return billingsSorted;
    }

    @Override
    public List<BillingDetails> getEmployeeBillingDetailsAll(String empId) {

        List<BillingDetails> billings = teamMatesBillingRepo
                .findByEmployeeId(empId);
        List<BillingDetails> billingsSorted = billings;
        try {
            billingsSorted = (billings == null || billings.size() == 0)
                    ? billings
                    : billings.stream()
                            .sorted(Comparator
                                    .comparing(BillingDetails::getCreateDate)
                                    .reversed())
                            .collect(Collectors.toList());
        } catch (Exception e) {
            // TODO: handle exception
        }
        return billingsSorted;
    }

    @Override
    public List<BillingDetails> getEmployeeActiveBillingDetails(String empId,
            String projectId) {
        Query query4 = new Query();
        query4.addCriteria(Criteria.where("active").is(new Boolean(true)));
        query4.addCriteria(Criteria.where("employeeId").is(empId));
        query4.addCriteria(Criteria.where("projectId").is(projectId));
        List<BillingDetails> billings = mongoTemplate.find(query4,
                BillingDetails.class);
        List<BillingDetails> billingsSorted = billings;
        try {
            billingsSorted = (billings == null || billings.size() == 0)
                    ? billings
                    : billings.stream()
                            .sorted(Comparator
                                    .comparing(
                                            BillingDetails::getBillingStartDate)
                                    .reversed())
                            .collect(Collectors.toList());
        } catch (Exception e) {
            // TODO: handle exception
        }
        return billingsSorted;
    }

    @Override
    public List<BillingDetails> getEmployeeActiveNisumBench(String empId) {
        Query query4 = new Query();
        query4.addCriteria(Criteria.where("active").is(new Boolean(true)));
        query4.addCriteria(Criteria.where("projectId").is("Nisum0000"));
        query4.addCriteria(Criteria.where("employeeId").is(empId));
        List<BillingDetails> billings = mongoTemplate.find(query4,
                BillingDetails.class);
        List<BillingDetails> billingsSorted = billings;
        try {
            billingsSorted = (billings == null || billings.size() == 0)
                    ? billings
                    : billings.stream()
                            .sorted(Comparator
                                    .comparing(
                                            BillingDetails::getBillingStartDate)
                                    .reversed())
                            .collect(Collectors.toList());
        } catch (Exception e) {
            // TODO: handle exception
        }
        return billingsSorted;
    }

    @Override
    public BillingDetails addEmployeeBillingDetails(BillingDetails teamMate) {
        /*
         * List<BillingDetails> billingsPast = getEmployeeBillingDetails(
         * teamMate.getEmployeeId(), teamMate.getProjectId());
         */ /*
             * for (BillingDetails tB : billingsPast) { tB.setActive(false);
             * teamMatesBillingRepo.save(tB); }
             */
        return teamMatesBillingRepo.save(teamMate);
    }

    @Override
    public BillingDetails updateEmployeeBilling(BillingDetails teamMate) {
        return teamMatesBillingRepo.save(teamMate);
    }

    // Updating the projectenddate field in ProjectTeamMate .
    private void updateProjectEndDateInProjectTeamMate(
            ProjectTeamMate projectTeamMate) {
        List<ProjectTeamMate> projectTeamMateList = mongoTemplate
                .find(getQuery(projectTeamMate.getEmployeeId(),
                        MyTimeUtils.ENDDATE_COLUMN), ProjectTeamMate.class);
        if (!CollectionUtils.isEmpty(projectTeamMateList)) {
            for (ProjectTeamMate teamMate : projectTeamMateList) {
                if (teamMate.getEndDate() != null && !teamMate.getProjectName()
                        .equalsIgnoreCase(MyTimeUtils.FREE_POLL)) {
                    updateProjectEndDate(
                            projectTeamMate.getNewBillingStartDate(),
                            teamMate.getEmployeeId(), teamMate.getProjectName(),
                            MyTimeUtils.ENDDATE_COLUMN,
                            MyTimeUtils.TEAMDETAILS_COLLECTION_NAME);

                }
            }
        }

    }

    // Updating the billingenddate in BillingDetails .
    private void updateProjectEndDateInBillingDetails(Date newBillingStartDate,
            BillingDetails billingDetails) {
        List<BillingDetails> billingDetailsList = mongoTemplate
                .find(getQuery(billingDetails.getEmployeeId(),
                        MyTimeUtils.CREATED_DATE_COLUMN), BillingDetails.class);
        if (!CollectionUtils.isEmpty(billingDetailsList)) {
            for (BillingDetails billing : billingDetailsList) {
                if (billing.getBillingEndDate() != null
                        && !billing.getProjectName()
                                .equalsIgnoreCase(MyTimeUtils.FREE_POLL)) {
                    updateProjectEndDate(newBillingStartDate,
                            billing.getEmployeeId(), billing.getProjectName(),
                            MyTimeUtils.BILLING_ENDDATE_COLUMN,
                            MyTimeUtils.BILLINGDETAILS_COLLECTION_NAME);
                }

            }
        }
    }

    // Updating the projectenddate
    private void updateProjectEndDate(Date newBillingStartDate,
            String employeeId, String projectName, String dateColumn,
            String collectionName) {
        DBCollection collection = mongoTemplate.getCollection(collectionName);
        BasicDBObject searchQuery = new BasicDBObject();
        searchQuery.append(MyTimeUtils.EMPLOYEE_ID, employeeId);
        searchQuery.append(MyTimeUtils.PROJECT_NAME, projectName);
        BasicDBObject updateQuery = new BasicDBObject();
        updateQuery.append(MyTimeUtils.SET, new BasicDBObject().append(
                dateColumn,
                DateUtils.addDays(newBillingStartDate, MyTimeUtils.MINUS_ONE)));
        collection.update(searchQuery, updateQuery);
    }

    private Query getQuery(String employeeId, String dateColumn) {
        Query query = new Query();
        query.addCriteria(
                Criteria.where(MyTimeUtils.EMPLOYEE_ID).is(employeeId));
        query.limit(MyTimeUtils.ONE);
        query.with(new Sort(Sort.Direction.DESC, dateColumn));
        return query;
    }

    @Override
    public void deleteEmployeeBilling(BillingDetails teamMate) {
        // TODO Auto-generated method stub
        teamMatesBillingRepo.delete(teamMate);
    }

    @Override
    public List<EmployeeDashboardVO> getEmployeesDashBoard() {
        List<EmployeeRoles> allEmployees = employeeRolesRepo.findAll();
        List<EmployeeRoles> notAssignedEmployees = new ArrayList<>();
        List<EmployeeDashboardVO> employeeDashboard = new ArrayList<>();
        Map<String, Object> teamMatesMap = new HashMap();
        Map<String, Object> teamMatesStatusMap = new HashMap();

        // Find all active employees
        List<ProjectTeamMate> empRecords = projectTeamMatesRepo.findAll();
        for (ProjectTeamMate pt : empRecords) {
            if (pt.isActive()) {
                Project project = projectRepo
                        .findByProjectId(pt.getProjectId());
                if (project != null && project.getStatus() != null
                        && !"Completed".equalsIgnoreCase(project.getStatus())) {
                    Object projectTeamMate = teamMatesMap
                            .get(pt.getEmployeeId());

                    if (projectTeamMate == null) {
                        List listOfObjects = new ArrayList<>();
                        listOfObjects.add(pt);
                        // A person can have multiple active projects with billability
                        teamMatesMap.put(pt.getEmployeeId(), listOfObjects);
                    } else {
                        List existingRecordsInMap = (List) teamMatesMap
                                .get(pt.getEmployeeId());
                        existingRecordsInMap.add(pt);
                        teamMatesMap.put(pt.getEmployeeId(),
                                existingRecordsInMap);

                    }
                }
            }
        }
        for (EmployeeRoles emp : allEmployees) {
            if (emp.getEmployeeId() != null
                    && emp.getEmployeeId().equalsIgnoreCase("16112")) {
                System.out.println(emp);
            }

            if (teamMatesMap.containsKey(emp.getEmployeeId())) {
                System.out.println("***************** Empl alrady assigned");
                Object value = teamMatesMap.get(emp.getEmployeeId());
                if (value instanceof List) {
                    List listOfTeamMates = (List) value;
                    String billableStatus = "NA";

                    for (Object obj : listOfTeamMates) {
                        ProjectTeamMate projectTeamMate = (ProjectTeamMate) obj;
                        String status = projectTeamMate.getBillableStatus();
                        if (status == null) {
                            status = "NA";
                        }
                        System.out.println("---------------------------");
                        EmployeeDashboardVO empVo = new EmployeeDashboardVO();

                        System.out.println(emp);
                        BeanUtils.copyProperties(emp, empVo);
                        BeanUtils.copyProperties(projectTeamMate, empVo,
                                "employeeId", "employeeName", "emailId", "role",
                                "designation", "mobileNumber");

                        employeeDashboard.add(empVo);
                    }

                }
            } else {
                EmployeeDashboardVO empVo = new EmployeeDashboardVO();

                System.out.println(emp);
                BeanUtils.copyProperties(emp, empVo);
                empVo.setBillableStatus("UA");
                empVo.setProjectAssigned(false);
                employeeDashboard.add(empVo);
            }
            System.out.println("&&&&&&&&&&");
            // System.out.println(empVo);

            System.out.println("---------------------------");

        }

        return employeeDashboard;
    }

    @Override
    public List<ProjectTeamMate> findByAccountAndActiveAndBillableStatus(
            String account, boolean status, String billableStatus) {
        return projectTeamMatesRepo.findByAccountAndActiveAndBillableStatus(
                account, status, billableStatus);
    }

    public List<HashMap<String, String>> getEmployeeData(List<String> ids) {
        List<HashMap<String, String>> EmployeeList = new ArrayList<>();

        Query query = new Query(Criteria.where("employeeId").in(ids));
        List<EmployeeRoles> employeeRoles = mongoTemplate.find(query,
                EmployeeRoles.class);
        for (EmployeeRoles employeesRole : employeeRoles) {
            HashMap<String, String> managerMap = new HashMap<>();
            managerMap.put("employeeId", employeesRole.getEmployeeId());
            managerMap.put("employeeName", employeesRole.getEmployeeName());
            EmployeeList.add(managerMap);
        }
        return EmployeeList;
    }
    
	@Override
	public String addProjectTeamMateWithCheck(ProjectTeamMate projectTeamMate) throws MyTimeException {
		boolean flag = false;
		String projectName = null;
		String fromDate = null;
		String toDate = null;
		List<ProjectTeamMate> teamMateList = projectTeamMatesRepo.findByEmployeeId(projectTeamMate.getEmployeeId());
		for (ProjectTeamMate projectMate : teamMateList) {
			if (projectMate.getEndDate()!=null && (projectMate.getEndDate().compareTo(new Date())>0)
					&& !projectMate.getProjectId().equals("Nisum0000")) {
				    flag=true; 
					projectName = projectMate.getProjectName();
					fromDate = projectMate.getStartDate().toString();
					toDate = projectMate.getEndDate().toString();
				
				break;
			}
		}
		if (flag==true)
			return "Resource " + projectTeamMate.getEmployeeId() + " already Assigned to the " + projectName
					+ " Project" + " from " + fromDate + "to " + toDate;
		else
			addProjectTeamMate(projectTeamMate);
		return "TeamMate added successfuly";

	}
	
	@Override
	public List<HashMap<Object, Object>>  projectsInfoByEmpId(String empId){
		HashMap<Object, Object> projectMap =null;
		List<HashMap<Object, Object>> projectList = new ArrayList<>();
		 
		Project project= null;
		Account account = null;
		Domains domain = null;
		
		List<ProjectTeamMate> projectAllocations = getMyProjectAllocations(empId);
		
		if( null != projectAllocations && !projectAllocations.isEmpty() && MyTimeUtils.INT_ZERO < projectAllocations.size()) {
			for (ProjectTeamMate ptm : projectAllocations) {
				projectMap = new HashMap<>();
				
				project = projectRepo.findByProjectId(ptm.getProjectId());
				
				account = accountRepo.findByAccountId(ptm.getAccountId());
		        domain = domainRepo.findByDomainId(ptm.getDomainId());
		        
		        projectMap.put("id", project.getId());
				projectMap.put("projectId", project.getProjectId());
			    projectMap.put("projectName", project.getProjectName());
			    
			    if (domain != null) 
			    	projectMap.put("domain", domain.getDomainName());
			   
			    if (account != null)
	                projectMap.put("account", account.getAccountName());
			    
			    projectMap.put("accountId", account!=null?account.getAccountId():ptm.getAccountId());
			    projectMap.put("domainId",  domain!=null? domain.getDomainId() : ptm.getDomainId() );
			    projectMap.put("status", ptm.isActive());
			    projectMap.put("projectStartDate",ptm.getStartDate());
			    projectMap.put("projectEndDate",ptm.getEndDate());
	            projectMap.put("employeeIds", ptm.getEmployeeId());
	            projectMap.put("shift", ptm.getShift());
	            projectMap.put("billableStatus", ptm.getBillableStatus());    
	            
	            if (project.getDeliveryLeadIds() != null) {
	            	projectMap.put("deliveryLeadIds", getEmployeeData(project.getDeliveryLeadIds()));
	            }
	            
	            if (project.getManagerIds() != null) {
	                projectMap.put("managerIds", getEmployeeData(project.getManagerIds()));
	            }
	            
	            projectList.add(projectMap);
			} 
		}	
		return projectList;
	}

	@Override
	public Set<String> accountsAssignedToDl(String empId) {
		Set<String> accIdsSet = new HashSet<String>() ;
		
		List<Project> prjtsList = projectRepo.findByDeliveryLeadIds(empId);
		
		if(null != prjtsList && !prjtsList.isEmpty() && MyTimeUtils.INT_ZERO < prjtsList.size()) {
			for(Project obj : prjtsList) {
				accIdsSet.add(obj.getAccountId());				
			}
		}
		return accIdsSet;
	}
	
	
	   @Override
   public List<HashMap<Object, Object>> deliveryLeadProjects(String empId) throws MyTimeException {
	   List<HashMap<Object, Object>> projectsList = new ArrayList<HashMap<Object, Object>> ();
			
		Set<String> accIdsSet = projectService.accountsAssignedToDl(empId);
		List<Project> prjts= projectRepo.findByAccountIdIn(accIdsSet);		

		if(null != prjts && !prjts.isEmpty() && MyTimeUtils.INT_ZERO < prjts.size()) {
			Account account = null;
			Domains domain =null;

			HashMap<Object, Object> projectMap = null;

			for(Project obj : prjts) {
				projectMap = new HashMap<>();
				
				account = accountRepo.findByAccountId(obj.getAccountId());
				domain = domainRepo.findByDomainId(obj.getDomainId());
					
				projectMap.put("projectId", obj.getProjectId());
				projectMap.put("projectName",obj.getProjectName());
					
				projectMap.put("accountId", obj.getAccountId());
				projectMap.put("domainId", obj.getDomainId());
				
				projectMap.put("account", null != account ? account.getAccountName():"");
				projectMap.put("domain", null != domain ? domain.getDomainName() : "");
				projectMap.put("status", obj.getStatus());
				
					
				projectMap.put("deliveryLeadIds", null != obj.getDeliveryLeadIds() ? getEmployeeData(obj.getDeliveryLeadIds()) : "");
				projectMap.put("managerIds", null != obj.getManagerIds() ?  getEmployeeData(obj.getManagerIds()) : "");
									
				projectsList.add(projectMap);
			}
		}		   
		return projectsList;
   }
}
