package com.nisum.mytime.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

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 com.nisum.mytime.exception.handler.MyTimeException;
import com.nisum.mytime.model.Account;
import com.nisum.mytime.model.AccountInfo;
import com.nisum.mytime.model.BillingDetails;
import com.nisum.mytime.model.Designation;
import com.nisum.mytime.model.Domains;
import com.nisum.mytime.model.EmpLoginData;
import com.nisum.mytime.model.EmpShiftDetails;
import com.nisum.mytime.model.EmployeeLocationDetails;
import com.nisum.mytime.model.EmployeeRoles;
import com.nisum.mytime.model.Location;
import com.nisum.mytime.model.MasterData;
import com.nisum.mytime.model.Project;
import com.nisum.mytime.model.ProjectTeamMate;
import com.nisum.mytime.model.Shift;
import com.nisum.mytime.model.Skill;
import com.nisum.mytime.repository.AccountInfoRepo;
import com.nisum.mytime.repository.AccountRepo;
import com.nisum.mytime.repository.DesignationRepo;
import com.nisum.mytime.repository.DomainRepo;
import com.nisum.mytime.repository.EmployeeLocationDetailsRepo;
import com.nisum.mytime.repository.EmployeeRolesRepo;
import com.nisum.mytime.repository.LocationRepo;
import com.nisum.mytime.repository.MasterDataRepo;
import com.nisum.mytime.repository.ProjectRepo;
import com.nisum.mytime.repository.ProjectTeamMatesRepo;
import com.nisum.mytime.repository.ShiftRepo;
import com.nisum.mytime.repository.TeamMatesBillingRepo;
import com.nisum.mytime.repository.TechnologyRepo;
import com.nisum.mytime.utils.MyTimeUtils;
import com.nisum.mytime.utils.PdfReportGenerator;

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private EmployeeRolesRepo employeeRolesRepo;
    @Autowired
    private ProjectRepo projectRepo;

    @Autowired
    private ProjectTeamMatesRepo projectTeamMatesRepo;
    @Autowired
    private ProjectService projectService;

    @Autowired
    private ShiftRepo shiftRepo;

    @Autowired
    private DesignationRepo designationRepo;

    @Autowired
    private AccountRepo accountRepo;
    @Autowired
    private MasterDataRepo masterDataRepo;

    @Autowired
    private TechnologyRepo technologyRepo;

    @Autowired
    private LocationRepo locationRepo;

    @Autowired
    private EmployeeDataService employeeDataBaseService;

    @Autowired
    private PdfReportGenerator pdfReportGenerator;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private EmployeeLocationDetailsRepo employeeLocationDetailsRepo;
    
    @Autowired
    private DomainRepo domainRepo;
    
    @Autowired
    private AccountInfoRepo accountInfoRepo;
    
    @Autowired
    private TeamMatesBillingRepo teamMatesBillingRepo;
    
    @Autowired
    private RoleMappingService roleMappingService;


    @Override
    public Boolean fetchEmployeesData(String perticularDate,
            boolean resynchFlag) throws MyTimeException {
 /*       return employeeDataBaseService.fetchEmployeesData(perticularDate,
                resynchFlag);*/
    	return true;  
    }

    @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<EmployeeRoles> getEmployeeRoles() throws MyTimeException {
        //return employeeRolesRepo.findAll();
    	return employeeRolesRepo.findByEmpStatus(MyTimeUtils.ACTIVE);
    }

    @Override
    public EmployeeRoles assigingEmployeeRole(EmployeeRoles employeeRoles, String empId)
            throws MyTimeException {
    	employeeRoles.setCreatedOn(new Date());
    	employeeRoles.setCreatedBy(empId);
        employeeRoles.setModifiedBy(empId);
        if (employeeRoles.getEmploymentType() == null || employeeRoles.getEmploymentType().isEmpty()) {
            employeeRoles.setEmploymentType("Full Time");
        }

        ProjectTeamMate newBenchAllocation = new ProjectTeamMate();
        newBenchAllocation.setAccount(MyTimeUtils.BENCH_ACCOUNT);
        newBenchAllocation.setBillableStatus(MyTimeUtils.BENCH_BILLABILITY_STATUS);
        newBenchAllocation.setDesignation(employeeRoles.getDesignation());
        newBenchAllocation.setEmailId(employeeRoles.getEmailId());
        newBenchAllocation.setEmployeeId(employeeRoles.getEmployeeId());
        newBenchAllocation.setMobileNumber(employeeRoles.getMobileNumber());
        
        newBenchAllocation.setEmployeeName(employeeRoles.getEmployeeName());
        newBenchAllocation.setProjectId(MyTimeUtils.BENCH_PROJECT_ID);
        newBenchAllocation.setStartDate(employeeRoles.getDateOfJoining() != null ? employeeRoles.getDateOfJoining() : new Date());
        Project p = projectRepo.findByProjectId(MyTimeUtils.BENCH_PROJECT_ID);
        newBenchAllocation.setProjectName(p.getProjectName());
        newBenchAllocation.setAccountId(p.getAccountId());
        newBenchAllocation.setDomainId(p.getDomainId());
        newBenchAllocation.setShift(employeeRoles.getShift());
        newBenchAllocation.setRole(employeeRoles.getRole());

        if (employeeRoles.getEmpStatus() != null
                && (employeeRoles.getEmpStatus().trim().equalsIgnoreCase("InActive")
                || employeeRoles.getEmpStatus().trim().equalsIgnoreCase("In Active"))) {
            employeeRoles.setEmpStatus("In Active");
            newBenchAllocation.setEndDate(employeeRoles.getEndDate());
            newBenchAllocation.setActive(false);
        } else {
            employeeRoles.setEmpStatus("Active");
            newBenchAllocation.setEndDate(p.getProjectEndDate());
            newBenchAllocation.setActive(true);
        }
        
        try {
            projectService.addProjectTeamMate(newBenchAllocation);
        } catch (MyTimeException e) {
            e.printStackTrace();
        }

        /*
         * BillingDetails billingDetails = new BillingDetails();
         * billingDetails.setBillableStatus("Bench"); billingDetails
         * .setBillingStartDate(employeeRoles.getDateOfJoining() != null ?
         * employeeRoles.getDateOfJoining() : new Date());
         * billingDetails.setActive(true);
         * billingDetails.setEmployeeId(employeeRoles.getEmployeeId());
         * billingDetails.setEmployeeName(employeeRoles.getEmployeeName());
         * billingDetails.setCreateDate(new Date());
         * billingDetails.setProjectId("Nisum0000");
         * billingDetails.setProjectName("Bench");
         * projectService.addEmployeeBillingDetails(billingDetails);
         */
        saveEmployeeLocationDetails(employeeRoles);
        return employeeRolesRepo.save(employeeRoles);
    }

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

    }

    @Override
    public void deleteEmployee(String employeeId) {
        EmployeeRoles role = employeeRolesRepo.findByEmployeeId(employeeId);
        employeeRolesRepo.delete(role);
    }

    @Override
    public EmployeeRoles updateEmployeeRole(EmployeeRoles employeeRoles, String empId) {
        // update all emp details to inactive if employee is inactive
        Query query = new Query(Criteria.where("employeeId").is(employeeRoles.getEmployeeId()));
        Update update = new Update();
        update.set("employeeName", employeeRoles.getEmployeeName());
        update.set("emailId", employeeRoles.getEmailId());
        update.set("role", employeeRoles.getRole());
        update.set("gender", employeeRoles.getGender());
        update.set("functionalGroup", employeeRoles.getFunctionalGroup());
        update.set("empStatus", employeeRoles.getEmpStatus());
        update.set("employmentType", employeeRoles.getEmploymentType());
        update.set("empLocation", employeeRoles.getEmpLocation());
        update.set("domain", employeeRoles.getDomain());
        update.set("designation", employeeRoles.getDesignation());
        update.set("dateOfBirth", employeeRoles.getDateOfBirth());
        update.set("dateOfJoining", employeeRoles.getDateOfJoining());
        update.set("lastModifiedOn", new Date());
        update.set("hasPassort", employeeRoles.getHasPassort());
        update.set("hasB1", employeeRoles.getHasB1());
        update.set("passportExpiryDate", employeeRoles.getPassportExpiryDate());
        update.set("b1ExpiryDate", employeeRoles.getB1ExpiryDate());
        
        update.set("modifiedBy", empId);
        
        if(employeeRoles.getEmpStatus().equalsIgnoreCase(MyTimeUtils.IN_ACTIVE_SPACE)) {
            update.set("endDate", employeeRoles.getEndDate());
        }
        // update employee location
        if (employeeRoles.getEmpLocation() != null && !employeeRoles.getEmpLocation().equals("")) {
            EmployeeRoles existingEmployee = employeeRolesRepo.findByEmployeeId(employeeRoles.getEmployeeId());
            if (!existingEmployee.getEmpLocation().equals(employeeRoles.getEmpLocation())) {
                updateEmployeeLocationDetails(employeeRoles, false);
            }

        }

        // update employee details
        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);
        options.upsert(true);
        EmployeeRoles emp = mongoTemplate.findAndModify(query, update, options, EmployeeRoles.class);
        try {
            // update employee location
            if (employeeRoles.getEmpLocation() != null && !employeeRoles.getEmpLocation().equals("")) {
                EmployeeRoles existingEmployee = employeeRolesRepo.findByEmployeeId(employeeRoles.getEmployeeId());
                if (!existingEmployee.getEmpLocation().equals(employeeRoles.getEmpLocation())) {
                    updateEmployeeLocationDetails(employeeRoles, false);
                }
            }

            // update employee details
            List<ProjectTeamMate> employeeProfiles = projectTeamMatesRepo
                    .findByEmployeeId(emp.getEmployeeId());
            if (employeeProfiles != null && !employeeProfiles.isEmpty()) {
                for (ProjectTeamMate profile : employeeProfiles) {
                    profile.setRole(emp.getRole());
                    projectTeamMatesRepo.save(profile);
                }
            }
            // inactive the employee from the assigned project 
			if (employeeRoles.getEmpStatus().equals(MyTimeUtils.IN_ACTIVE_SPACE) && employeeRoles.getEndDate() != null
					&& employeeRoles.getEndDate().compareTo(new Date()) <= 0) {

				List<ProjectTeamMate> projectTeamMate = projectTeamMatesRepo
						.findByEmployeeIdAndActive(employeeRoles.getEmployeeId(), true);
				if (projectTeamMate.size()==1) {
					ProjectTeamMate teamMate = projectTeamMate.get(0);
					teamMate.setActive(false);
					teamMate.setEndDate(employeeRoles.getEndDate());
					List<BillingDetails> listBD = getEmployeeActiveBillingDetails(teamMate.getEmployeeId(),
							teamMate.getProjectId());
					if (listBD != null && !listBD.isEmpty()) {
						BillingDetails billingDetailsExisting = listBD.get(0);
						billingDetailsExisting.setBillingEndDate(employeeRoles.getEndDate());
						billingDetailsExisting.setActive(false);
						teamMatesBillingRepo.save(billingDetailsExisting);
					}
					projectTeamMatesRepo.save(teamMate);
					updateShiftDetails(teamMate);
				}
			}
        } catch (Exception e) {
        }
        return emp;
    }

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

    @Override
    public List<Shift> getAllShifts() throws MyTimeException {
        return shiftRepo.findAll();
    }

    @Override
    public List<Designation> getAllDesignations() throws MyTimeException {
        return designationRepo.findAll();
    }

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

    @Override
    public List<Skill> getTechnologies() throws MyTimeException {
        return technologyRepo.findAll();
    }

    @Override
    public List<Location> getLocations() throws MyTimeException {
        return locationRepo.findAll();
    }

    @Override
    public EmployeeRoles updateProfile(EmployeeRoles employeeRoles)
            throws MyTimeException {
        boolean mobileNumberChanged = false;
        employeeRoles.setLastModifiedOn(new Date());
        EmployeeRoles existingEmployee = employeeRolesRepo
                .findByEmployeeId(employeeRoles.getEmployeeId());
        String newMobileNumber = employeeRoles.getMobileNumber();
        if (newMobileNumber != null && !newMobileNumber.equalsIgnoreCase("")) {
            if ((existingEmployee != null
                    && existingEmployee.getMobileNumber() != null
                    && !existingEmployee.getMobileNumber()
                            .equalsIgnoreCase(newMobileNumber))
                    || (existingEmployee.getMobileNumber() == null)) {
                mobileNumberChanged = true;
            }
        }
        existingEmployee.setMobileNumber(employeeRoles.getMobileNumber());
        existingEmployee.setAlternateMobileNumber(employeeRoles.getAlternateMobileNumber());
        existingEmployee.setPersonalEmailId(employeeRoles.getPersonalEmailId());
        existingEmployee.setBaseTechnology(employeeRoles.getBaseTechnology());
        existingEmployee.setTechnologyKnown(employeeRoles.getTechnologyKnown());
        EmployeeRoles employeeRolesDB = employeeRolesRepo.save(existingEmployee);
        if (mobileNumberChanged) {
            try {
                List<ProjectTeamMate> employeeProfiles = projectTeamMatesRepo
                        .findByEmployeeId(employeeRoles.getEmployeeId());
                if (employeeProfiles != null && !employeeProfiles.isEmpty()) {
                    for (ProjectTeamMate profile : employeeProfiles) {
                        profile.setMobileNumber(employeeRolesDB.getMobileNumber());
                        projectTeamMatesRepo.save(profile);
                    }
                }
            } catch (Exception e) {
            }
        }
        return employeeRolesDB;

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.nisum.mytime.service.UserService#getEmployeeRoleDataForSearchCriteria
     * (java.lang.String, java.lang.String)
     */
    @Override
    public EmployeeRoles getEmployeeRoleDataForSearchCriteria(String searchId,
            String searchAttribute) {
        if (MyTimeUtils.EMPLOYEE_NAME.equals(searchAttribute)) {
            return employeeRolesRepo.findByEmployeeName(searchId);
        } else if (MyTimeUtils.EMAIL_ID.equals(searchAttribute)) {
            return employeeRolesRepo.findByEmailId(searchId);
        }
        return null;

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.nisum.mytime.service.UserService#getEmployeeDetailsForAutocomplete()
     */
    @Override
    public List<String> getEmployeeDetailsForAutocomplete() {
        List<EmployeeRoles> roles = employeeRolesRepo.findAll();
        List<String> details = new ArrayList<>();
        roles.stream()
                .sorted(java.util.Comparator
                        .comparing(EmployeeRoles::getEmployeeId))
                .collect(Collectors.toList()).forEach(role -> {
                    details.add(role.getEmployeeId());
                });
        roles.stream()
                .sorted(java.util.Comparator
                        .comparing(EmployeeRoles::getEmployeeName))
                .collect(Collectors.toList()).forEach(role -> {
                    details.add(role.getEmployeeName());
                });
        roles.stream()
                .sorted(java.util.Comparator
                        .comparing(EmployeeRoles::getEmailId))
                .collect(Collectors.toList()).forEach(role -> {
                    details.add(role.getEmailId());
                });
        return details;
    }

    @Override
    public List<MasterData> getMasterData() throws MyTimeException {
        return masterDataRepo.findAll();
    }

    @Override
    public void updateEmployeeLocationDetails(EmployeeRoles employeeRoles,
            boolean delete) {
        try {
            Query getQuery = new Query();
            getQuery.addCriteria(new Criteria().andOperator(
                    Criteria.where("active").is(true),
                    Criteria.where("employeeId")
                            .is(employeeRoles.getEmployeeId())));
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DATE, -1);
            EmployeeLocationDetails existingLocation = mongoTemplate
                    .findOne(getQuery, EmployeeLocationDetails.class);
            if (existingLocation != null) {
                existingLocation.setActive(false);
                existingLocation.setEndDate(cal.getTime());
                existingLocation.setUpdatedDate(new Date());
                mongoTemplate.save(existingLocation);
            }
            if (!delete)
                saveEmployeeLocationDetails(employeeRoles);
        } catch (Exception ex) {
            System.out.println(ex);
        }
    }

    @Override
    public void saveEmployeeLocationDetails(EmployeeRoles employeeRoles) {
        EmployeeLocationDetails employeeLocationDetails = new EmployeeLocationDetails();
        employeeLocationDetails.setActive(employeeRoles.getEmpStatus().equalsIgnoreCase("Active"));
        employeeLocationDetails.setEmployeeId(employeeRoles.getEmployeeId());
        employeeLocationDetails.setEmployeeName(employeeRoles.getEmployeeName());
        employeeLocationDetails.setCreateDate(new Date());
        employeeLocationDetails.setEndDate(new Date());
        employeeLocationDetails.setUpdatedDate(new Date());
        employeeLocationDetails.setStartDate(new Date());
        employeeLocationDetails.setEmpLocation(employeeRoles.getEmpLocation());
        employeeLocationDetailsRepo.save(employeeLocationDetails);
    }

    @Override
    public List<EmployeeLocationDetails> getEmployeeLocationDetails(String empId) {
        return employeeLocationDetailsRepo.findByEmployeeId(empId);
    }

    @Override
    public List<EmployeeRoles> getEmployeesByFunctionalGrp(
            String functionalGrp) {
        return employeeRolesRepo.findByEmpStatusAndFunctionalGroup("Active", functionalGrp);

    }

    @Override
    public List<EmployeeRoles> getEmployeesByStatus(String status) {
        if (status.equals("both")) {
            return employeeRolesRepo.findAll(new Sort(Sort.Direction.ASC, "employeeName"));
        } else {
            return employeeRolesRepo.findByEmpStatusOrderByEmployeeNameAsc(status);
        }
    }

	@Override
	public List<HashMap<String, String>> getDeliveryLeads(String domainId) {
		List<HashMap<String, String>> EmployeeList = null;
		Domains domains= domainRepo.findByDomainId(domainId);
		EmployeeList=getEmployeeData(domains.getDeliveryManagers());
		return EmployeeList;
	}
	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 List<AccountInfo> getAccountsInfo() throws MyTimeException {
        return accountInfoRepo.findAll();
    }

	@Override
	public List<Domains> getDomains(String accountId)throws MyTimeException {
		List<Domains> domains= domainRepo.findByAccountId(accountId);
		return domains;

	}
	  
	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) {
			e.printStackTrace();
		}
		return billingsSorted;
	}

	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);
		}
	}
	
	@Override
	public boolean verifyRole(String empId,String roleName) {
		boolean flag = false;
		String role  = getEmployeesRoleData(empId).getRole();
		if( null != role &&  "" !=  role && !"Admin".equalsIgnoreCase(role)  ) {
			Set<String> roleSet = roleMappingService.empRolesMapInfoByEmpId(empId);
			if(null != roleSet && !roleSet.isEmpty() && MyTimeUtils.INT_ZERO < roleSet.size() ) {
				if( roleSet.contains(roleName) ) {
					flag = true;
				}
			}
			
		}
		return flag;
	}
}
