package performa.utils;

import java.util.Arrays;
import java.util.Collection;
import oneit.objstore.ObjectTransaction;
import oneit.objstore.StorageException;
import oneit.objstore.rdbms.filters.EqualsFilter;
import oneit.objstore.services.TransactionServices;
import oneit.objstore.services.TransactionTask;
import oneit.utils.NestedException;
import oneit.utils.filter.CollectionFilter;
import oneit.utils.parsers.FieldException;
import performa.orm.Job;
import performa.orm.JobApplication;
import performa.orm.MessageTemplate;
import performa.orm.WorkFlow;
import performa.orm.WorkFlowMessage;
import performa.orm.WorkFlowStage;
import performa.orm.WorkFlowTemplate;
import performa.orm.types.ApplicationStatus;
import performa.orm.types.StageType;


public class WorkflowDataMigration 
{
    public static void main (final String[] args) throws Exception
    {
        TransactionServices.run(new TransactionTask() {
            @Override
            public void run(ObjectTransaction objTran) throws FieldException, StorageException
            {
                try
                {
                    Job[]               jobs        =   Job.searchAll(objTran);
                    MessageTemplate[]   incomplete  =   MessageTemplate.SearchByAll().andApplicationStatus(new EqualsFilter<>(ApplicationStatus.DRAFT)).andIsSystemGenerated(new EqualsFilter<>(Boolean.TRUE)).search(objTran);
                    MessageTemplate[]   postIngest  =   MessageTemplate.SearchByAll().andApplicationStatus(new EqualsFilter<>(ApplicationStatus.POST_INGEST)).andIsSystemGenerated(new EqualsFilter<>(Boolean.TRUE)).search(objTran);
                    
                    for(Job job : jobs)
                    {
                        WorkFlowTemplate    wfTemplate  =   WorkFlowTemplate.createWorkFlowTemplate(objTran);
                        
                        wfTemplate.setTemplateName(job.getJobTitle());
                        wfTemplate.setIsSystemGenerated(Boolean.TRUE);
                        wfTemplate.setHiringTeam(job.getHiringTeam());
                        wfTemplate.addToJobs(job);
                        
                        int sortOrder   =   0;
                        
                        WorkFlowStage   postIngestStage =   createWorkFlowStage(objTran, wfTemplate, "Post Ingest", StageType.POST_INGEST, sortOrder++);
                        
                        for(MessageTemplate mTemplate : (Collection<MessageTemplate>)CollectionFilter.filter(Arrays.asList(postIngest), MessageTemplate.SearchByAll().andMessageID(new EqualsFilter<>(job.getHiringTeam().getMessageID()))))
                        {
                            createWorkFlowMessage(objTran, postIngestStage, mTemplate);
                        }
                        
                        WorkFlowStage   incompleteStage =   createWorkFlowStage(objTran, wfTemplate, "Incomplete", StageType.INCOMPLETE, sortOrder++);
                        
                        for(MessageTemplate mTemplate : (Collection<MessageTemplate>)CollectionFilter.filter(Arrays.asList(incomplete), MessageTemplate.SearchByAll().andMessageID(new EqualsFilter<>(job.getHiringTeam().getMessageID()))))
                        {
                            createWorkFlowMessage(objTran, incompleteStage, mTemplate);
                        }
                        
                        if(job.getDiversityIncluded())
                        {
                            createWorkFlowStage(objTran, wfTemplate, "Diversity", StageType.DIVERSITY, sortOrder++);
                        }
                        
                        if(job.getIncludeAssessmentCriteria())
                        {
                            createWorkFlowStage(objTran, wfTemplate, "Requirements", StageType.REQUIREMENT_FIT, sortOrder++);
                        }
                        
                        if(job.getIncludeCulture())
                        {
                            createWorkFlowStage(objTran, wfTemplate, "Culture Fit", StageType.CULTURE_FIT, sortOrder++);
                        }
                        
                        createWorkFlowStage(objTran, wfTemplate, "Role Fit", StageType.ROLE_FIT, sortOrder++);
                        
                        for(WorkFlow workFlow : job.getWorkFlowsSet())
                        {
                            if(workFlow.getApplicationStatus() == ApplicationStatus.DRAFT || workFlow.getApplicationStatus() == ApplicationStatus.UNSUITABLE)
                            {
                                continue;
                            }
                            
                            createWorkFlowStage(objTran, wfTemplate, workFlow.getName(), getStageTypeByStatus(workFlow.getApplicationStatus()), sortOrder++);
                        }
                        
                        createWorkFlowStage(objTran, wfTemplate, "Successful", StageType.SUCCESSFUL, sortOrder++);
                        
                        createWorkFlowStage(objTran, wfTemplate, "Withdrew", StageType.WITHDREW, sortOrder++);
                        
                        createWorkFlowStage(objTran, wfTemplate, "Unsuitable", StageType.UNSUITABLE, sortOrder++);
                        
                        // TODO:  might have to move to another transaction
                        for(JobApplication jobApplication : job.getJobApplicationsSet())
                        {
                            jobApplication.setWorkFlowStage(getWFStageByStatus(wfTemplate, jobApplication.getApplicationStatus()));
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new NestedException(e);
                }
            }

            private void createWorkFlowMessage(ObjectTransaction objTran, WorkFlowStage stage, MessageTemplate mTemplate) throws StorageException, FieldException 
            {
                WorkFlowMessage wfMessage   =   WorkFlowMessage.createWorkFlowMessage(objTran);
                
                wfMessage.setWorkFlowStage(stage);
                wfMessage.setBusinessHoursOnly(mTemplate.getBusinessHoursOnly());
                wfMessage.setMessageTemplate(mTemplate);
                wfMessage.setDelay(getDelay(mTemplate));
                wfMessage.setVariance(mTemplate.getVariance().toString() + "m");
            }

            private WorkFlowStage createWorkFlowStage(ObjectTransaction objTran, WorkFlowTemplate wfTemplate, String name, StageType stageType, int sortOrder) throws FieldException, StorageException 
            {
                WorkFlowStage   wfStage =   WorkFlowStage.createWorkFlowStage(objTran);
                
                wfStage.setWorkFlowTemplate(wfTemplate);
                wfStage.setName(name);
                wfStage.setStageType(stageType);
                
                return wfStage;
            }
        });
    }

    private static String getDelay(MessageTemplate mTemplate) 
    {
        StringBuilder   sb  =   new StringBuilder();
        
        sb.append((int)(mTemplate.getDelayHrs()/24)).append("d:");
        sb.append(mTemplate.getDelayHrs()%24).append("h:");
        sb.append(mTemplate.getDelayMin()).append("m");
        
        return sb.toString();
    }
      
    public static StageType getStageTypeByStatus(ApplicationStatus status)
    {
        if(status == ApplicationStatus.POST_INGEST)
        {
            return StageType.POST_INGEST;
        }
        else if(status == ApplicationStatus.DRAFT)
        {
            return StageType.INCOMPLETE;
        }
        else if(status == ApplicationStatus.UNSUITABLE)
        {
            return StageType.UNSUITABLE;
        }
        
        // TODO: map other statuses
        return null;
    }
    
    public static WorkFlowStage getWFStageByStatus(WorkFlowTemplate template , ApplicationStatus status)
    {
        if(status == ApplicationStatus.POST_INGEST)
        {
            return getWFStage(template, StageType.POST_INGEST);
        }
        else if(status == ApplicationStatus.DRAFT)
        {
            return getWFStage(template, StageType.INCOMPLETE);
        }
        else if(status == ApplicationStatus.UNSUITABLE)
        {
            return getWFStage(template, StageType.UNSUITABLE);
        }
        
        // TODO: map other statuses
        return null;
    }

    private static WorkFlowStage getWFStage(WorkFlowTemplate template, StageType stageType) 
    {
        return template.pipelineWorkFlowTemplate().toWorkFlowStages(WorkFlowStage.SearchByAll().andStageType(new EqualsFilter<>(stageType))).val();
    }
}