package performa.utils;

import java.util.*;
import javax.activation.DataSource;
import javax.servlet.http.*;
import oneit.appservices.config.ConfigMgr;
import oneit.business.content.Article;
import oneit.email.*;
import oneit.logging.LogLevel;
import oneit.logging.LogMgr;
import oneit.logging.LoggingArea;
import oneit.net.LoopbackHTTP;
import oneit.objstore.BaseBusinessClass;
import oneit.objstore.ObjectTransaction;
import oneit.objstore.rdbms.filters.EqualsFilter;
import oneit.objstore.rdbms.filters.GreaterThanEqualFilter;
import oneit.objstore.rdbms.filters.LessThanFilter;
import oneit.security.*;
import oneit.utils.Tuple;
import performa.orm.*;
import performa.orm.types.Importance;
import performa.orm.types.JobSortOption;
import oneit.objstore.utils.*;
import oneit.security.jsp.PasswordDIHandler;
import oneit.servlets.forms.RedirectResult;
import oneit.servlets.forms.SubmissionDetails;
import oneit.servlets.forms.SuccessfulResult;
import oneit.servlets.orm.DataMap;
import oneit.servlets.process.ORMProcessState;
import oneit.servlets.security.SessionSecUserDecorator;
import oneit.utils.*;
import oneit.utils.filter.CollectionFilter;
import oneit.utils.filter.Filter;
import oneit.utils.transform.MapTransform;
import oneit.utils.transform.param.ErrorTransform;
import oneit.utils.transform.param.ORMTransform;
import oneit.utils.transform.param.PrefixCompoundTransform;
import performa.orm.types.AppSortOption;
import performa.orm.types.ClientSortOption;
import performa.orm.types.JobStatus;
import performa.orm.types.UserSortOption;

/**
 *
 * @author Harsh
 */
public class Utils
{
    public static final String  ROLE_APPLICANT                  =   "TL_Applicant";
    public static final String  ROLE_CLIENT                     =   "TL_Client";
    public static final String  ROLE_COMPANY_ADMIN              =   "TL_CompanyAdmin";
    public static final String  ROLE_COMPANY_USER               =   "TL_CompanyUser";
    public static final String  PRIV_ACCESS_ADMIN_PORTAL        =   "TL_AccessAdminPortal";
    public static final String  PRIV_ACCESS_APPLICANT_PORTAL    =   "TL_AccessApplicantPortal";
    public static final String  PRIV_ACCESS_COMPANY_ADMIN       =   "TL_AccessCompanyAdmin";
    public static final String  PRIV_ACCESS_COMPANY             =   "TL_AccessCompany";
    public static final String  LEVEL_GENERAL_PURPOSE           =   "General Purpose";
    public static final String  LEVEL_SALES                     =   "Sales";
    public static final String  LEVEL_MANAGEMENT                =   "Management";
    public static final String  LEVEL_EXECUTIVE                 =   "Executive";
    public static final String  HEAP_ANALYSIS_ID                = 	ConfigMgr.getKeyfileString("heap.analysis.id", "3411110378");

    public static Role  getRole(String role, ObjectTransaction transaction)
    {
        return Role.SearchByNAME().byName(role).search(transaction);
    }
    
    public static Privilege  getPrivilege(String priv, ObjectTransaction transaction)
    {
        return Privilege.searchNAME(transaction, priv);
    }
    
    public static boolean checkAdminPortalAccess(SecUser secUser)
    {
        return (secUser != null && secUser.hasPrivilege(PRIV_ACCESS_ADMIN_PORTAL));
    }
    
    public static boolean checkApplicantPortalAccess(SecUser secUser)
    {
        return (secUser != null && secUser.hasPrivilege(PRIV_ACCESS_APPLICANT_PORTAL));
    }
    
    public static Candidate getCandidateUser (ObjectTransaction objTran)
    {
        return  SecUser.getTXUser(objTran).getExtension(Candidate.REFERENCE_Candidate);
    }
    
    public static Importance[] getImportancesForAssessment()
    {
        List<Importance>    importances =   new ArrayList<>();
        
        for(Importance importance : Importance.getImportanceArray())
        {
            if(importance.getConsiderForAssessment())
            {
                importances.add(importance);
            }
        }
        return importances.toArray(new Importance[0]);
    }
    
    // TODO: Levels hardcoded for this phase, needs to be changed later!
    public static List<Tuple.T3> getLevelsForComprehensive(ObjectTransaction objTran)
    {
        List<Tuple.T3>  levels  =   new ArrayList();
        
        levels.add(new Tuple.T3(Level.searchName(objTran, LEVEL_GENERAL_PURPOSE), "General Purpose", "gn-pu"));
        levels.add(new Tuple.T3(Level.searchName(objTran, LEVEL_SALES), "Sales", "sales-pic"));
        levels.add(new Tuple.T3(Level.searchName(objTran, LEVEL_MANAGEMENT), "Management", "manag-pic"));
        levels.add(new Tuple.T3(Level.searchName(objTran, LEVEL_EXECUTIVE), "Executive", "exe-pic"));
        
        return levels;
    }
    
    
    //to sort jobs list
    public static List<Job> getJobsSorted(Job[] jobs, JobSortOption jobSortOption, JobStatus jobStatus)
    {
        ObjectTransform transform   =   Job.pipesJob().toObjectCreated();
        Comparator      comparator  =   CollectionUtils.DEFAULT_COMPARATOR;
        
        if(jobSortOption == JobSortOption.OLDEST || jobSortOption == JobSortOption.NEWEST)
        {
            if(jobStatus == null)
            {
                transform   =   Job.pipesJob().toObjectCreated();
            }
            else if(jobStatus == JobStatus.OPEN)
            {
                transform   =   Job.pipesJob().toOpenDate();
            }
            else
            {
                transform   =   Job.pipesJob().toLastStatusChangeDate();
            }
            
            comparator  =   jobSortOption == JobSortOption.OLDEST  ? 
                                CollectionUtils.DEFAULT_COMPARATOR : 
                                CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR);
        }
        else if(jobSortOption == JobSortOption.CLOSING_SOON)
        {
            transform   =   Job.pipesJob().toApplyBy();
            comparator  =   CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST;
        }
        else if(jobSortOption == JobSortOption.ALPHA_A_Z)
        {
            transform   =   Job.pipesJob().toJobTitle();
            comparator  =   CollectionUtils.CASE_INSENSITIVE_COMPARATOR;
        }
        else if(jobSortOption == JobSortOption.ALPHA_Z_A)
        {
            transform   =   Job.pipesJob().toJobTitle();
            comparator  =   CollectionUtils.reverse(CollectionUtils.CASE_INSENSITIVE_COMPARATOR);
        }
        
        return ObjstoreUtils.sort( Arrays.asList(jobs), 
                        new ObjectTransform[]{transform},
                        new Comparator[]{comparator});
    }
    

    public static List<Client> getClientsSorted(Client[] clients, ClientSortOption clientSortOption)
    {
        ObjectTransform transform   =   Client.pipesClient().toObjectCreated();
        Comparator      comparator  =   CollectionUtils.DEFAULT_COMPARATOR;
        
        if(clientSortOption == ClientSortOption.ALPHA_A_Z)
        {
            transform   =   Client.pipesClient().toClientName();
            comparator  =   CollectionUtils.CASE_INSENSITIVE_COMPARATOR;
        }
        else if(clientSortOption == ClientSortOption.ALPHA_Z_A)
        {
            transform   =   Client.pipesClient().toClientName();
            comparator  =   CollectionUtils.reverse(CollectionUtils.CASE_INSENSITIVE_COMPARATOR);
        }
        else if(clientSortOption == ClientSortOption.OPEN_JOBS)
        {
            transform   =   ClientToOpenJobCountTransform.INSTANCE;
            comparator  =   CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR);
        }
        else if(clientSortOption == ClientSortOption.FILLED_JOBS)
        {
            transform   =   ClientToFilledJobCountTransform.INSTANCE;
            comparator  =   CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR);
        }
        
        return ObjstoreUtils.sort( Arrays.asList(clients), 
                        new ObjectTransform[]{transform},
                        new Comparator[]{comparator});
    }
    
    
    public static Collection<CompanyUser> getUsersSorted(Collection<CompanyUser> users, UserSortOption userSortOption)
    {
        Comparator      comparator  =   new UserComparator();
        
        if(userSortOption == UserSortOption.ALPHA_A_Z)
        {
            comparator  =   new UserComparator();
        }
        else if(userSortOption == UserSortOption.ALPHA_Z_A)
        {
            comparator  =   CollectionUtils.reverse(new UserComparator());
        }
        
        return CollectionUtils.sort(users, comparator);
    }

    
    
    public static class ClientToOpenJobCountTransform implements ObjectTransform<Client, Integer>
    {
        public static ClientToOpenJobCountTransform INSTANCE = new ClientToOpenJobCountTransform();
        
        @Override
        public Integer transform(Client client)
        {
            return client.getApplicationCountByStatus(JobStatus.OPEN);
        }
    }
    
        
    public static class ClientToFilledJobCountTransform implements ObjectTransform<Client, Integer>
    {
        public static ClientToFilledJobCountTransform INSTANCE = new ClientToFilledJobCountTransform();
        
        @Override
        public Integer transform(Client client)
        {
            return client.getApplicationCountByStatus(JobStatus.FILLED);
        }
    }
    
    
    public static int getClosingSoonJobCount(Job[] jobs)
    {
        Filter<Job> filter          =   Job.SearchByAll()
                                            .andApplyBy(new LessThanFilter<>(DateDiff.add(DateDiff.getToday(), Calendar.DATE, 5)))
                                            .andApplyBy(new GreaterThanEqualFilter<>(DateDiff.getToday()));
        
        Object[]    closingSoonJobs = CollectionFilter.filterArray(jobs, filter);
        
        return closingSoonJobs.length;
    }
    
    //to sort application list
    public static List<JobApplication> getApplicationsSorted(JobApplication[] applications, AppSortOption appSortOption)
    {
        ObjectTransform transform   =   JobApplication.pipesJobApplication().toObjectCreated();
        Comparator      comparator  =   CollectionUtils.DEFAULT_COMPARATOR;
        
        if(appSortOption==AppSortOption.OLDEST)
        {
            transform   =   JobApplication.pipesJobApplication().toSubmittedDate();
            comparator  =   CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST;
        }
        else if(appSortOption==AppSortOption.NEWEST)
        {
            transform   =   JobApplication.pipesJobApplication().toSubmittedDate();
            comparator  =   CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST);
        }
        else if(appSortOption==AppSortOption.RANK) 
        {
            transform   =   JobApplication.pipesJobApplication().toOverallRank();
            comparator  =   CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST;
        }
        else if(appSortOption==AppSortOption.ALPHA_A_Z)
        {
            transform   =   JobApplication.pipesJobApplication().toCandidate().toUser().toFirstName();
            comparator  =   CollectionUtils.CASE_INSENSITIVE_COMPARATOR;
        }
        else if(appSortOption==AppSortOption.ALPHA_Z_A)
        {
            transform   =   JobApplication.pipesJobApplication().toCandidate().toUser().toFirstName();
            comparator  =   CollectionUtils.reverse(CollectionUtils.CASE_INSENSITIVE_COMPARATOR);
        }
        
        return ObjstoreUtils.sort( Arrays.asList(applications), 
                                new ObjectTransform[]{transform},
                                new Comparator[]{comparator});
    }
    
    
    public static ObjectTransform createCompoundTransform(Map defaultTransMap, BaseBusinessClass... bbcs)
    {
        PrefixCompoundTransform prefixTransform =   new PrefixCompoundTransform();
        
        if(defaultTransMap != null)
        {
            prefixTransform.setDefault(new MapTransform(defaultTransMap));
        }
        
        for(BaseBusinessClass bbc : bbcs)
        {
            if(bbc != null)
            {
                prefixTransform.add(bbc.getClass().getSimpleName(), new ORMTransform(bbc));
            }
        }
        
        return new StringUtils.NullToBlankPostTransform(new ErrorTransform(prefixTransform, ""));
    }
    
    
    public static void sendMail(ConfigurableEmailer emailer, ObjectTransform finalTransform, String[] emails, DataSource[] attachments, BaseBusinessClass bo) throws ConfigurableEmailerException
    {
        LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, "Sending Mail from Utils class to" + Arrays.toString(emails));
        
        emailer.sendTransactionalEmail(bo.getTransaction(), 
                new Date(), 
                finalTransform, 
                emails, 
                null, 
                null, 
                null, 
                null, 
                null, 
                null, 
                attachments, 
                bo.getClass().getName(), 
                bo.getObjectID());
        
        LogMgr.log(LoggingArea.ALL, LogLevel.PROCESSING1, "Mail sent from Utils class to " + Arrays.toString(emails));
    }
    
    
    public static String getPwdKeyOfSecUser(HttpServletRequest request, SecUser user, boolean mandatoryPwd)
    {
        if(user != null)
        {
            DataMap dm  =   DataMap.getDataMap(request, true);
            
            return dm.storeORMHandler(new PasswordDIHandler(user, "md5:" + SecUser.FIELD_Password, mandatoryPwd), user, "md5:" + SecUser.FIELD_Password);
        }
        
        return "";
    }
    
        
    public static SuccessfulResult processSuccessfulLogin(ORMProcessState process, SubmissionDetails submission, Map params, SecUser user) throws BusinessException
    {
        HttpServletRequest  request =   submission.getRequest();
        
        request.getSession().setAttribute (SecUser.SEC_USER_ID, user);
        request.getSession().setAttribute (SessionSecUserDecorator.REFRESH_SECURITY, Boolean.TRUE);
        
        process.completeAndRestart();
        
        return new RedirectResult((String) request.getAttribute("nextPage"), null);
    }
    
    
    public static Client[] getClientsByCompany(ObjectTransaction transaction)
    {
        SecUser     secUser     =   SecUser.getTXUser(transaction);
        CompanyUser companyUser =   secUser.getExtension(CompanyUser.REFERENCE_CompanyUser);
        Company     company     =   companyUser.getCompany();
        
        return Client.SearchByAll()
                    .andCompany(new EqualsFilter<>(company))
                    .search(transaction);
    }
    
    
    public static Boolean isCompanyUserEmailFound(ObjectTransaction objTran, String email)
    {
        if(email!=null)
        {
            SecUser user    = SecUser.searchNAME(objTran, email.toLowerCase());
            
            if(user!=null && user.getExtension(CompanyUser.REFERENCE_CompanyUser)!=null)
            {
                return Boolean.TRUE;
            }
        }
        
        return Boolean.FALSE;
    }
    
    
    public static SecUser getSecUserForCompanyIfAvailable(CompanyUser companyUser)
    {
        SecUser     secUser             =   companyUser.getUser();
        SecUser     availableSecUser    =   SecUser.searchNAME(companyUser.getTransaction(), secUser.getEmail());

        if(availableSecUser!=null)
        {
            if(availableSecUser.getExtension(CompanyUser.REFERENCE_CompanyUser)==null)
            {

                availableSecUser.addToExtensions(companyUser);

                //remove new sec user obj
                secUser.removeFromExtensions(companyUser);
                secUser.delete();

                return availableSecUser;
            }
        }
        
        return secUser;
    }
    
    
    public static void sendVerificationMail(CompanyUser companyUser, HttpServletRequest request, ConfigurableArticleTemplateEmailer  emailer, String callingClass) throws BusinessException
    {
        if(!CollectionUtils.equals(companyUser.getIsAccountVerified(), Boolean.TRUE))
        {
            try
            {
                LogMgr.log(CompanyUser.LOG, LogLevel.PROCESSING1, "Sending verification mail from  " + callingClass + " to :: ", companyUser);

                Article         verificationArticle =   WebUtils.getArticleByShortCut(companyUser.getTransaction(), WebUtils.COMPANY_ACCOUNT_VERIFICATION);
                RandomStringGen random              =   new RandomStringGen();

                //set verification key and send mail time
                companyUser.setVerificationKey(random.generateAlphaNum(6));
                companyUser.setVerificationMailSendDate(new Date());

                String          link                =   LoopbackHTTP.getRemoteAccessURL(request) 
                                                            + verificationArticle.getLink(request, CollectionUtils.EMPTY_MAP, "/") 
                                                            + "?id=" + companyUser.getID()
                                                            + "&key=" + companyUser.getVerificationKey();
                Map             defaultParams       =   CollectionUtils.mapEntry("link", link).toMap(); 
                ObjectTransform transform           =   Utils.createCompoundTransform(defaultParams, companyUser);

                Utils.sendMail(emailer, transform, new String[]{companyUser.getUser().getEmail()}, null, companyUser);

                LogMgr.log(CompanyUser.LOG, LogLevel.PROCESSING1, "Sent verification mail successfully from " + callingClass + " to :: ", companyUser);
            }
            catch (ConfigurableEmailerException ex)
            {
                LogMgr.log(CompanyUser.LOG, LogLevel.SYSTEMERROR1, ex, "Error occured while sending mail for Candidate :: " + companyUser);

                throw new BusinessException("We are unable to send mail. Please try again or contact Talentology for more details.");
            }
        }
        else
        {
            LogMgr.log(CompanyUser.LOG, LogLevel.PROCESSING1, "Call from " + callingClass + ". Account is already verified  for candidate :: ", companyUser);
        }
    }
}
    
    
class UserComparator implements Comparator<CompanyUser>
{
    @Override
    public int compare(CompanyUser o1, CompanyUser o2) 
    {
        String  o1Name  =   StringUtils.subBlanks(o1.getUser().getName())!=null ? o1.getUser().getName() : o1.getUser().getUserName();
        String  o2Name  =   StringUtils.subBlanks(o2.getUser().getName())!=null ? o2.getUser().getName() : o2.getUser().getUserName();
        
        return o1Name.trim().compareTo(o2Name.trim());
    }
}
    