package performa.orm;

import java.util.*;
import oneit.logging.LoggingArea;
import oneit.net.LoopbackHTTP;
import oneit.objstore.*;
import oneit.objstore.cloning.AssocCopyingRule;
import oneit.objstore.cloning.BusinessCopyHelper;
import oneit.objstore.cloning.CopyType;
import oneit.objstore.rdbms.filters.*;
import oneit.objstore.utils.ObjstoreUtils;
import oneit.security.*;
import oneit.utils.*;
import oneit.utils.filter.CollectionFilter;
import oneit.utils.filter.Filter;
import oneit.utils.parsers.FieldException;
import performa.orm.types.*;
import performa.utils.*;


public class Job extends BaseJob
{
    private static final long serialVersionUID = 0L;
   
    public  static LoggingArea  LOG = LoggingArea.createLoggingArea("Job");

    
    // This constructor should not be called
    public Job ()
    {
        // Do not add any code to this, always put it in initialiseNewObject
    }

    
    @Override
    protected void postInitNewObj() throws BusinessException 
    {
        super.postInitNewObj();
        
        setJobStatus(JobStatus.DRAFT);
        
        initAttribs();
        
        for(CultureElement cultureElement : CultureElement.searchAll(getTransaction()))
        {
            CultureCriteria cultureCriteria =   CultureCriteria.createCultureCriteria(getTransaction());
            
            cultureCriteria.setCultureElement(cultureElement);
            addToCultureCriterias(cultureCriteria);
        }
        
        createWorkFlow(ApplicationStatus.DRAFT, 1);
        createWorkFlow(ApplicationStatus.SUBMITTED, 2);
        createWorkFlow(ApplicationStatus.SHORTLISTED, 3);
        createWorkFlow(ApplicationStatus.UNSUITABLE, 9);
    }

    public WorkFlow createWorkFlow(ApplicationStatus status, int sortOrder) throws FieldException
    {
        WorkFlow    workFlow    =   WorkFlow.createWorkFlow(getTransaction());

        workFlow.setName(status.getDescription());
        workFlow.setApplicationStatus(status);
        workFlow.setSortOrder(sortOrder);
        addToWorkFlows(workFlow);
        
        return workFlow;
    }
    
    public void initAttribs() throws BusinessException
    {
        setRandomKey(RandomStringGen.getRandomStringGen().generateAlphaNum(4));
        
        //TODO: verify
        SecUser txUser = SecUser.getTXUser(getTransaction());
        
        setCreatedBy(txUser.getExtension(CompanyUser.REFERENCE_CompanyUser));
    }
    
    
    @Override
    public void validate(ValidationContext context) 
    {
        super.validate(context);

        if (getIncludeAssessmentCriteria())
        {
           context.check(getAssessmentCriteriasCount() > 0 ,  this, FIELD_IncludeAssessmentCriteria, "atleastOneRequirement");
        }
        
        context.check(getSaveAssessmentTemplate() == null || !getSaveAssessmentTemplate() ,  this, FIELD_AssessmentTemplateName, "saveTemplateFirst"); 
        
        context.check(getSaveCultureTemplate() == null || !getSaveCultureTemplate() ,  this, FIELD_CultureTemplateName, "saveTemplateFirst"); 
    }
    

    @Override
    protected void postJobStatusChange() throws FieldException 
    {
        setLastStatusChangeDate(new Date());
        super.postJobStatusChange();
    }

    
    @Override
    public FieldWriteability getWriteability_JobStatus() 
    {
        if(getJobStatus() == JobStatus.FILLED)
        {
            return FieldWriteability.NOT_IN_GUI;
        }
        
        return super.getWriteability_JobStatus();
    }
    
    public void calculateRoleFitForRanking() throws FieldException
    {
        if(!isTrue(getRankingCompleted()))
        {
            //Preloading data
            pipelineJob().toJobApplications().toCandidate().toTestAnalysises().toCandidateClassScores().uniqueVals();
            pipelineJob().toJobApplications().toCandidate().toCultureCriteriaAnswers().toSelectedQuestion().toNarratives().toCultureElementRating().uniqueVals();
            pipelineJob().toJobApplications().toCandidate().toCultureCriteriaAnswers().toCultureElement().uniqueVals();
            
            pipelineJob().toLevel().toLevelClassCriterias().toFactorClass().uniqueVals();
            pipelineJob().toLevel().toClassNormalisations().uniqueVals();
            pipelineJob().toLevel().toLevelNormalisations().uniqueVals();
            
            List<JobApplication>    sortedApplications  =   ObjstoreUtils.sort(getSubmittedApplications(), 
                                                                                new ObjectTransform[]{JobApplicationRoleFitTransform.INSTANCE, JobApplicationCultureFitTransform.INSTANCE}, 
                                                                                new Comparator[]{CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR), CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR)});
            
            for(JobApplication jobApplication : getSubmittedApplications())
            {
                jobApplication.setOverallRank(sortedApplications.indexOf(jobApplication) + 1);
            }
        }
        setRankingCompleted(true);
    }
    
    public JobApplication getTopper()
    {
        try
        {
            calculateRoleFitForRanking();
        }
        catch(FieldException ex)
        {
            return null;
        }
        List<JobApplication>    sortedApplications  =   ObjstoreUtils.sort(getSubmittedApplications(), 
                                                                            new ObjectTransform[]{JobApplicationRoleFitTransform.INSTANCE}, 
                                                                            new Comparator[]{CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR)});
        
        if(sortedApplications.size() > 0)
        {
            return sortedApplications.get(0);
        }
        return null;
    }
    
    public Boolean jobDetailsCompleted()
    {
        return getJobTitle() != null && getJobDescription() != null && getLevel() != null;
    }
    
    public Boolean jobAssessmentCompleted()
    {
        return cultureCriteriaCompleted() || getAssessmentCriteriasCount() > 0;
    }
    
    public Boolean cultureCriteriaCompleted()
    {
        Filter<CultureCriteria> filter  =   CultureCriteria.SearchByAll().andIsApplicable(new EqualsFilter<>(Boolean.TRUE));
        
        return pipelineJob().toCultureCriterias(filter).uniqueVals().size() > 0;
    }
    
    
    public Collection<AssessmentCriteria> getAssessmentCriteraByType(CriteriaType criteria)
    {
        Filter  filter  = AssessmentCriteria.SearchByAll().andCriteriaType(new EqualsFilter<>(criteria));   

        return  CollectionFilter.filter(getAssessmentCriteriasSet(), filter);
    }
    
    
    public int getAssessmentCriteraCountByType(CriteriaType criteria)
    {
        return getAssessmentCriteraByType(criteria).size();
    }
    
    public boolean isExpressJob()
    {
        return getAssessmentType() == AssessmentType.EXPRESS;
    }
    
    public List<Question> getAllQuestions()
    {
        return Arrays.asList(Question.SearchByAll().andIsExpressQuestion(new EqualsFilter(isExpressJob())).search(getTransaction()));
    }
    
    public List<Question> getAllLeftQuestions()
    {
        return getAllLeftQuestions(getAllQuestions());
    }
    
    //to get only left Questions
    public List<Question> getAllLeftQuestions(List<Question> totalQuestions)
    {
        Filter  filter  = Question.SearchByAll().andIsRightQuestion(new EqualsFilter<>(Boolean.FALSE));
        
        return  ObjstoreUtils.sort(CollectionFilter.filter(totalQuestions , filter), 
                                new ObjectTransform[] {Question.pipesQuestion().toSection().toObjectID(),Question.pipesQuestion().toInnerSortOrder(),Question.pipesQuestion().toObjectID()},  
                                new Comparator[]{CollectionUtils.DEFAULT_COMPARATOR,CollectionUtils.DEFAULT_COMPARATOR,CollectionUtils.DEFAULT_COMPARATOR});
    }
  
    
    public Collection<CultureCriteria> getCultureCriteriasForClass(CultureClass cultureClass)
    {
        Filter  elementFilter   =   CultureElement.SearchByAll().andCultureClass(new EqualsFilter(cultureClass));
        
        Set<CultureElement> cultureElements =   pipelineJob().toCultureCriterias().toCultureElement(elementFilter).uniqueVals();
        
        Filter  criteriaFilter  =   CultureCriteria.SearchByAll().andCultureElement(new InFilter(cultureElements));
        
        return ObjstoreUtils.sort(pipelineJob().toCultureCriterias(criteriaFilter).vals(), 
                                    new ObjectTransform[]{CultureCriteria.pipesCultureCriteria().toObjectID()},
                                    new Comparator[]{CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST});
    }
    
    
    public int getNoOfCandidatesApplied()
    {
        Filter<JobApplication>  filter =    JobApplication.SearchByAll().andApplicationStatus(new NotInFilter<>(ApplicationStatus.DRAFT));
        
        return pipelineJob().toJobApplications(filter).toCandidate().vals().size();
    }
    
    
    public int getNoOfCandidatesFor(ApplicationStatus... status)
    {
        Filter<JobApplication>  filter =    JobApplication.SearchByAll().andApplicationStatus(new InFilter<>(status));
        
        return pipelineJob().toJobApplications(filter).toCandidate().vals().size();
    }
    

    public Collection<JobApplication> getSubmittedApplications()
    {
        Filter<JobApplication>  filter =    JobApplication.SearchByAll().andApplicationStatus(new NotInFilter<>(ApplicationStatus.DRAFT));
        
        return CollectionFilter.filter(getJobApplicationsSet(), filter);
    }
    
    
    public Collection<JobApplication> getShortlistedApplications()
    {
        return getApplicationsFor(ApplicationStatus.SHORTLISTED);
    }
    
    
    public Collection<JobApplication>   getApplicationsFor(ApplicationStatus... status)
    {
        Filter<JobApplication>  filter =    JobApplication.SearchByAll().andApplicationStatus(new InFilter<>(status));
        
        return CollectionFilter.filter(getJobApplicationsSet(), filter);
    }
    
    
    public int  getNoOfDaystoClosed()
    {
        int dateDiff    =   0;
        
        if(getJobStatus()==JobStatus.OPEN && getApplyBy()!=null)
        {
            dateDiff    =   DateDiff.getDateDiff(Calendar.DATE, DateDiff.getToday(), getApplyBy());
        }
        
        return dateDiff>0 ? dateDiff : 0;
    }
    
    
    public String getClosingInText()
    {
        StringBuilder   sb  =   new StringBuilder("Closing in ");

        if(getApplyBy()!=null)
        {
            int dateDiff    = getNoOfDaystoClosed();

            sb.append(dateDiff);

            if(dateDiff==1)
            {
                sb.append(" day");
            }
            else
            {
                sb.append(" days");
            }
        }
        return sb.toString();
    }
    
    
    public String getJobStatusText()
    {
        StringBuilder   sb  =   new StringBuilder("THIS JOB IS ");
        
        sb.append(getJobStatus().getDescription());
        
        return sb.toString();
    }
    
    
    public AssessmentCriteriaTemplate[] getAssessmentTemplates()
    {
        CompanyUser companyUser =   SecUser.getTXUser(getTransaction()).getExtension(CompanyUser.REFERENCE_CompanyUser);

        return AssessmentCriteriaTemplate.SearchByCompany()
                                        .byCompany(companyUser.getCompany())
                                        .search(getTransaction());
    }
    
    
    public CultureCriteriaTemplate[] getCultureTemplates()
    {
        CompanyUser companyUser =   SecUser.getTXUser(getTransaction()).getExtension(CompanyUser.REFERENCE_CompanyUser);
        
        return CultureCriteriaTemplate.SearchByCompany()
                                        .byCompany(companyUser.getCompany())
                                        .search(getTransaction());
    }

    
    @Override
    public String getToString() 
    {
        return getJobTitle();
    }
    
    
    public Job cloneJob(Job newJob) throws FieldException
    {
        return (Job) BusinessCopyHelper.copyBusinessObject(this, newJob, null, new HashMap(), new AssocCopyingRule()
        {
            @Override
            protected BaseBusinessClass getVersionToReferTo (BaseBusinessClass from, String assocName, BaseBusinessClass to, Map newObjectRefs, CopyType copyType)
            {
                if (newObjectRefs.containsKey (to.getObjectID ()))
                {
                    return (BaseBusinessClass)newObjectRefs.get (to.getObjectID ());
                }
                else if (to instanceof AssessmentCriteria || to instanceof CultureCriteria )
                {
                    /* Create a copy of 'to' */
                    return BusinessCopyHelper.copyBusinessObject (to, copyType, newObjectRefs, this);
                }
                else if (to instanceof JobApplication || to instanceof AssessmentCriteriaAnswer)
                {
                    /* Do not create a copy of 'to' */
                    return null;
                }
                else
                {
                    /* Do not create a copy of 'to' BUT use original object */
                    return to;
                }
            }
        });
    }
    
    public boolean showSaveCultureTemplate()
    {
        return isTrue(getSaveCultureTemplate());
    }
    
    public boolean showSaveAssessmentTemplate()
    {
        return isTrue(getSaveAssessmentTemplate());
    }

    public boolean showAssessmentCriteriaSection()
    {
        return isTrue(getIncludeAssessmentCriteria());
    }
    
    public int getMaxShortlistApplicants()
    {
        return 25;
    }
    
    public Boolean isClosed()
    {
        return getApplyBy() != null && DateDiff.endOfDay(getApplyBy()).before(DateDiff.getToday());
    }
    
    public List<AppProcessOption> getValidProcessOptions(ApplicationStatus appStatus)
    {
        List<AppProcessOption>  options = new ArrayList<>();
        
        if(appStatus == null)
        {
            options.add(AppProcessOption.TO_SHORTLIST);
//            options.add(AppProcessOption.REMOVE_FROM_SHORTLIST);
            options.add(AppProcessOption.TO_UNSUITABLE);
        }
        else if(appStatus == ApplicationStatus.SHORTLISTED)
        {
//            options.add(AppProcessOption.REMOVE_FROM_SHORTLIST);
            options.add(AppProcessOption.TO_UNSUITABLE);
        }
        else if(appStatus == ApplicationStatus.UNSUITABLE)
        {
            options.add(AppProcessOption.TO_SHORTLIST);
            options.add(AppProcessOption.REMOVE_FROM_UNSUITABLE);
        }
        
        
        return options;
    }
    
    public List<ApplicationStatus> getValidApplicationStatuses()
    {
        return new ArrayList<>(pipelineJob().toWorkFlows().toApplicationStatus().uniqueVals());
    }
        
    
    public String getPageTitle()
    {
        StringBuilder   title  = new StringBuilder();
        
        title.append("Talentology - ");

        if(isClientAvailable())
        {
            title.append(getClient()).append(" - ");
        }
        else if(getCreatedBy() != null && getCreatedBy().getCompany() != null)
        {
            title.append(getCreatedBy().getCompany()).append(" - ");
        }
        
        title.append(getJobTitle()).append(" ");
        
        if(getReferenceNumber() != null)
        {
            title.append(getReferenceNumber());
        }
        
        return title.toString();
    }
    
    
    public String getLocation()
    {
        StringBuilder   location    = new StringBuilder();
        
        if(getCity() != null)
        {
            location.append(getCity()).append(", ");
        }
        
        if(getState() != null)
        {
            location.append(getState().getDescription()).append(", ");
        }
        
        if(getCountry() != null)
        {
            location.append(getCountry().getDescription()).append(".");
        }
        
//        if(getRemote() != null && getRemote())
//        {
//            location.append(" (Remote OK)");
//        }
        
        return location.toString(); 
    }
    
    
    public Boolean isEmailFound()
    {
        if(getEmail()!= null)
        {
            SecUser user    = SecUser.searchNAME(getTransaction(), getEmail().toLowerCase());

            if(user!=null && user.getExtension(Candidate.REFERENCE_Candidate)!=null && user.getExtension(Candidate.REFERENCE_Candidate).getIsAccountVerified())
            {
                return Boolean.TRUE;
            }
        }
        
        return Boolean.FALSE;
    }
    
    public Boolean hasHiringTeamAccount()
    {
        if(getEmail()!= null)
        {
            return Utils.emailExists(getTransaction(), getEmail());
        }
        
        return Boolean.FALSE;
    }
    
    
    public Boolean isClientAvailable()
    {
        return getClient() != null && getHiringTeam()!= null && isTrue(getHiringTeam().getHasClientSupport());
    }
    
    
    public BinaryContent getLogo()
    {
        if(isClientAvailable() && getClient().getClientLogo() != null)
        {
            return getClient().getClientLogo();
        }
        else if(getHiringTeam() != null)
        {
            return getHiringTeam().getHiringTeamLogo();
        }
        
        return null;
    }
    
    public String getTeamName()
    {
        if(isClientAvailable())
        {
            return getClient().getClientName();
        }
        else if(getHiringTeam() != null)
        {
            return getHiringTeam().getHiringTeamName();
        }
        
        return "";
    }
    
    
    public List<JobStatus> getAvailableStatus(RoleType roleType)
    {
        List<JobStatus> statusList  =   new ArrayList<>();
        
        if(getJobStatus() != JobStatus.FILLED)
        {
            statusList.add(JobStatus.FILLED);
        }
        
        if(getJobStatus() == JobStatus.OPEN || getJobStatus() == JobStatus.DRAFT)
        {
            if(roleType != RoleType.STANDARD)
            {
                statusList.add(JobStatus.COMPLETE);
            }
        }

        if(getJobStatus() == JobStatus.DRAFT )
        {
            statusList.add(JobStatus.OPEN);
        }
        
        return statusList;
    }
    
    private String getURL()
    {
        return "/ApplicantPortal-ApplyJob.htm?" + "id=" + getID() + "&key=" + getRandomKey();
    }
    
    
    public void createShortenedURL() throws StorageException, FieldException
    {
        ShortenedURL shortenedURL   =   ShortenedURL.createShortenedURL(getTransaction());
            
        shortenedURL.setUrlLink(getURL());
        shortenedURL.setCode(generateUniqueCode());  
        
        setShortenedURL(shortenedURL);
    }
    
    
    private String generateUniqueCode()
    {
        String randomString;
        
        while (true)
        {
            randomString                =   RandomStringGen.getRandomStringGen().generateAlphaNum(6);
            ShortenedURL[] searchByCode =   ShortenedURL.searchByCode(getTransaction(), randomString);

            if (searchByCode.length == 0)
            {
                return randomString;
            }  
        }
    }
    
    
    public String getShortenedUrlLink()
    {
        return LoopbackHTTP.getRemoteAccessURL("/j/" + (getShortenedURL() != null ? getShortenedURL().getCode() : ""));
    }
    
    public String getLevelClass(Level level)
    {
        if(!showLevelOption(level))
        {
            return "hide";
        }
        
        if(CollectionUtils.equals(level, getLevel()))
        {
            return "active";
        }
        return "";
    }
    
    public boolean showLevelOption(Level level)
    {
        if(isExpressJob())
        {
            return isTrue(level.getAppliedToExpress());
        }
        return !isTrue(level.getAppliedToExpress());
    }

    
    public List<WorkFlow> getSortedWorkFlows()
    {
        return ObjstoreUtils.sort(getWorkFlowsSet(), 
                                new ObjectTransform[]{WorkFlow.pipesWorkFlow().toSortOrder()}, 
                                new Comparator[]{CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST});
    }

    public List<WorkFlow> getSortedMiddleWorkFlows()
    {
        Filter<WorkFlow>    filter  =   WorkFlow.SearchByAll().andSortOrder(new LessThanFilter<>(9)).andSortOrder(new GreaterThanFilter<>(1));
        
        return ObjstoreUtils.sort(pipelineJob().toWorkFlows(filter).uniqueVals(), 
                                new ObjectTransform[]{WorkFlow.pipesWorkFlow().toSortOrder()}, 
                                new Comparator[]{CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST});
    }
    
    public WorkFlow getWorkFlowBySortOrder(int sortOrder)
    {
        Filter<WorkFlow>    filter  =   WorkFlow.SearchByAll().andSortOrder(new EqualsFilter<>(sortOrder));
        
        return pipelineJob().toWorkFlows(filter).val();
    } 
    
    public WorkFlow getWorkFlowByStatus(ApplicationStatus appStatus)
    {
        Filter<WorkFlow>    filter  =   WorkFlow.SearchByAll().andApplicationStatus(new EqualsFilter<>(appStatus));
        
        return pipelineJob().toWorkFlows(filter).val();
    } 
    
    public Set<WorkFlow> getIncompleteApplicantWFs()
    {
        Filter<WorkFlow>    filter  =   WorkFlow.SearchByAll().andApplicationStatus(new InFilter<>(ApplicationStatus.DRAFT, ApplicationStatus.UNSUITABLE));
        return pipelineJob().toWorkFlows(filter).uniqueVals();
    }
    
    public Set<WorkFlow> getApplicantWFs()
    {
        Filter<WorkFlow>    filter  =   WorkFlow.SearchByAll().andApplicationStatus(new NotEqualsFilter<>(ApplicationStatus.DRAFT));
        return pipelineJob().toWorkFlows(filter).uniqueVals();
    }
    
    public List<JobApplication> filterEssentialRequirements(boolean meetsRequirements, List<JobApplication> applicantList)
    {
        List<JobApplication> filteredList = new ArrayList();

        for (JobApplication application : applicantList)
        {
            boolean failed  =   application.hasFailedEssentialRequirements();
            
            if((meetsRequirements && !failed) || (!meetsRequirements && failed))
            {
                filteredList.add(application);
            }
        }
        
        return filteredList;
    }

    // templates disabled for sprint 1
    @Override
    public FieldWriteability getWriteability_JobTemplate() 
    {
        return FieldWriteability.NOT_IN_GUI;
    }
}