package ca.firstvoices.securitypolicies.lifecycle;

import java.security.Principal;
import java.util.Arrays;
import java.util.List;

import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.api.security.ACP;
import org.nuxeo.ecm.core.api.security.Access;
import org.nuxeo.ecm.core.model.Document;
import org.nuxeo.ecm.core.query.sql.NXQL;
import org.nuxeo.ecm.core.query.sql.model.Operator;
import org.nuxeo.ecm.core.query.sql.model.Predicate;
import org.nuxeo.ecm.core.query.sql.model.Reference;
import org.nuxeo.ecm.core.query.sql.model.SQLQuery;
import org.nuxeo.ecm.core.query.sql.model.SQLQuery.Transformer;
import org.nuxeo.ecm.core.query.sql.model.StringLiteral;
import org.nuxeo.ecm.core.query.sql.model.WhereClause;
import org.nuxeo.ecm.core.security.AbstractSecurityPolicy;

public class NonRecorders extends AbstractSecurityPolicy {

    @Override
    public Access checkPermission(Document doc, ACP mergedAcp, Principal principal, String permission,
            String[] resolvedPermissions, String[] additionalPrincipals) {

        // Skip administrators
        if (Arrays.asList(additionalPrincipals).contains("administrators")) {
            return Access.UNKNOWN;
        }

        List<String> additionalPrincipalsList = Arrays.asList(additionalPrincipals);

        // Security policy should only apply to non-proxied documents
        if (!doc.isProxy()) {
            // Users who aren't at least recorders should be denied access documents with New or Disabled state
            if (!additionalPrincipalsList.contains("recorders") && !additionalPrincipalsList.contains("language_administrators")) {
            	String docLifeCycle = doc.getLifeCycleState();
                if (docLifeCycle != null) {
                	if(docLifeCycle.equals("New") || docLifeCycle.equals("Disabled")) {
                		return Access.DENY;
                	}
                }
            }
        }

        return Access.UNKNOWN;
    }

    @Override
    public SQLQuery.Transformer getQueryTransformer(String repositoryName) {
        return NOT_DISABLED_AND_NOT_NEW_TRANSFORMER;
    }

    @Override
    public boolean isExpressibleInQuery(String repositoryName) {
        return true;
    }

    public static final Transformer NOT_DISABLED_AND_NOT_NEW_TRANSFORMER = new NotDisabledAndNotNewTransformer();

    /**
     * Transformer that adds {@code AND ecm:lifeCycleState <> 'Disabled' AND ecm:lifeCycleState <> 'New'} to the query.
     */
    public static class NotDisabledAndNotNewTransformer implements SQLQuery.Transformer {

		private static final long serialVersionUID = 1L;
		public static final Predicate NOT_DISABLED = new Predicate(new Reference(NXQL.ECM_LIFECYCLESTATE), Operator.NOTEQ, new StringLiteral("Disabled"));
        public static final Predicate NOT_NEW = new Predicate(new Reference(NXQL.ECM_LIFECYCLESTATE), Operator.NOTEQ, new StringLiteral("New"));

        @Override
        public SQLQuery transform(Principal principal, SQLQuery query) {

        	NuxeoPrincipal nxPrincipal = (NuxeoPrincipal) principal;

        	// Skip Admins
        	if (nxPrincipal.isAdministrator()) {
        		return query;
        	}

        	// Modify the query for anonymous and non-recorders only
        	if(nxPrincipal.isAnonymous() || (!nxPrincipal.isMemberOf("recorders") && !nxPrincipal.isMemberOf("language_administrators")) ) {

                WhereClause where = query.where;
                Predicate predicate;

                // Do not limit published assets in the Site and SharedData directories. These generally do not follow fv-lifecycle
                if (where.toString().indexOf("ecm:path STARTSWITH '/FV/sections/Site/Resources/'") != -1 || where.toString().indexOf("ecm:path STARTSWITH '/FV/sections/SharedData/'") != -1) {
                    return query;
                }

                if (where == null || where.predicate == null) {
                	predicate = new Predicate(NOT_DISABLED, Operator.AND, NOT_NEW);
                } else {
                	Predicate notDisabledAndNotNew = new Predicate(NOT_DISABLED, Operator.AND, NOT_NEW);
                	predicate = new Predicate(notDisabledAndNotNew, Operator.AND, where.predicate);
                }
                // return query with updated WHERE clause
                return new SQLQuery(query.select, query.from, new WhereClause(predicate),
                        query.groupBy, query.having, query.orderBy, query.limit, query.offset);
        	}

            return query;
        }
    }

}

@@ -3461,16 +3461,191 @@
 icate;%0A%0A
+                if (where == null %7C%7C where.predicate == null) %7B%0A                %09predicate = new Predicate(NOT_DISABLED, Operator.AND, NOT_NEW);%0A                %7D else %7B%0A%0A    
         
@@ -3776,32 +3776,36 @@
                 
+    
 if (where.toStri
@@ -3801,39 +3801,40 @@
 here.toString().
-indexOf
+contains
 (%22ecm:path START
@@ -3871,22 +3871,16 @@
 rces/'%22)
- != -1
  %7C%7C wher
@@ -3896,15 +3896,16 @@
 g().
-indexOf
+contains
 (%22ec
@@ -3954,18 +3954,16 @@
 /'%22)
- != -1
 ) %7B%0A
+    
     
@@ -4000,168 +4000,8 @@
     
-            %7D%0A%0A                if (where == null %7C%7C where.predicate == null) %7B%0A                %09predicate = new Predicate(NOT_DISABLED, Operator.AND, NOT_NEW);%0A
     
@@ -4013,23 +4013,17 @@
        %7D
- else %7B
+%0A
 %0A       

Fix null pointer exception when no where clause