/*
 *  Copyright (C) 2014 Stichting Akvo (Akvo Foundation)
 *
 *  This file is part of Akvo FLOW.
 *
 *  Akvo FLOW is free software: you can redistribute it and modify it under the terms of
 *  the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
 *  either version 3 of the License or any later version.
 *
 *  Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU Affero General Public License included below for more details.
 *
 *  The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
 */

package org.akvo.gae.remoteapi;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.tools.remoteapi.RemoteApiInstaller;
import com.google.appengine.tools.remoteapi.RemoteApiOptions;

/**
 * Run this application to create two default user roles (admin and user), and to grant existing
 * users the authorization to view, modify and delete all surveys groups and surveys that exist on
 * an instance
 *
 * @author emmanuel
 */
public class DefaultUserRoles {

    private static String ADMIN_ROLE_NAME = "admin";

    private static String USER_ROLE_NAME = "user";

    private static List<String> allPermissionsList = Arrays.asList("PROJECT_FOLDER_CREATE",
            "PROJECT_FOLDER_READ", "PROJECT_FOLDER_UPDATE", "PROJECT_FOLDER_DELETE", "FORM_CREATE",
            "FORM_READ", "FORM_UPDATE", "FORM_DELETE");

    private static Entity createAuthorizationEntity(Key userKey, Key roleKey) {
        Entity authorizationEntity = new Entity("UserAuthorization");
        authorizationEntity.setProperty("userId", userKey.getId());
        authorizationEntity.setProperty("roleId", roleKey.getId());
        authorizationEntity.setProperty("objectPath", "/");
        return authorizationEntity;
    }

    public static String anonymizeEmail(String email) {
        return email.replaceAll(".{4}@.{2}", "**@**");
    }

    public static void main(String[] args) {
        if (args.length < 3 || args.length > 4) {
            System.out
                    .println(
                    "Usage: java org.akvo.gae.remoteapi.DefaultUserRoles <appid> <user> '<passwd>' [port]");
            System.exit(0);
        }

        final String instanceUrl = "localhost".equals(args[0]) ? "localhost" : args[0]
                + ".appspot.com";
        final String userEmail = args[1];
        final String passwd = args[2];
        final int port = args.length == 4 ? Integer.parseInt(args[3]) : 443;

        final RemoteApiOptions options = new RemoteApiOptions().server(instanceUrl, port)
                .credentials(
                        userEmail, passwd);

        final RemoteApiInstaller installer = new RemoteApiInstaller();

        try {
            installer.install(options);

            DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

            // create / retrieve defaultroles + retrieve key ids

            Query rolesQuery = new Query("UserRole").setFilter(new Query.FilterPredicate("name",
                    FilterOperator.IN, Arrays.asList(ADMIN_ROLE_NAME, USER_ROLE_NAME)));
            PreparedQuery rolesPq = ds.prepare(rolesQuery);
            Entity defaultAdminRole = null;
            Entity defaultUserRole = null;

            for (Entity role : rolesPq.asIterable()) {
                String roleName = (String) role.getProperty("name");
                if (ADMIN_ROLE_NAME.equals(roleName)) {
                    defaultAdminRole = role;
                } else {
                    defaultUserRole = role;
                }
            }

            Key adminRoleKey = null;
            if (defaultAdminRole == null) {
                defaultAdminRole = new Entity("UserRole");
                defaultAdminRole.setProperty("name", "admin");
                defaultAdminRole.setProperty("permissions", allPermissionsList);
                adminRoleKey = ds.put(defaultAdminRole);
                System.out.println(ADMIN_ROLE_NAME + " role created");
            } else {
                adminRoleKey = defaultAdminRole.getKey();
                System.out.println(ADMIN_ROLE_NAME + " role already exists");
            }

            Key userRoleKey = null;
            if (defaultUserRole == null) {
                defaultUserRole = new Entity("UserRole");
                defaultUserRole.setProperty("name", "user");
                defaultUserRole.setProperty("permissions", allPermissionsList);
                userRoleKey = ds.put(defaultUserRole);
                System.out.println(USER_ROLE_NAME + " role created");
            } else {
                userRoleKey = defaultUserRole.getKey();
                System.out.println(USER_ROLE_NAME + " role already exists");
            }

            // for each user create authorization

            // retrieve existing authorization
            Query userAuthQuery = new Query("UserAuthorization");
            userAuthQuery.setFilter(new Query.FilterPredicate(
                    "objectPath", FilterOperator.EQUAL, "/"));

            PreparedQuery userAuthPq = ds.prepare(userAuthQuery);
            Map<Long, Long> userAuthMap = new HashMap<Long, Long>();
            for (Entity authorization : userAuthPq.asIterable()) {
                Long userId = (Long) authorization.getProperty("userId");
                Long roleId = (Long) authorization.getProperty("roleId");
                userAuthMap.put(userId, roleId);
            }

            List<Entity> userAuthorizationList = new ArrayList<Entity>();
            Query userQuery = new Query("User");
            // ignore superadmin users
            userQuery.setFilter(new Query.FilterPredicate(
                    "permissionList", FilterOperator.IN, Arrays.asList("10", "20")));
            PreparedQuery userPq = ds.prepare(userQuery);

            for (Entity user : userPq.asIterable()) {
                String permissionList = (String) user.getProperty("permissionList");
                String email = (String) user.getProperty("emailAddress");
                Long userId = user.getKey().getId();

                if (permissionList.trim().equals("10")) {
                    if (userAuthMap.containsKey(userId)
                            && userAuthMap.get(userId).equals(adminRoleKey.getId())) {
                        System.out.println("Admin authorization for user " + anonymizeEmail(email)
                                + " already exists");
                        continue;
                    }
                    userAuthorizationList
                            .add(createAuthorizationEntity(user.getKey(), adminRoleKey));
                    System.out.println("Creating Admin user authorization for: "
                            + anonymizeEmail(email));
                } else {
                    if (userAuthMap.containsKey(userId)
                            && userAuthMap.get(userId).equals(userRoleKey.getId())) {
                        System.out.println("User authorization for user " + anonymizeEmail(email)
                                + " already exists");
                        continue;
                    }
                    userAuthorizationList
                            .add(createAuthorizationEntity(user.getKey(), userRoleKey));
                    System.out.println("Creating user authorization for: " + anonymizeEmail(email));
                }
            }

            ds.put(userAuthorizationList);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            installer.uninstall();
        }
    }
}

@@ -2823,17 +2823,17 @@
 em.exit(
-0
+1
 );%0A     

Exit on error