package com.nisum.myteam.service.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import com.nisum.myteam.model.dao.*;
import com.nisum.myteam.service.*;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.myteam.exception.handler.MyTeamException;
import com.nisum.myteam.repository.EmployeeRepo;
import com.nisum.myteam.statuscodes.ResourceStatus;
import com.nisum.myteam.utils.MyTeamUtils;
import com.nisum.myteam.utils.constants.ApplicationRole;
import com.nisum.myteam.utils.constants.RoleConstant;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class EmployeeService implements IEmployeeService {


    @Autowired
    private EmployeeRepo employeeRepo;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IAccountService accountService;

    @Autowired
    private IProjectService projectService;

    @Autowired
    private IDomainService domainService;

    @Autowired
    private IEmployeeRoleService employeeRoleService;

    @Autowired
    private EmployeeLocationService empLocationService;

    @Autowired
    private IResourceService resourceService;

    @Autowired
    private ISubStatusService subStatusService;

    public Map<String , String> response = new HashMap<>();

    @Override
    public Employee createEmployee(Employee employee, String loginEmpId) throws MyTeamException {
        employee.setCreatedOn(new Date());
        employee.setCreatedBy(loginEmpId);
        employee.setModifiedBy(loginEmpId);

        // adding employee to Bench Allocation
        resourceService.addResourceToBenchProject(employee, loginEmpId);

        // Saving employee Location Details.
        empLocationService.save(employee);

        return employeeRepo.save(employee);
    }

    @Override
    public Employee updateEmployee(Employee employeeReq, String loginEmpId) throws ParseException {
        response.put("messege" , "Employee has been updated");
        // update all emp details to inactive if employee is inactive
        Query query = new Query(Criteria.where("employeeId").is(employeeReq.getEmployeeId()));
        Update update = new Update();
        update.set("employeeName", employeeReq.getEmployeeName());
        update.set("emailId", employeeReq.getEmailId());
        update.set("role", employeeReq.getRole());
        update.set("gender", employeeReq.getGender());
        update.set("functionalGroup", employeeReq.getFunctionalGroup());
        update.set("empStatus", employeeReq.getEmpStatus());
//        update.set("empSubStatus", employeeReq.getEmpSubStatus());
        update.set("employmentType", employeeReq.getEmploymentType());
        update.set("empLocation", employeeReq.getEmpLocation());
//        update.set("domain", employeeReq.getDomain());
        update.set("designation", employeeReq.getDesignation());
        update.set("dateOfBirth", employeeReq.getDateOfBirth());
        update.set("dateOfJoining", employeeReq.getDateOfJoining());
        update.set("lastModifiedOn", new Date());
        update.set("hasPassort", employeeReq.getHasPassort());
        update.set("hasB1", employeeReq.getHasB1());
        update.set("passportExpiryDate", employeeReq.getPassportExpiryDate());
        update.set("b1ExpiryDate", employeeReq.getB1ExpiryDate());

        update.set("modifiedBy", loginEmpId);

        if (employeeReq.getEmpStatus().equalsIgnoreCase(MyTeamUtils.IN_ACTIVE_SPACE)) {
            update.set("endDate", employeeReq.getEndDate());
//            update.set("empSubStatus", null);
        }
        // update employee location
        if (employeeReq.getEmpLocation() != null && !employeeReq.getEmpLocation().equals("")) {
            Employee existingEmployee = employeeRepo.findByEmployeeId(employeeReq.getEmployeeId());
            if (!existingEmployee.getEmpLocation().equals(employeeReq.getEmpLocation())) {
                empLocationService.update(employeeReq, false);
            }

        }

        //update substatus
        if(employeeReq.getEmpSubStatus()!=null) {
            HashMap<String, Object> substatus = (LinkedHashMap) employeeReq.getEmpSubStatus();
            if (substatus.keySet().contains("subStatus") && substatus.get("subStatus") != null) {
                EmployeeSubStatus latestSubStatus = new EmployeeSubStatus();
                latestSubStatus.setEmployeeID(substatus.get("employeeID").toString());
                latestSubStatus.setSubStatus(substatus.get("subStatus").toString());
                latestSubStatus.setFromDate(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(substatus.get("fromDate").toString()));
                latestSubStatus.setToDate(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(substatus.get("toDate").toString()));
                EmployeeSubStatus currentSubStatus = subStatusService.getCurrentSubStatus(employeeReq.getEmployeeId());
                if (currentSubStatus != null) {
                    if (currentSubStatus.getSubStatus().equals(latestSubStatus.getSubStatus())) {
                        latestSubStatus.setId(currentSubStatus.getId());
                        subStatusService.updateExistingEmplyeeSubStatus(latestSubStatus);
                    } else {
                        response.put("messege", "Employee is already " + currentSubStatus.getSubStatus() + " from " + currentSubStatus.getFromDate() + " to " + currentSubStatus.getToDate());
                    }
                } else {
//                currentSubStatus = subStatusService.getLatestEmployeeSubStatus(employeeReq.getEmployeeId());
//                if(currentSubStatus == null)
                    subStatusService.addEmployeeSubStatus(latestSubStatus);
//                else {
//                    latestSubStatus.setId(currentSubStatus.getId());
//                    subStatusService.updateExistingEmplyeeSubStatus(latestSubStatus);
//                }
                }
            }
        }


        // update employee details
        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);
        options.upsert(true);
        Employee employeeUpdated = mongoTemplate.findAndModify(query, update, options, Employee.class);

        try {
            // add to resource collection
            //resourceService.addResources(employeeUpdated, loginEmpId);

            // inactive the employee from the assigned project.
            //resourceService.inactivateResource(employeeReq, employeeUpdated, loginEmpId);

        } catch (Exception e) {
        }
        return employeeUpdated;
    }

    @Override
    public Employee deleteEmployee(String employeeId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("employeeId").is(employeeId));
        Update update = new Update();
        update.set("empStatus", "InActive");
        Employee employeeUpdated = mongoTemplate.findAndModify(query, update, Employee.class);
        log.info("The Deletion operation Result::" + employeeUpdated);
        return employeeUpdated;
    }

    @Override
    public Employee updateProfile(Employee employeeReq) throws MyTeamException {
        boolean mobileNumberChanged = false;
        employeeReq.setLastModifiedOn(new Date());
        Employee existingEmployee = employeeRepo.findByEmployeeId(employeeReq.getEmployeeId());
        String newMobileNumber = employeeReq.getMobileNumber();
        if (newMobileNumber != null && !newMobileNumber.equalsIgnoreCase("")) {
            if ((existingEmployee != null && existingEmployee.getMobileNumber() != null
                    && !existingEmployee.getMobileNumber().equalsIgnoreCase(newMobileNumber))
                    || (existingEmployee.getMobileNumber() == null)) {
                mobileNumberChanged = true;
            }
        }
        existingEmployee.setMobileNumber(employeeReq.getMobileNumber());
        existingEmployee.setAlternateMobileNumber(employeeReq.getAlternateMobileNumber());
        existingEmployee.setPersonalEmailId(employeeReq.getPersonalEmailId());
        existingEmployee.setBaseTechnology(employeeReq.getBaseTechnology());
        existingEmployee.setTechnologyKnown(employeeReq.getTechnologyKnown());
        Employee employeePersisted = employeeRepo.save(existingEmployee);
        return employeePersisted;

    }

    public boolean isEmployeeExistsById(String employeeId) {
        Employee employeeFound = getEmployeeById(employeeId);
        return (employeeFound == null) ? false : true;

    }

    @Override
    public Employee getEmployeeById(String employeeId) {
        log.info("The employeeId::" + employeeId);

        Employee employee = employeeRepo.findByEmployeeId(employeeId);
        log.info("Employee Found in Repo::" + employee);
        return employee;
    }

    @Override
    public List<Employee> getAllEmployees() {
        return employeeRepo.findAll();
    }

    @Override
    public Employee getEmployeeByEmaillId(String emailId) {
        return employeeRepo.findByEmailId(emailId);
    }

    @Override
    public List<Employee> getEmployeesByEmpStatusAndShift(String empStatus, String shift) {
        return employeeRepo.findByEmpStatusAndShiftLikeOrderByEmployeeIdDesc(empStatus, shift);
    }

    public List<Employee> getManagers() throws MyTeamException {
        List<Employee> activeEmpsList = getActiveEmployees();
        List<Employee> managers = activeEmpsList.stream()
                .filter(employee -> (RoleConstant.DIRECTOR.getRoleName().equalsIgnoreCase(employee.getRole())
                        || RoleConstant.DELIVERY_MANAGER.getRoleName().equalsIgnoreCase(employee.getRole())
                        || RoleConstant.MANAGER.getRoleName().equalsIgnoreCase(employee.getRole())
                        || RoleConstant.HR_MANAGER.getRoleName().equalsIgnoreCase(employee.getRole())
                        || RoleConstant.LEAD.getRoleName().equalsIgnoreCase(employee.getRole())))
                .sorted(Comparator.comparing(Employee::getEmployeeName)).collect(Collectors.toList());
        return managers;
    }

    @Override
    public List<Employee> getActiveEmployees() throws MyTeamException {
        return employeeRepo.findByEmpStatus(MyTeamUtils.ACTIVE);
    }

    @Override
    public List<Employee> getEmployeesByStatus(String status) {
//        if (status.equals("all")) {
//            return employeeRepo.findAll(new Sort(Sort.Direction.ASC, "employeeName"));
//        } else if (status.equals("Vacation")) {
//            Query query = new Query();
//            query.addCriteria(Criteria.where("empSubStatus").ne("Resigned").andOperator(Criteria.where("empSubStatus").ne(null),Criteria.where("empSubStatus").ne("")));
//            return mongoTemplate.find(query, Employee.class);
//        } else if (status.equals("Resigned")) {
//            return employeeRepo.findByEmpSubStatusOrderByEmployeeNameAsc("Resigned");
//        } else {
//            return employeeRepo.findByEmpStatus(status);
//        }
        List<Employee> employeesList = employeeRepo.findAll(new Sort(Sort.Direction.ASC, "employeeName"));
        employeesList.stream().forEach(e -> {
            EmployeeSubStatus subStatus = subStatusService.getCurrentSubStatus(e.getEmployeeId());
            if(subStatus!=null)
                e.setEmpSubStatus((EmployeeSubStatus) subStatus);
            else
                e.setEmpSubStatus(null);
        });
        if (status.equals("all")) {
            return employeesList;
        } else if (status.equals("Vacation")) {

            return employeesList.stream().filter(e -> e.getEmpSubStatus()!= null ||
                    (e.getEmpSubStatus()!=null && !e.getEmpSubStatus().equals("Resigned")) ||
                    (e.getEmpSubStatus()!=null && !e.getEmpSubStatus().equals(""))).collect(Collectors.toList());
        } else if (status.equals("Resigned")) {
            return employeeRepo.findByEmpSubStatusOrderByEmployeeNameAsc("Resigned");
        } else {
            return employeesList.stream().filter(e->e.getEmpStatus().equals(status)).collect(Collectors.toList());
        }

    }


    @Override
    public List<Account> getAccounts() throws MyTeamException {
        return accountService.getAllAccounts();

    }

    @Override
    public Employee getEmployeeRoleDataForSearchCriteria(String searchId, String searchAttribute) {
        if (MyTeamUtils.EMPLOYEE_NAME.equals(searchAttribute)) {
            return employeeRepo.findByEmployeeName(searchId);
        } else if (MyTeamUtils.EMAIL_ID.equals(searchAttribute)) {
            return employeeRepo.findByEmailId(searchId);
        }
        return null;

    }

    @Override
    public List<String> getEmployeeDetailsForAutocomplete() {
        List<Employee> employeeList = employeeRepo.findAll();
        List<String> resultList = new ArrayList<>();
        employeeList.stream().sorted(Comparator.comparing(Employee::getEmployeeId))
                .collect(Collectors.toList()).forEach(employee -> {
            resultList.add(employee.getEmployeeId());
        });
        employeeList.stream().sorted(Comparator.comparing(Employee::getEmployeeName))
                .collect(Collectors.toList()).forEach(employee -> {
            resultList.add(employee.getEmployeeName());
        });
        employeeList.stream().sorted(Comparator.comparing(Employee::getEmailId)).collect(Collectors.toList())
                .forEach(employee -> {
                    resultList.add(employee.getEmailId());
                });
        return resultList;
    }

    @Override
    public List<HashMap<String, String>> getDeliveryLeads(String domainId) {
        Domain domain = domainService.getDomainById(domainId);
        return getDeliveryManagerMap(domain.getDeliveryManagers());
    }

    @Override
    public List<HashMap<String, String>> getDeliveryManagerMap(List deliveryManagerIdsList) {
        List<HashMap<String, String>> employeeList = new ArrayList<>();

        Query query = new Query(Criteria.where("employeeId").in(deliveryManagerIdsList));
        List<Employee> employeePersistedList = mongoTemplate.find(query, Employee.class);
        for (Employee employee : employeePersistedList) {
            HashMap<String, String> managerMap = new HashMap<>();
            managerMap.put("employeeId", employee.getEmployeeId());
            managerMap.put("employeeName", employee.getEmployeeName());
            employeeList.add(managerMap);
        }
        return employeeList;
    }


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

    }

    @Override
    public boolean verifyEmployeeRole(String empId, String roleIdReq) {
        boolean flag = false;

        log.info("The employeeId::" + empId);

        Employee employee = getEmployeeById(empId);
        log.info("Employee::::in EmployeeService::" + employee);

        String roleName = employee.getRole();
        log.info("The employee role::" + roleName);
        if (StringUtils.isNotBlank(roleName) && !ApplicationRole.ADMIN.getRoleName().equalsIgnoreCase(roleName)) {

            if (ApplicationRole.FUNCTIONAL_MANAGER.getRoleName().equalsIgnoreCase(roleName) ||
                    ApplicationRole.DELIVERY_LEAD.getRoleName().equalsIgnoreCase(roleName)) {
                flag = true;
                log.info("in if block");
            } else {
                log.info("in else block");
                Set<String> roleSet = employeeRoleService.empRolesMapInfoByEmpId(empId);
                if (null != roleSet && !roleSet.isEmpty() && MyTeamUtils.INT_ZERO < roleSet.size()) {
                    if (roleSet.contains(roleIdReq)) {
                        flag = true;
                    }
                }

            }


        }
        log.info("before return flag::" + flag);
        return flag;
    }

    public List<Employee> getEmployeesFromList(Set<String> empIdsSet) {
        return employeeRepo.findByEmployeeIdIn(empIdsSet);

    }

}
