package com.nisum.myteam.service.impl;

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

import org.apache.commons.lang.StringUtils;
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.myteam.exception.handler.MyTeamException;
import com.nisum.myteam.model.BillableEmployee;
import com.nisum.myteam.model.dao.Account;
import com.nisum.myteam.model.dao.Domain;
import com.nisum.myteam.model.dao.Employee;
import com.nisum.myteam.model.dao.Project;
import com.nisum.myteam.model.dao.Resource;
import com.nisum.myteam.repository.EmployeeRepo;
import com.nisum.myteam.service.IAccountService;
import com.nisum.myteam.service.IDomainService;
import com.nisum.myteam.service.IEmployeeLocationService;
import com.nisum.myteam.service.IEmployeeRoleService;
import com.nisum.myteam.service.IEmployeeService;
import com.nisum.myteam.service.IProjectService;
import com.nisum.myteam.service.IResourceService;
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 IEmployeeLocationService empLocationService;

    @Autowired
    private IResourceService resourceService;

    @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) {
        // 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 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);
        }
    }


    @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(java.util.Comparator.comparing(Employee::getEmployeeId))
                .collect(Collectors.toList()).forEach(employee -> {
            resultList.add(employee.getEmployeeId());
        });
        employeeList.stream().sorted(java.util.Comparator.comparing(Employee::getEmployeeName))
                .collect(Collectors.toList()).forEach(employee -> {
            resultList.add(employee.getEmployeeName());
        });
        employeeList.stream().sorted(java.util.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);

    }
    @Override
    public List<BillableEmployee> getEmployeeDetailsByFGAndBillability(String fGroup, String billableStatus) {
        Map<String,Resource> resourceMap=resourceService.findByBillableStatus(billableStatus).stream().collect(Collectors.toMap(e->e.getEmployeeId(), e->e));
        return employeeRepo.findByEmpStatusAndFunctionalGroup(ResourceStatus.ACTIVE.getStatus(), fGroup).stream().filter(e->resourceMap.keySet().contains(e.getEmployeeId())).map(e-> mappingBillableEmployee(e,fGroup,resourceMap)).collect(Collectors.toList());
    }

    private BillableEmployee mappingBillableEmployee(Employee e,String fGroup,Map<String,Resource> resourceMap ) {
        Resource resource=resourceMap.get(e.getEmployeeId());
        Project project=projectService.getProjectByProjectId(resource.getProjectId());
        return  new BillableEmployee(e.getEmployeeId(),e.getEmployeeName(),e.getEmailId(),project.getProjectName(),resource.getBillableStatus(),resource.getBillingStartDate(),resource.getBillingEndDate(),fGroup);
    }

}
