package performa.orm;

import java.util.*;
import oneit.logging.LoggingArea;
import oneit.objstore.StorageException;
import oneit.objstore.rdbms.filters.EqualsFilter;
import oneit.objstore.rdbms.filters.IsNotNullFilter;
import oneit.objstore.rdbms.filters.NotEqualsFilter;
import oneit.objstore.utils.ObjstoreUtils;
import oneit.utils.CollectionUtils;
import oneit.utils.ObjectTransform;
import oneit.utils.filter.CollectionFilter;
import oneit.utils.filter.Filter;
import oneit.utils.math.NullArith;
import oneit.utils.math.Rounding;
import oneit.utils.parsers.FieldException;
import performa.orm.types.*;
import performa.utils.AnalysisEngine;


public class JobApplication extends BaseJobApplication
{
    private static final long   serialVersionUID    = 0L;
    public  static LoggingArea  LOG                 = LoggingArea.createLoggingArea("JobApplication");
    
    // This constructor should not be called
    public JobApplication ()
    {
        // Do not add any code to this, always put it in initialiseNewObject
    }

    
    public static JobApplication createNewApplication(Candidate candidate, Job job) throws StorageException, FieldException
    {
        JobApplication  jobApplication  =   createJobApplication(job.getTransaction());
        
        jobApplication.setCandidate(candidate);
        jobApplication.setJob(job);
        
        return jobApplication;
    }
    
    
//    private void initACAnswers()
//    {
//        if(isIncludeAssessmentCriteria())
//        {
//            for(AssessmentCriteria assessmentCriteria : getJob().getAssessmentCriteriasSet())
//            {
//                AssessmentCriteriaAnswer    answer  =   AssessmentCriteriaAnswer.createAssessmentCriteriaAnswer(getTransaction());
//
//                addToAssessmentCriteriaAnswers(answer);
//                assessmentCriteria.addToAnswers(answer);
//            }
//        }
//    }
    
    
    public boolean createAssessmentCriteriaObjects() throws FieldException
    {
        boolean safeRedirect = false;

        if(isIncludeAssessmentCriteria())
        {
            for(AssessmentCriteria assessmentCriteria : getJob().getAssessmentCriteriasSet())
            {
                boolean available   = false;
                
                for(AssessmentCriteriaAnswer answer : getAssessmentCriteriaAnswersSet())
                {
                    if(assessmentCriteria.equals(answer.getAssessmentCriteria()))
                    {
                        available = true;
                        break;
                    }
                }
                
                if(!available)
                {
                    AssessmentCriteriaAnswer    answer  =   AssessmentCriteriaAnswer.createAssessmentCriteriaAnswer(getTransaction());

                    addToAssessmentCriteriaAnswers(answer);
                    assessmentCriteria.addToAnswers(answer);
                    safeRedirect = true;
                }
            }
        }
        
        return safeRedirect;    
    }
    
    
    public boolean createCultureCriteriaObjects() throws FieldException
    {
        boolean safeRedirect = false;
        
        //to skip culture test
        if(getAlreadyCompletedApplicationForCulture()!=null)
        {
            return safeRedirect;
        }
        
        for(CultureCriteria cultureCriteria : getJob().getCultureCriteriasSet())
        {
            boolean available   = false;

            for(CultureCriteriaAnswer answer : getCultureCriteriaAnswersSet())
            {
                if(cultureCriteria.equals(answer.getCultureCriteria()))
                {
                    available = true;
                    break;
                }
            }

            if(!available)
            {
                CultureCriteriaAnswer   answer  =   CultureCriteriaAnswer.createCultureCriteriaAnswer(getTransaction());

                addToCultureCriteriaAnswers(answer);
                cultureCriteria.addToAnswers(answer);
                safeRedirect = true;
            }
        }
        
        return safeRedirect;    
    }

    public List<CultureCriteriaAnswer> getSortedCultureCriteriaAnswers()
    {
        return  ObjstoreUtils.sort(getCultureCriteriaAnswersSet(), 
                                new ObjectTransform[] {CultureCriteriaAnswer.pipesCultureCriteriaAnswer().toObjectID()},  
                                new Comparator[]{CollectionUtils.DEFAULT_COMPARATOR});
    }
    
    public boolean initAssessmentAnswers(List<Question> questions) throws StorageException, FieldException
    {
        boolean         redirect        =   false;
        List<Question>  allQuestions    =   new ArrayList<>();
        
        for(Question question : questions)
        {
            allQuestions.add(question);
            
            if(question.getRightQuestion() != null)
            {
                allQuestions.add(question.getRightQuestion());
            }
        }
        
        for(Question question : allQuestions)            
        {
            boolean available   = false;

            for(Answer answer : getProfileAssessmentAnswersSet())
            {
                if(question.equals(answer.getQuestion()))
                {
                    available = true;
                    break;
                }
            }

            if(!available)
            {
                Answer  answer  =   Answer.createAnswer(getTransaction());
            
                addToProfileAssessmentAnswers(answer);
                answer.setQuestion(question);                
                redirect    =   true;
            }
        } 
        
        return redirect;
    }
  
    public Answer getAnswerForQuestion(Question question) throws FieldException
    {
        Filter  filter  =   Answer.SearchByAll().andQuestion(new EqualsFilter<>(question));
        
        return CollectionFilter.getFirstMatch(getProfileAssessmentAnswersSet(), filter);
    }
    
    
    public Collection<AssessmentCriteriaAnswer> getACAnswersByType(CriteriaType criteriaType)
    {
        Filter  filter  =   AssessmentCriteriaAnswer.SearchByCriteriaType().byCriteriaType(criteriaType);

        return  CollectionFilter.filter(getAssessmentCriteriaAnswersSet(), filter);
    }

    
    public boolean selectionCompleted() //req   
    {
        if(getJob()!=null && getJob().getIncludeAssessmentCriteria()!=Boolean.TRUE)
        {
            return Boolean.TRUE;
        }
        
        int allAnswersCount =   getAssessmentCriteriaAnswersCount();
        
        if(allAnswersCount == getJob().getAssessmentCriteriasCount())
        {
            Filter      filter          =   AssessmentCriteriaAnswer.SearchByAll().andAnswer(new IsNotNullFilter<>());
            Collection  selectedAnswers =   pipelineJobApplication().toAssessmentCriteriaAnswers(filter).vals();

            return (selectedAnswers.size() == allAnswersCount);
        }
        
        return false;
    }
       
    
    public boolean cultureCompleted()
    {
        int allAnswersCount =   getCultureCriteriaAnswersCount();
        
        if(allAnswersCount == getJob().getCultureCriteriasCount())
        {
            Filter      filter          =   CultureCriteriaAnswer.SearchByAll().andSelectedQuestion(new IsNotNullFilter<>());
            Collection  selectedAnswers =   pipelineJobApplication().toCultureCriteriaAnswers(filter).vals();

            return (selectedAnswers.size() == allAnswersCount);
        }
        
        return false;
    }
    
    
    public boolean assessmentCompleted() //role
    {
        int allAnswersCount = getJob().getAllLeftQuestions(Arrays.asList(Question.searchAll(getTransaction()))).size();
        
        if(allAnswersCount > 0)
        {
            Filter      filter          =   Answer.SearchByAll().andAnswerNo(new IsNotNullFilter<>());
            Collection  selectedAnswers =   pipelineJobApplication().toProfileAssessmentAnswers(filter).vals();
            
            return (selectedAnswers.size() >= allAnswersCount);
        }
        
        return false;
    }
    
    
    public int getActualQuestionNumber()
    {
        return pipelineJobApplication().toProfileAssessmentAnswers().toQuestion().toRightQuestion().uniqueVals().size() + pipelineJobApplication().toProfileAssessmentAnswers().uniqueVals().size() + 1;
    }
    
    
    public boolean isIncludeAssessmentCriteria()
    {
        return getJob() != null && isTrue(getJob().getIncludeAssessmentCriteria());
    }
    
//    TODO: Fix these hard coded values
    @Override
    public Integer getOverallSuitability()
    {
        return 1;
    }
    
    
    public Double getJobMatchPercentage() //ROLE
    {
        return AnalysisEngine.getRoleFitSuitability(getCandidate(), getJob().getLevel()).get(null);
    }
    
    
    public int getCulturePercentage()
    {
        return AnalysisEngine.getCultureFit(getAlreadyCompletedApplicationForCulture()!=null ? getAlreadyCompletedApplicationForCulture().getCultureCriteriaAnswersSet() : getCultureCriteriaAnswersSet()).get(null).intValue();
    }
    
    
    public int getCriteriaPercentage () //REQ
    {
        return AnalysisEngine.getRequirementFit(getAssessmentCriteriaAnswersSet()).get(null).intValue();
    }
    
    
    public List<AppProcessOption> getValidProcessOptions()
    {
        List<AppProcessOption>  options = new ArrayList<>();
        
        if(getApplicationStatus() == ApplicationStatus.SUBMITTED)
        {
            options.add(AppProcessOption.TO_SHORTLIST);
        }
        else if(getApplicationStatus() == ApplicationStatus.SHORTLISTED)
        {
            options.add(AppProcessOption.REMOVE_FROM_SHORTLIST);
        }
        
        return options;
    }
    
    
    public String getApplicantStatusStr()
    {
        return getApplicationStatus().getDescription();
    }
    
    
    public List<ApplicationStatus> getAvailableStatuses()
    {
        List<ApplicationStatus>  statuses = new ArrayList<>();
        
        statuses.add(ApplicationStatus.SUBMITTED);
        statuses.add(ApplicationStatus.SHORTLISTED);
        statuses.add(ApplicationStatus.UNSUITABLE);
        
        return statuses;
    }
        
    
    public Collection<JobApplication> getAllSubmittedApplicationsByCandidate()
    {
        Filter  filter  =   JobApplication.SearchByAll().andApplicationStatus(new NotEqualsFilter<>(ApplicationStatus.DRAFT));
        
        return  CollectionFilter.filter(getCandidate().getJobApplicationsSet(), filter);
    }
    
    
    public JobApplication getAlreadyCompletedApplicationForCulture()
    {
        for(JobApplication application: getAllSubmittedApplicationsByCandidate())
        {
            if(CollectionUtils.equals(application, this))
            {
                continue;
            }
            
            if(application.cultureCompleted())
            {
                return application;
            }
        }
        
        return null;
    }
    
    
    public JobApplication getAlreadyCompletedApplicationForRole()
    {
        for(JobApplication application: getAllSubmittedApplicationsByCandidate())
        {
            if(CollectionUtils.equals(application, this))
            {
                continue;
            }
            
            if(application.assessmentCompleted())
            {
                return application;
            }
        }
        
        return null;
    }
    
    public Set<Answer> getCompletedAnswers()
    {
        return getAlreadyCompletedApplicationForRole() != null ? getAlreadyCompletedApplicationForRole().getProfileAssessmentAnswersSet() : getProfileAssessmentAnswersSet();
    }
    
    public Set<FactorClass> getSortedFactorClasses()
    {
        Set<FactorClass>    factorClasses       =   new LinkedHashSet<>();
        Set<FactorClass>    levelFactorClasses  =   pipelineJobApplication().toJob().toLevel().toLevelClassCriterias().toFactorClass().uniqueVals();
        
        for(FactorClass factorClass : FactorClass.getFactorClassArray())    //getFactorClassArray returns Factor Classes sorted by SortOrder
        {
            if(levelFactorClasses.contains(factorClass))
            {
                factorClasses.add(factorClass);
            }
        }
        return factorClasses;
    }
    
    
    //to get pending time to complete job application
    public Integer  getRemainingTime()
    {
        //req-07 min
        //cul-07 min
        //req-26 min
        
        int remainingTime   =   0;
        
        //REQ
        if(getJob()!=null && getJob().getIncludeAssessmentCriteria()==Boolean.TRUE)
        {
            if(!selectionCompleted())
            {
                remainingTime   +=  7;
            }
        }
        
        if(!cultureCompleted() && getAlreadyCompletedApplicationForCulture()==null)
        {
            remainingTime   +=  7;
        }
        
        //ROLE
        remainingTime   +=  getRoleTestRemainingTime() ;
        
        if(!assessmentCompleted() && getAlreadyCompletedApplicationForRole()==null)
        {
            remainingTime   += 6;
        }
        
        return remainingTime;
    }
    
    
    //to get remaining time for role test
    public Integer getRoleTestRemainingTime()
    {
        int remainingTime   =   20;
        
        if(assessmentCompleted() || getAlreadyCompletedApplicationForRole()!=null)
        {
            remainingTime   -=  20;
        }
        else //partially completed test for role
        {
            int allAnswersCount =   getJob().getAllLeftQuestions(Arrays.asList(Question.searchAll(getTransaction()))).size(); 

            if(allAnswersCount > 0)
            {
                Filter      filter          =   Answer.SearchByAll().andAnswerNo(new IsNotNullFilter<>());
                Collection  selectedAnswers =   pipelineJobApplication().toProfileAssessmentAnswers(filter).vals();

                if(selectedAnswers!=null && selectedAnswers.size()>0)
                {
                    Double  answerrate  =   NullArith.divide(selectedAnswers.size(), allAnswersCount);
                    
                    remainingTime   -=  Rounding.roundDouble( NullArith.multiply(answerrate, 20), 0);
                }
            }
        }
        
        return remainingTime;
    }
}