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);
        }
    }

    
    public void initAttribs() throws BusinessException
    {
        setRandomKey(RandomStringGen.getRandomStringGen().generateAlphaNum(4));
        
        //TODO: verify
        SecUser txUser = SecUser.getTXUser(getTransaction());
        
        setCompanyUser(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.COMPLETE && getApplyBy() != null && getApplyBy().before(DateDiff.getToday()))
        {
            return FieldWriteability.NOT_IN_GUI;
        }
        
        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;
    }
    
    
    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 List<Question> getAllQuestions()
    {
        return Arrays.asList(Question.searchAll(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().toObjectID()},  
                                new 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 pipelineJob().toCultureCriterias(criteriaFilter).vals();
    }
    
    
    public Level[] getAllLevels()
    {     
        List<Level> levels  =   new ArrayList<>();
        
        Utils.getLevelsForComprehensive(getTransaction()).stream().forEach((tuple) -> {
            levels.add((Level)tuple.get0());
        });
        return levels.toArray(new Level[0]);
    }

    
    public int getNoOfCandidatesApplied()
    {
        return getNoOfCandidatesFor(ApplicationStatus.SUBMITTED, ApplicationStatus.SHORTLISTED, ApplicationStatus.UNSUITABLE);
    }
    
    
    public int getNoOfCandidatesShortlisted()
    {
        return getNoOfCandidatesFor(ApplicationStatus.SHORTLISTED);
    }
    
    
    public int getNoOfCandidatesUnsuitable()
    {
        return getNoOfCandidatesFor(ApplicationStatus.UNSUITABLE);
    }
    
    
    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()
    {
        return getApplicationsFor(ApplicationStatus.SUBMITTED, ApplicationStatus.SHORTLISTED, ApplicationStatus.UNSUITABLE);
    }
    
    
    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()
    {
        return AssessmentCriteriaTemplate.SearchByAll()
                                        .andUser(new EqualsFilter<>(SecUser.getTXUser(getTransaction())))
                                        .search(getTransaction());
    }
    
    
    public CultureCriteriaTemplate[] getCultureTemplates()
    {
        return CultureCriteriaTemplate.SearchByAll()
                                        .andUser(new EqualsFilter<>(SecUser.getTXUser(getTransaction())))
                                        .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)
                {
                    /* 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 String getPageTitle()
    {
        StringBuilder   title  = new StringBuilder();
        
        title.append("Talentology - ");

        if(isClientAvailable())
        {
            title.append(getClient()).append(" - ");
        }
        else if(getCompanyUser() != null && getCompanyUser().getCompany() != null)
        {
            title.append(getCompanyUser().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)
            {
                return Boolean.TRUE;
            }
        }
        
        return Boolean.FALSE;
    }
    
    
    public Boolean isClientAvailable()
    {
        return getClient() != null && getCompanyUser() != null && isTrue(getCompanyUser().getCompany().getHasClientSupport());
    }
    
    
    public BinaryContent getLogo()
    {
        if(isClientAvailable())
        {
            return getClient().getClientLogo();
        }
        else if(getCompanyUser() != null)
        {
            return getCompanyUser().getCompany().getCompanyLogo();
        }
        
        return null;
    }
    
    
    public List<JobStatus> getAvailableStatus()
    {
        if(getJobStatus() == JobStatus.OPEN)
        {
            return Arrays.asList(new JobStatus[]{JobStatus.COMPLETE, JobStatus.FILLED});
        }
        else if(getJobStatus() == JobStatus.COMPLETE)
        {
            return Arrays.asList(new JobStatus[]{JobStatus.FILLED});
        }

        return Arrays.asList(new JobStatus[]{JobStatus.OPEN, JobStatus.COMPLETE, JobStatus.FILLED});
    }
    
    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() : ""));
    }
}