package org.keyscience.BackgroundTask;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.keyscience.Database.DatabaseHelper;
import org.keyscience.Database.EcoQuestDBContract;
import org.keyscience.SingletonPattern.User;
import org.keyscience.app.Constants;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class LoginAsyncTask extends AsyncTask<String, Boolean, Boolean> {

    private boolean remember;
    private String jsonString = null;
    private WeakReference<Context> contextWeakReference;

    public LoginAsyncTask(boolean remember, Context context) {
        this.remember = remember;
        contextWeakReference = new WeakReference<>(context);
    }

    @Override
    protected Boolean doInBackground(String... strings) {
        try {
            if (userLogin(strings[0], strings[1])) {

                JSONArray jsonArray = new JSONArray(jsonString);
                JSONObject jsonUserData = jsonArray.getJSONObject(0);
                JSONObject completedQuest = getCompletedQuestsFromRemoteDB(jsonUserData.getInt("ID"));
                JSONArray completedQuestArray = completedQuest.getJSONArray(jsonUserData.getString("ID"));

                long userID = Integer.parseInt(jsonUserData.getString("ID"));
                String name = jsonUserData.getString("display_name");
                String email = jsonUserData.getString("user_email");
                String bio = jsonUserData.getString("user_bio");
                int points = jsonUserData.getInt("totalpoints");
                String rank = jsonUserData.getString("user_rank");

                HashSet<Integer> completedQuestSet = new HashSet<>();
                for (int i = 0; i < completedQuestArray.length(); i++) {
                    completedQuestSet.add(completedQuestArray.optInt(i));
                }

                // make instance of user
                User.getInstance(userID, name, email, bio, points, rank, completedQuestSet);

                // if user selected 'remember me', then write to local DB
                if (remember) {
                    DatabaseHelper dbHelper = new DatabaseHelper(contextWeakReference.get());
                    SharedPreferences sharedPreferences = contextWeakReference.get().getSharedPreferences(Constants.SHARE_PRE_FILE, Context.MODE_PRIVATE);
                    SharedPreferences.Editor preferencesEditor = sharedPreferences.edit();
                    preferencesEditor.putLong(Constants.PERSISTENT_SESSION, userID);
                    preferencesEditor.commit(); // we need data immediately to avoid a race condition

                    SQLiteDatabase db = dbHelper.getWritableDatabase();

                    // Create a map of values, where column names are the keys
                    ContentValues values = new ContentValues();
                    values.put(EcoQuestDBContract.UserTable._ID, userID);
                    values.put(EcoQuestDBContract.UserTable.NAME, name);
                    values.put(EcoQuestDBContract.UserTable.EMAIL, email);
                    values.put(EcoQuestDBContract.UserTable.BIO, bio);
                    values.put(EcoQuestDBContract.UserTable.POINTS, points);
                    values.put(EcoQuestDBContract.UserTable.RANK, rank);

                    // Insert the new row, returning the primary key value of the new row
                    db.insert(EcoQuestDBContract.UserTable.TABLE_NAME, null, values);

                    //insert completed quests for the user

                    // Create a map of values for the completed quests
                    ContentValues valuesQuest = new ContentValues();
                    valuesQuest.put(EcoQuestDBContract.CompletedQuest.USER_ID, jsonUserData.getString("ID"));

                    for (int i = 0; i < completedQuestArray.length(); i++) {
                        valuesQuest.put(EcoQuestDBContract.CompletedQuest.QUEST_ID, completedQuestArray.optInt(i));
                        //insert into local DB completed quests
                        db.insert(EcoQuestDBContract.CompletedQuest.TABLE_NAME, null, valuesQuest);
                    }
                    dbHelper.close();
                }

                return true;
            } else {
                return false;
            }
        } catch (IOException | JSONException e) {
            e.printStackTrace();
            return false;
        }
    }

    private JSONObject getCompletedQuestsFromRemoteDB(long userID) throws JSONException, IOException {
        HashMap<String, String> parameters = new HashMap<>();

        parameters.put("user_id", Long.toString(userID));

        String urlParameters = getDataString(parameters);

        // POST CALL
        byte[] postData;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
            postData = urlParameters.getBytes(StandardCharsets.UTF_8);
        } else
            postData = urlParameters.getBytes(Charset.forName("UTF-8"));

        int postDataLength = postData.length;

        String request = "http://www.keyscience.org/wp-content/app/EcoQuest/userEcoQuests.php";

        URL url = new URL(request);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setInstanceFollowRedirects(false);

        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("charset", "utf-8");
        conn.setRequestProperty("Content-Length", Integer.toString(postDataLength));
        conn.setUseCaches(false);

        DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
        wr.write(postData);

        // READ PHP OUTPUT
        InputStream in = conn.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        String inputLine;
        StringBuilder jsonString = new StringBuilder();
        while ((inputLine = reader.readLine()) != null) {
            jsonString.append(inputLine);
        }
        in.close();
        return new JSONObject(jsonString.toString());
    }

    @Override
    protected void onPostExecute(Boolean successfulLogin) {
        super.onPostExecute(successfulLogin);
        if (successfulLogin) {
            // notify of successful log in
            Intent intent = new Intent();
            intent.setAction(Constants.ACTION_LOGIN);
            LocalBroadcastManager.getInstance(contextWeakReference.get()).sendBroadcast(intent);
        } else {
            Toast.makeText(contextWeakReference.get(), "Failed to Login", Toast.LENGTH_LONG).show();
            Intent intent = new Intent();
            intent.setAction(Constants.FAILED_LOGIN);
            LocalBroadcastManager.getInstance(contextWeakReference.get()).sendBroadcast(intent);
        }
    }


    private Boolean userLogin(String user_login, String user_pass) throws IOException {
        boolean loginSuccess = true;

        HashMap<String, String> userCredentials = new HashMap<>();

        userCredentials.put("user_login", user_login);
        userCredentials.put("user_pass", user_pass);

        String urlParameters = getDataString(userCredentials);

        // POST CALL
        byte[] postData = new byte[0];
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
            postData = urlParameters.getBytes(StandardCharsets.UTF_8);
        } else
            postData = urlParameters.getBytes(Charset.forName("UTF-8"));

        int postDataLength = postData.length;

        String request = "http://www.keyscience.org/wp-content/app/userLogin.php";

        URL url = new URL(request);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setInstanceFollowRedirects(false);

        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("charset", "utf-8");
        conn.setRequestProperty("Content-Length", Integer.toString(postDataLength));
        conn.setUseCaches(false);

        DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
        wr.write(postData);
        wr.close();

        // READ PHP OUTPUT
        InputStream in = conn.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        String inputLine;
        StringBuilder jsonString = new StringBuilder();
        while ((inputLine = reader.readLine()) != null) {

            jsonString.append(inputLine);

            if (inputLine.equals("FAIL!")) {
                loginSuccess = false;
            }
        }
        in.close();
        this.jsonString = jsonString.toString();
        return loginSuccess;
    }

    // Build string for POST
    private static String getDataString(HashMap<String, String> params) throws
            UnsupportedEncodingException {
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (first)
                first = false;
            else
                result.append("&");
            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }
        return result.toString();
    }

}