package performa.utils;

import java.util.*;
import javax.activation.DataSource;
import javax.servlet.http.HttpServletRequest;
import oneit.appservices.config.ConfigMgr;
import oneit.business.content.Article;
import oneit.email.*;
import oneit.logging.*;
import oneit.net.LoopbackHTTP;
import oneit.objstore.*;
import oneit.objstore.rdbms.filters.*;
import oneit.objstore.utils.ObjstoreUtils;
import oneit.security.*;
import oneit.security.jsp.*;
import oneit.security.oauth.form.*;
import oneit.servlets.forms.*;
import oneit.servlets.orm.DataMap;
import oneit.servlets.process.ORMProcessState;
import oneit.servlets.security.SessionSecUserDecorator;
import oneit.utils.*;
import oneit.utils.filter.*;
import oneit.utils.transform.MapTransform;
import oneit.utils.transform.param.*;
import performa.form.LinkedInOAuthLoginFP;
import performa.orm.*;
import performa.orm.types.*;

/**
 *
 * @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 final String  INTERCOM_APP_ID                = 	ConfigMgr.getKeyfileString("intercom.appId", "");
    public static final String  INTERCOM_TOKEN                = 	ConfigMgr.getKeyfileString("intercom.token", "");

    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);
            }
        }
        
        Collections.sort(importances, (a, b) -> b.getWeightingScore().compareTo(a.getWeightingScore()));
       
        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_NULLS_FIRST;
        
        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_NULLS_FIRST : 
                                CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST);
        }
        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_NULLS_FIRST;
        }
        else if(jobSortOption == JobSortOption.ALPHA_Z_A)
        {
            transform   =   Job.pipesJob().toJobTitle();
            comparator  =   CollectionUtils.reverse(CollectionUtils.CASE_INSENSITIVE_COMPARATOR_NULLS_FIRST);
        }
        
        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_NULLS_FIRST;
        
        if(clientSortOption == ClientSortOption.ALPHA_A_Z)
        {
            transform   =   Client.pipesClient().toClientName();
            comparator  =   CollectionUtils.CASE_INSENSITIVE_COMPARATOR_NULLS_FIRST;
        }
        else if(clientSortOption == ClientSortOption.ALPHA_Z_A)
        {
            transform   =   Client.pipesClient().toClientName();
            comparator  =   CollectionUtils.reverse(CollectionUtils.CASE_INSENSITIVE_COMPARATOR_NULLS_FIRST);
        }
        else if(clientSortOption == ClientSortOption.OPEN_JOBS)
        {
            transform   =   new ClientJobCountTransform(JobStatus.OPEN);
            comparator  =   CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST);
        }
        else if(clientSortOption == ClientSortOption.FILLED_JOBS)
        {
            transform   =   new ClientJobCountTransform(JobStatus.FILLED);
            comparator  =   CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST);
        }
        
        return ObjstoreUtils.sort( Arrays.asList(clients), new ObjectTransform[]{transform}, new Comparator[]{comparator});
    }
    
    
    public static Collection<CompanyUser> getUsersSorted(Collection<CompanyUser> users, UserSortOption userSortOption)
    {
        Comparator  comparator  =   (userSortOption == UserSortOption.ALPHA_Z_A) ? CollectionUtils.reverse(CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST) : CollectionUtils.DEFAULT_COMPARATOR_NULLS_FIRST;
        
        return ObjstoreUtils.sort(users, new ObjectTransform[]{CompanyUserTransform.INSTANCE}, new Comparator[]{comparator});
    }

    
    
    public static class ClientJobCountTransform implements ObjectTransform<Client, Integer>
    {
        JobStatus   jobStatus;

        public ClientJobCountTransform(JobStatus jobStatus)
        {
            this.jobStatus = jobStatus;
        }
        
        @Override
        public Integer transform(Client client)
        {
            return client.getJobCountByStatus(jobStatus);
        }
    }
    
    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_NULLS_FIRST;
        
        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_NULLS_FIRST;
        }
        else if(appSortOption==AppSortOption.ALPHA_Z_A)
        {
            transform   =   JobApplication.pipesJobApplication().toCandidate().toUser().toFirstName();
            comparator  =   CollectionUtils.reverse(CollectionUtils.CASE_INSENSITIVE_COMPARATOR_NULLS_FIRST);
        }
        
        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);
        
        return companyUser.getCompany().getClientsSet().toArray(new Client[0]);
    }
    
    
    public static Boolean emailExists(ObjectTransaction objTran, String email, boolean isCandidate)
    {
        if(email!= null)
        {
            SecUser secUser =   SecUser.searchNAME(objTran, email.toLowerCase());
            
            if(isCandidate)
            {
                Candidate   candidate   =   secUser.getExtension(Candidate.REFERENCE_Candidate);
                
                return candidate != null && candidate.getIsAccountVerified();
            }
            else
            {
                return secUser != null;
            }
        }
        
        return Boolean.FALSE;
    }
    
    
    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 Boolean isCompanyUserEmailFound(ObjectTransaction objTran, String email, CompanyUser currentUser)
    {
        if(email!=null)
        {
            SecUser user    = SecUser.searchNAME(objTran, email.toLowerCase());

            if(user!=null)
            {
                CompanyUser comUser = user.getExtension(CompanyUser.REFERENCE_CompanyUser); 
                
                if(comUser!=null && !CollectionUtils.equals(comUser, currentUser))
                {
                    return Boolean.TRUE;
                }
            }
        }

        return Boolean.FALSE;
    }
    
    
    public static Tuple.T2<SecUser, Boolean> 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 new Tuple.T2<SecUser, Boolean>(availableSecUser, Boolean.TRUE);
            }
        }
        
        return new Tuple.T2<SecUser, Boolean>(secUser, Boolean.FALSE);
    }
    
    
    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.getUser());

                Utils.sendMail(emailer, transform, new String[]{companyUser.getEmailAddressFromUser()}, 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 user :: " + 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 user :: ", companyUser);
        }
    }
    
    
    public static void sendEmailChangedMail(CompanyUser companyUser, HttpServletRequest request, ConfigurableArticleTemplateEmailer  emailer, String callingClass) throws BusinessException
    {
        if(companyUser.getIsEmailChanged()==Boolean.TRUE)
        {
            try
            {
                LogMgr.log(CompanyUser.LOG, LogLevel.PROCESSING1, "Sending email changed mail from  " + callingClass + " to :: ", companyUser);

                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) 
                                                            + "/extensions/adminportal/email_verified.jsp"
                                                            + "?id=" + companyUser.getID()
                                                            + "&key=" + companyUser.getVerificationKey();
                Map             defaultParams       =   CollectionUtils.mapEntry("link", link).toMap(); 
                ObjectTransform transform           =   Utils.createCompoundTransform(defaultParams, companyUser.getUser());

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

                LogMgr.log(CompanyUser.LOG, LogLevel.PROCESSING1, "Sent email changed mail successfully from " + callingClass + " to :: ", companyUser);
            }
            catch (ConfigurableEmailerException ex)
            {
                LogMgr.log(CompanyUser.LOG, LogLevel.SYSTEMERROR1, ex, "Error occured while sending mail for user :: " + 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 user :: ", companyUser);
        }
    }
    
    public static boolean facebookAvailable()
    {
        return  ConfigMgr.getKeyfileString(FacebookOAuthLoginFP.FACEBOOK_APPID_ATTRIB_NAME) != null 
                && ConfigMgr.getKeyfileString(FacebookOAuthLoginFP.FACEBOOK_APP_SECRET_ATTR_NAME) != null;
    }   
    
    
    public static boolean linkedInAvailable()
    {
        return  ConfigMgr.getKeyfileString(LinkedInOAuthLoginFP.LINKEDIN_CLIENTID_ATTR_NAME) != null 
                && ConfigMgr.getKeyfileString(LinkedInOAuthLoginFP.LINKEDIN_CLIENTSECRET_ATTR_NAME) != null;
    }   
    
    
    public static boolean googleAvailable()
    {
        return  ConfigMgr.getKeyfileString(GoogleOAuthLoginFP.GOOGLE_CLIENTID_ATTR_NAME) != null 
                && ConfigMgr.getKeyfileString(GoogleOAuthLoginFP.GOOGLE_CLIENTSECRET_ATTR_NAME) != null;
    }
    
    
    //to get Job status list sorted for client
    public static Collection<JobStatus>  getJobStatusesForClient()
    {
         return CollectionUtils.sort(Arrays.asList(JobStatus.getJobStatusArray()), 
                            new Comparator<JobStatus> () {
                                @Override
                                public int compare(JobStatus o1, JobStatus o2)
                                {
                                    return o1.getClientSortOrder().compareTo(o2.getClientSortOrder());
                                }
                            });
    }
    
    private static class CompanyUserTransform implements ObjectTransform<CompanyUser, String>
    {
        static  CompanyUserTransform    INSTANCE    =   new CompanyUserTransform();
        
        @Override
        public String transform(CompanyUser companyUser)
        {
            return SecUserToNameTransform.INSTANCE.transform(companyUser.getUser());
        }
    }
}