// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.moodle.moodlemobile;

import android.os.Build;
import android.util.Log;
import android.content.Context;
import android.content.SharedPreferences;
import java.security.GeneralSecurityException;
import java.io.IOException;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;

import com.adobe.phonegap.push.EncryptionHandler;

public class SecureStorage extends CordovaPlugin {

    private static final String TAG = "SecureStorage";
    private static final String SHARED_PREFS_NAME = "moodlemobile_shared_prefs";

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
        try {
            switch (action) {
                case "get":
                    callbackContext.success(this.get(args.getJSONArray(0), args.getString(1)));

                    return true;
                case "store":
                    this.store(args.getJSONObject(0), args.getString(1));
                    callbackContext.success();

                    return true;
                case "delete":
                    this.delete(args.getJSONArray(0), args.getString(1));
                    callbackContext.success();

                    return true;
                case "deleteCollection":
                    this.deleteCollection(args.getString(0));
                    callbackContext.success();

                    return true;
            }
        } catch (Throwable e) {
            Log.e(TAG, "Failed executing action: " + action, e);
            callbackContext.error(e.getMessage());
            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR));
        }

        return false;
    }

    /**
     * Get several values from secure storage.
     *
     * @param names List of names to get.
     * @param collection The collection where the values are stored.
     * @return Values for each name.
     */
    private JSONObject get(JSONArray names, String collection) throws GeneralSecurityException, IOException, JSONException {
        Context context = this.cordova.getActivity().getApplicationContext();
        SharedPreferences sharedPreferences = getSharedPreferences(collection);
        JSONObject result = new JSONObject();

        Log.d(TAG, "Get values with names " + names.toString());

        for(int i = 0; i < names.length(); i++) {
            String name = names.optString(i);

            if (name == null || name.isEmpty()) {
                continue;
            }

            String rawValue = sharedPreferences.getString(name, null);
            if (rawValue == null) {
                continue;
            }

            result.put(name, EncryptionHandler.Companion.decrypt(context, rawValue));
        }

        return result;
    }

    /**
     * Store data in secure storage.
     *
     * @param data Data to store, using a name -> value format.
     * @param collection The collection where to store the values.
     */
    private void store(JSONObject data, String collection) throws GeneralSecurityException, IOException, JSONException {
        Context context = this.cordova.getActivity().getApplicationContext();
        SharedPreferences sharedPreferences = getSharedPreferences(collection);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        JSONArray names = data.names();

        Log.d(TAG, "Store values with names " + names.toString());

        for(int i = 0; i < names.length(); i++) {
            String name = names.optString(i);

            if (name != null && !name.isEmpty()) {
                editor.putString(name, EncryptionHandler.Companion.encrypt(context, data.getString(name)));
            }
        }

        editor.apply();
    }

    /**
     * Delete some values from secure storage.
     *
     * @param names Names to delete.
     * @param collection The collection where to delete the values.
     */
    private void delete(JSONArray names, String collection) throws GeneralSecurityException, IOException {
        Log.d(TAG, "Delete value with names " + names.toString());

        SharedPreferences sharedPreferences = getSharedPreferences(collection);
        SharedPreferences.Editor editor = sharedPreferences.edit();

        for(int i = 0; i < names.length(); i++) {
            String name = names.optString(i);

            if (name != null && !name.isEmpty()) {
                editor.remove(name);
            }
        }

        editor.apply();
    }

    /**
     * Delete all values from a collection.
     *
     * @param collection The collection to delete.
     */
    private void deleteCollection(String collection) throws GeneralSecurityException, IOException {
        Log.d(TAG, "Delete all values in collection " + collection);

        SharedPreferences sharedPreferences = getSharedPreferences(collection);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.clear();
        editor.apply();
    }

    /**
     * Get shared preferences instance.
     *
     * @param collection The collection to use.
     * @return Shared preferences instance.
     */
    private SharedPreferences getSharedPreferences(String collection) {
        return this.cordova.getActivity().getApplicationContext().getSharedPreferences(
            SHARED_PREFS_NAME + "_" + collection,
            Context.MODE_PRIVATE
        );
    }

}
