﻿using PlayFab;
using PlayFab.ClientModels;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Net.Mail;
using System;
using System.Text.RegularExpressions;
using UnityEngine.Analytics;
using UnityEngine.Networking;

public class PlayFabUserAuthenticationController : MonoBehaviour {

    public enum FormType {
        Login = 0, 
        Signup,
		Shared
    }

	public GameObject sharedAccountUI;
	public GameObject createAccountUI;
	public GameObject verifyCheckMark;

	public string shareAttempt;
	public string sharedBy = "";

	bool otherUserCheck;
    bool playerLoggedAsTestOrLive = false;

	public TMP_InputField sharedUserFieldPro;
    public TMP_InputField usernameFieldPro;
    public TMP_InputField emailFieldPro;
    public TMP_InputField moodleUsernameFieldPro;
    public TMP_InputField passwordFieldPro;
    public TMP_InputField confirmpasswordFieldPro;
    [HideInInspector]
	public TMP_InputField firstNameFieldPro;
	[HideInInspector]
	public TMP_InputField lastNameFieldPro;
    

    public MenuNavigationController controller;
    public PlayerStats playerStats;
    public PlayerLeaderboard playerLeaderboard;

    public GameObject alertPopup;

    const int minPassLength = 6;
    const int maxPassLength = 100;

    public FormType formType;

    public TMP_InputField moodleUsernameInput;

    Dictionary<string, string> defaultUserData = new Dictionary<string, string> {
    };

    // Use this for initialization
    void Start() {
        if (!controller)
            GetComponent<MenuNavigationController>();

        if (!emailFieldPro)
            return;

        emailFieldPro.characterValidation = TMP_InputField.CharacterValidation.EmailAddress;
    }

    private void Update() {
        if (Input.GetKeyDown(KeyCode.Return)) {
            switch (formType) {
                case FormType.Login:
                    AttemptLogin();
                    break;
                case FormType.Signup:
                    AttemptCreateAccount();
                    break;
				case FormType.Shared:
					AttemptVerifySharer ();
					break;
			}
        }
    }

    public void AttemptLogin() {
        if (!usernameFieldPro || !passwordFieldPro)
            return;

        string username = usernameFieldPro.text;
        string pass = passwordFieldPro.text;

        if (!ValidateInput(username, pass))
            return;

        var request = new LoginWithPlayFabRequest { Username = username, Password = pass, TitleId = PlayFabSettings.TitleId };
		PlayFab.PlayFabClientAPI.LoginWithPlayFab(request, OnLoginSuccesUsername, OnFailedLoginUsername);
    }

	public void AttemptVerifySharer () {

		shareAttempt = sharedUserFieldPro.text;

        if(shareAttempt.Length <= 0) {
            SkipToCreateAccount();
            return;
        }

		var sharedRequest = new LoginWithPlayFabRequest { Username = shareAttempt, Password = "IhOpEn0oNeUsEsThIs", TitleId = PlayFabSettings.TitleId };
		otherUserCheck = true;
		PlayFabClientAPI.LoginWithPlayFab(sharedRequest, setDefaultResult => print("This should not have worked"), OnClientApiCallFailure);

	}

	void OnSharedUserSuccess (GetAccountInfoResult result) {

		PlayfabCloudScriptAPI.AfterRegistration (result.AccountInfo.PlayFabId);
	}

    public void SwitchBack() {
        if(verifyCheckMark) verifyCheckMark.SetActive(false);
        if(createAccountUI) createAccountUI.SetActive(false);
        if(sharedAccountUI) sharedAccountUI.SetActive(true);
        formType = FormType.Shared;
    }

	public void SkipToCreateAccount () {
		sharedAccountUI.SetActive (false);
		createAccountUI.SetActive (true);
        formType = FormType.Signup;
    }

	IEnumerator DelayCreateAccountFields () {

		verifyCheckMark.SetActive (true);
		formType = FormType.Signup;
		yield return new WaitForSecondsRealtime (2.0f);
		sharedAccountUI.SetActive (false);
		createAccountUI.SetActive (true);

	}

    void OnLoginSuccesUsername(LoginResult result) {
        // im assuming at this point the username field was valid since I'm already logged in
        PlayFabClientAPI.UpdateUserTitleDisplayName(new UpdateUserTitleDisplayNameRequest() { DisplayName = usernameFieldPro.text }, null, null);

        OnLoginSuccess(result);
    }

    void RunQueryParamsHandlers() {
        #if UNITY_WEBGL && !UNITY_EDITOR
		/*
            var isCreatorCorps = QueryParamsHandler.IsCreatorCorps();
            if (isCreatorCorps) {
                PlayfabCloudScriptAPI.SetCurrentPlayerAsCreatorCorps();
            }

            var unlockClassroomItems = QueryParamsHandler.UnlockClassroomItems();
            Debug.Log("OnRegisterSuccess: " + unlockClassroomItems);
            if (unlockClassroomItems) {
                PlayfabCloudScriptAPI.CurricUserUnlockAllItems();
          }

            PlayfabCloudScriptAPI.UpdatePlayerMoodleData(QueryParamsHandler.GetMoodleData());
		*/
        #endif
    }

    void OnLoginSuccess(LoginResult result) {
        // after logging in we check to make sure this user has all the data that we expect them to have by setting any new defaults that we have
        RetrieveLocalPlayerInfo();

        RunQueryParamsHandlers();

        playerStats.GetPlayerStats();
        //playerLeaderboard.GetLeaderboardAroundPlayer("1st Place Wins");

        DetermineIfTestUser();
        
        if (controller)
        {
            controller.LoginToMenu();
        }
    }

    void RetrieveLocalPlayerInfo() {
        var request = new GetPlayerCombinedInfoRequest {
            InfoRequestParameters = new GetPlayerCombinedInfoRequestParams {
                GetUserData = true,
                GetUserAccountInfo = true
            }
        };
        PlayFabClientAPI.GetPlayerCombinedInfo(request, SetupPlayerInfoInClient, OnClientApiCallFailure);
    }

    void SetupPlayerInfoInClient (GetPlayerCombinedInfoResult result) {
        SetNewUserDataDefaults(result.InfoResultPayload.UserData);
        
        PersistentData.localPlayerData.playFabId = result.PlayFabId;
        PersistentData.localPlayerData.username = result.InfoResultPayload.AccountInfo.Username;

        PlayFabClientAPI.GetUserData(new GetUserDataRequest() {
                PlayFabId = result.PlayFabId,
                Keys = null
            },
            getUserDataResult => {
                if (getUserDataResult.Data == null || !getUserDataResult.Data.ContainsKey("moodleUsername"))
                    Debug.Log("No moodleUsername");
                else
                    moodleUsernameInput.text = getUserDataResult.Data["moodleUsername"].Value;
            }, 
            (error) => {
                Debug.Log("Got error retrieving user data:");
                Debug.Log(error.GenerateErrorReport());
            });
    }

    void SetNewUserDataDefaults(Dictionary <string, UserDataRecord> userData) {
        Dictionary<string, string> valuesToAdd = new Dictionary<string, string>();
        foreach (KeyValuePair<string, string> entry in defaultUserData) {
            if (!userData.ContainsKey(entry.Key)) {
                valuesToAdd.Add(entry.Key, entry.Value);
            }
        }

        if(valuesToAdd.Count > 0) {

            foreach(KeyValuePair<string, string> entry in valuesToAdd) {
                print(entry.Key + ":" + entry.Value);
            }

            var request = new UpdateUserDataRequest { Data = valuesToAdd };
            PlayFabClientAPI.UpdateUserData(request, setDefaultResult => print("success setting new defaults"), OnClientApiCallFailure);
        }
    }

    void OnFailedLoginUsername(PlayFabError error) {
        if (!usernameFieldPro || !passwordFieldPro)
            return;

        string username = usernameFieldPro.text;
        string pass = passwordFieldPro.text;

        if (!ValidateInput(username, pass))
            return;

        string email = username;
        var otherRequest = new LoginWithEmailAddressRequest { Email = email, Password = pass, TitleId = PlayFabSettings.TitleId };
        PlayFabClientAPI.LoginWithEmailAddress(otherRequest, OnLoginSuccess, OnClientApiCallFailure);
    }

    void OnClientApiCallFailure(PlayFabError error) {
		
        switch (error.Error) {
            // Login With Email Error Codes
            case PlayFabErrorCode.InvalidEmailOrPassword:
                MakePopup("Invalid Email or Password. Please re-enter your information.");
                break;
			case PlayFabErrorCode.InvalidUsernameOrPassword:
				if (otherUserCheck) {
					sharedBy = shareAttempt;
					StartCoroutine (DelayCreateAccountFields ());
					break;
				} else {
					MakePopup ("Invalid Username or Password. Please re-enter your information.");
					break;
				}
            case PlayFabErrorCode.AccountNotFound:
                MakePopup("Account Not Found");
                break;
            case PlayFabErrorCode.InvalidTitleId:
                MakePopup("Invalid Title ID");
                break;
            case PlayFabErrorCode.RequestViewConstraintParamsNotAllowed:
                MakePopup("Request View Constraint Params Not Allowed");
                break;

            // Register PlayFab User Error Codes
            case PlayFabErrorCode.EmailAddressNotAvailable:
                MakePopup("Email Address not available.  Please try a new one.");
                break;
            case PlayFabErrorCode.InvalidEmailAddress:
                MakePopup("Invalid Email Address.  Please re-enter your information.");
                break;
            case PlayFabErrorCode.InvalidPartnerResponse:
                MakePopup("Invalid Partner Response");
                break;
            case PlayFabErrorCode.InvalidPassword:
                MakePopup("Invalid Password.  Please re-enter your information.");
                break;
            case PlayFabErrorCode.InvalidUsername:
                MakePopup("Invalid Username.  Please re-enter your information.");
                break;
            case PlayFabErrorCode.NameNotAvailable:
                MakePopup("Name not available. Please choose another.");
                break;
            case PlayFabErrorCode.ProfaneDisplayName:
                MakePopup("Profane Name. Choose another");
                break;
            case PlayFabErrorCode.UsernameNotAvailable:
                MakePopup("Username not available. Please choose another.");
                break;
            case PlayFabErrorCode.InvalidParams:
                MakePopup("Invalid username or password. Please re-enter your information.");
                break;
            default:
                MakePopup("Something went wrong. Debug information:" + error.GenerateErrorReport());
                break;
        }
		otherUserCheck = false;
    }

    public void AttemptCreateAccount() {
        if (!emailFieldPro || !usernameFieldPro || !confirmpasswordFieldPro || !passwordFieldPro)
            return;

        string username = usernameFieldPro.text;
        string email = emailFieldPro.text;
        string moodleUsername = moodleUsernameFieldPro.text;
        string pass = passwordFieldPro.text;
        string passConfirm = confirmpasswordFieldPro.text;

        if (!ValidateInput(username, pass, passConfirm))
            return;

        if (username.Length > 20)
        {
            MakePopup("Username has a limit of 20 characters");
            return;
        }

		Regex rgx = new Regex(@"[^A-Za-z0-9]+");
        if (rgx.IsMatch(username)) {
            MakePopup("Username must contain only letters and numbers");
            return;
        }
  
        if(email.Length > 0) {
            if (!ValidateInput(username, pass, passConfirm, email)) {
                return;
            }
            var request = new RegisterPlayFabUserRequest { RequireBothUsernameAndEmail = false, Username = username, DisplayName = username, Email = email, Password = pass, TitleId = PlayFabSettings.TitleId };
            PlayFabClientAPI.RegisterPlayFabUser(request, OnRegisterSuccess, OnClientApiCallFailure);
            return;
        }

        var otherRequest = new RegisterPlayFabUserRequest { RequireBothUsernameAndEmail = false, Username = username, DisplayName = username, Password = pass, TitleId = PlayFabSettings.TitleId };
        PlayFabClientAPI.RegisterPlayFabUser(otherRequest, OnRegisterSuccess, OnClientApiCallFailure);
    }

    void OnRegisterSuccess(RegisterPlayFabUserResult result) {
        string moodleUsername = moodleUsernameFieldPro.text;

        RunQueryParamsHandlers();

        if(moodleUsername.Length > 0) {
            PlayfabCloudScriptAPI.UpdatePlayerMoodleData(moodleUsername);
            moodleUsernameInput.text = moodleUsername;
        }

        RetrieveLocalPlayerInfo();


        if (sharedBy != "") {
			var sendShare = new GetAccountInfoRequest { Username = sharedBy };
			PlayFabClientAPI.GetAccountInfo (sendShare, OnSharedUserSuccess, OnClientApiCallFailure);
		}

		if (controller)
			controller.LoginToMenu();
    }

    bool ValidateInput(string username, string pass, string passConfirm = null, string email = "!") {

        // check if there is any input
        string errorMessage = "Please enter your ";
        bool missingInput = false;
        if (username.Length <= 0) {
            errorMessage += "username ";
            missingInput = true;
        }
        if (pass.Length <= 0) {
            if (missingInput) {
                errorMessage += "and ";
            }
            errorMessage += "password";
            missingInput = true;
        }

        if (missingInput) {
            MakePopup(errorMessage);
            return false;
        }

        // check if the input is valid
        if (pass.Length < minPassLength || pass.Length > maxPassLength) {
            MakePopup("Password needs to be between 6 and 100 characters long");
            return false;
        }        

        if (passConfirm != null) {
            if (string.Compare(pass, passConfirm) != 0) {
                MakePopup("Passwords do not match");
                return false;
            }
		}

        if(username.Length < 3) {
            MakePopup("Username needs to be at least 3 characters");
            return false;
        }

        if(email != "!") {
            if (!IsValidEmail(email)) {
                MakePopup("Not a valid email address ");
                return false;
            }
        }

        return true;
    }

    public bool IsValidEmail(string emailaddress) {
        try {
            MailAddress m = new MailAddress(emailaddress);

            return true;
        }
        catch (FormatException) {
            return false;
        }
    }

	public void WhatIsThis () {
		alertPopup.SetActive (true);
		alertPopup.GetComponent<Popup>().SetMessageText(
			"Each time a friend creates an account using your username, " +
			"you and your friend will be entered in for a chance to win cool prizes!" +
			" If a friend reffered you to our game, enter that username in the space provided.");
	}

    void MakePopup(string text) {
        alertPopup.SetActive(true);
        alertPopup.GetComponent<Popup>().SetMessageText(text);
    }

    void DetermineIfTestUser()
    {
        PlayFabClientAPI.GetTitleData(
            new GetTitleDataRequest
            {
                Keys = new List<string> { "testUsers" }
            },
            (result) => {
                if (result.Data.ContainsKey("testUsers")) {
                    string testUsersString = result.Data["testUsers"];
                    var testUsers = SimpleJSON.JSON.Parse(testUsersString).AsArray;
                    bool isTestUser = false;

                    for (int i = 0; i < testUsers.Count; i++) {
                        if(testUsers[i] == PersistentData.localPlayerData.playFabId) {
                            isTestUser = true;
                            break;
                        }
                    }

                    if(isTestUser) {
                        Analytics.CustomEvent("testUser");
                    }
                    else {
                        Analytics.CustomEvent("liveUser");
                    }
                }
            },
            OnGetTitleDataFailure
        );
    }

    void OnGetTitleDataFailure (PlayFabError error) {
        Debug.Log("Got error getting titleData:");
        Debug.Log(error.GenerateErrorReport());
    }
}

