package performa.search;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import oneit.logging.LogLevel;
import oneit.logging.LogMgr;
import oneit.logging.LoggingArea;
import oneit.objstore.*;
import oneit.objstore.rdbms.filters.EqualsFilter;
import oneit.objstore.services.TransactionTask;
import oneit.utils.parsers.FieldException;
import performa.orm.*;
import performa.orm.types.*;


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

    // This constructor should not be called
    public SearchApplicant ()
    {
        // Do not add any code to this, always put it in initialiseNewObject
    }
    
    @Override
    public BaseBusinessClass[] doSearch() 
    {
        final List<JobApplication> appResults = new ArrayList<>();

        try 
        {
            getTransaction().runInNewTX(new TransactionTask()
            {                              
                @Override
                public void run(ObjectTransaction newObjTran) throws FieldException, StorageException
                {
                    JobApplication[]    results     =   JobApplication.SearchByDetails()
                                                                        .byName(getDetails())
                                                                        .andJob(new EqualsFilter<>(getJob()))
                                                                        .search(newObjTran);
                    for(JobApplication app : results)
                    {
                        ApplicationStatus   appStatus   =   app.getApplicationStatus();
                        boolean             skip        =   false;
                        
                        app =   app.getInTransaction(getTransaction());
                        
                        app.setApplicationStatus(appStatus);
                        
                        if(getFilter() != null)
                        {
                            skip = applyFilters(app);
                        }
                        
                        if(skip)
                        {
                            continue;
                        }
                        
                        Answer[]    answers =   Answer.SearchByAll().andCandidate(new EqualsFilter<>(app.getCandidate())).search(getTransaction());

                        for (Answer answer: answers)
                        {
                            app.getCandidate().addToProfileAssessmentAnswers(answer);
                        }

                        AssessmentCriteriaAnswer[]  requirements    =   AssessmentCriteriaAnswer.SearchByAll().andJobApplication(new EqualsFilter<>(app)).search(getTransaction());

                        for (AssessmentCriteriaAnswer requirement: requirements)
                        {
                            app.addToAssessmentCriteriaAnswers(requirement);
                        }

                        CultureCriteriaAnswer[] cultures =   CultureCriteriaAnswer.SearchByAll().andCandidate(new EqualsFilter<>(app.getCandidate())).search(getTransaction());

                        for (CultureCriteriaAnswer culture: cultures)
                        {
                            app.getCandidate().addToCultureCriteriaAnswers(culture);
                        }

                        getJob().addToJobApplications(app);
                        appResults.add(app);
                    }
                }

                private boolean applyFilters(JobApplication app) throws StorageException 
                {
                    Map<AppFilter, List<AppFilter>>  map = createFilterMap();
                    
                    for(AppFilter filter : getFilter())
                    {
                        if(filter.getIsRoot())
                        {
                            if(filter == AppFilter.ATSI || filter == AppFilter.DISABLED)
                            {
                                DiversityQuestion question = app.getJob().getQuestionByFilter(filter);
                                
                                if(question != null)
                                {
                                    CandidateDiversityAnswer    answer      =   app.getCandidate().getDiversityAnswerByQuestion(question);
                                    boolean                     hasAnswer   =   false;
                                    
                                    for(AppFilter child : filter.getChildNodes())
                                    {
                                        if(answer != null && answer.pipelineCandidateDiversityAnswer().toAnswers().toAnswer().toCode().uniqueVals().contains(child.getName()))
                                        {
                                            hasAnswer   =   true;
                                            break;
                                        }
                                    }
                                    
                                    if(answer == null || !hasAnswer)
                                    {
                                        return true;
                                    }
                                }
                            }
                        }
                        else if(filter.getParentNode() != null && filter.getParentNode() == AppFilter.REQ && getJob().showAssessmentCriteriaSection())
                        {
                            if((filter == AppFilter.REQ_MET && !app.hasAllEssentialRequirements()) || (filter == AppFilter.REQ_NOT_MET && !app.hasFailedEssentialRequirements()))
                            {
                                return true;
                            }
                        }
                    }
                    
                    for(Map.Entry<AppFilter, List<AppFilter>> entry :map.entrySet()){  
                        
                        AppFilter       parentNode  =   entry.getKey();
                        List<AppFilter> childList   =   entry.getValue();
                        
                        DiversityQuestion question = app.getJob().getQuestionByFilter(parentNode);
                        
                        if(question != null)
                        {
                            CandidateDiversityAnswer    answer      =   app.getCandidate().getDiversityAnswerByQuestion(question);
                            boolean                     hasAnswer   =   false;

                            for(AppFilter child : childList)
                            {
                                if(answer != null && answer.pipelineCandidateDiversityAnswer().toAnswers().toAnswer().toCode().uniqueVals().contains(child.getName()))
                                {
                                    hasAnswer   =   true;
                                    break;
                                }
                            }

                            if(answer == null || !hasAnswer)
                            {
                                return true;
                            }
                        }
                    }
                    
                    return false;
                }
                
            }); 
        } 
        catch (FieldException | StorageException ex) 
        {
            LogMgr.log(LOG, LogLevel.PROCESSING2 , ex, "Error when searching job applications");

        }
        
        return appResults.toArray(new JobApplication[0]);
    }
    
    private Map<AppFilter, List<AppFilter>>    createFilterMap()
    {
        Map<AppFilter, List<AppFilter>>  map = new HashMap<>();
                            
        for(AppFilter filter : getFilter())
        {
            if(filter.getParentNode() != null && filter.getParentNode() != AppFilter.REQ)
            {
                if (!map.containsKey(filter.getParentNode())) 
                {
                    map.put(filter.getParentNode(), new ArrayList<>());
                }

                map.get(filter.getParentNode()).add(filter);
            }
        }
        
        return map;
    }
    
}