--[[
			// SetAccountCredentialsScreen.lua
]]
local CoreGui = game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local ShellModules = Modules:FindFirstChild("Shell")

local ContextActionService = game:GetService('ContextActionService')
local GuiService = game:GetService('GuiService')

local AccountManager = require(ShellModules:FindFirstChild('AccountManager'))
local BaseSignInScreen = require(ShellModules:FindFirstChild('BaseSignInScreen'))
local Errors = require(ShellModules:FindFirstChild('Errors'))
local ErrorOverlay = require(ShellModules:FindFirstChild('ErrorOverlay'))
local EventHub = require(ShellModules:FindFirstChild('EventHub'))
local GlobalSettings = require(ShellModules:FindFirstChild('GlobalSettings'))
local LoadingWidget = require(ShellModules:FindFirstChild('LoadingWidget'))
local ScreenManager = require(ShellModules:FindFirstChild('ScreenManager'))
local SoundManager = require(ShellModules:FindFirstChild('SoundManager'))
local Strings = require(ShellModules:FindFirstChild('LocalizedStrings'))
local Utility = require(ShellModules:FindFirstChild('Utility'))

local function createSetAccountCredentialsScreen(title, description, buttonText)
	local this = BaseSignInScreen()

	this:SetTitle(title or "")
	this:SetDescriptionText(description or "")
	this:SetButtonText(buttonText or "")

	local ModalOverlay = Utility.Create'Frame'
	{
		Name = "ModalOverlay";
		Size = UDim2.new(1, 0, 1, 0);
		BackgroundTransparency = GlobalSettings.ModalBackgroundTransparency;
		BackgroundColor3 = GlobalSettings.ModalBackgroundColor;
		BorderSizePixel = 0;
		ZIndex = 4;
	}

	local myUsername = nil
	local myPassword = nil

	local isUsernameValid = false
	local isPasswordValid = false

	local UserNameProcessingCount = 0
	local PasswordProcessingCount = 0

	this.UsernameObject:SetDefaultText(Strings:LocalizedString("UsernameWord").." ("..
		Strings:LocalizedString("UsernameRulePhrase")..")")
	this.UsernameObject:SetKeyboardTitle(Strings:LocalizedString("UsernameWord"))
	this.UsernameObject:SetKeyboardDescription(Strings:LocalizedString("UsernameRulePhrase"))
	local usernameChangedCn = nil

	this.PasswordObject:SetDefaultText(Strings:LocalizedString("PasswordWord").." ("..
		Strings:LocalizedString("PasswordRulePhrase")..")")
	this.PasswordObject:SetKeyboardTitle(Strings:LocalizedString("PasswordWord"))
	this.PasswordObject:SetKeyboardDescription(Strings:LocalizedString("PasswordRulePhrase"))
	this.PasswordObject:SetKeyboardType(Enum.XboxKeyBoardType.Password)
	local passwordChangedCn = nil

	local function createAndSetCredentialsAsync()
		local result = nil

		local function signupAsync()
			-- legacy check
			-- Users may be in state where they are linked, but have not set credentials from previous
			-- versions of log in logic. If they are in this state, we correct it by having them set their
			-- credentials. Keep this until we can unlink all accounts in this state.
			result = AccountManager:HasLinkedAccountAsync()

			if result == AccountManager.AuthResults.Success then
				result = AccountManager:SetRobloxCredentialsAsync(myUsername, myPassword)
			elseif result == AccountManager.AuthResults.AccountUnlinked then
				result = AccountManager:SignupAsync(myUsername, myPassword)
				if result == AccountManager.AuthResults.Success then
					result = AccountManager:LoginAsync()
				end
			end
		end

		local loader = LoadingWidget(
			{ Parent = ModalOverlay }, { signupAsync } )

		-- set up full screen loader
		ModalOverlay.Parent = GuiRoot
		ContextActionService:BindCoreAction("BlockB", function() end, false, Enum.KeyCode.ButtonB)
		local selectedObject = GuiService.SelectedCoreObject
		GuiService.SelectedCoreObject = nil

		-- call loader
		loader:AwaitFinished()

		-- clean up
		loader:Cleanup()
		loader = nil
		GuiService.SelectedCoreObject = selectedObject
		ContextActionService:UnbindCoreAction("BlockB")
		ModalOverlay.Parent = nil

		if result == AccountManager.AuthResults.Success then
			EventHub:dispatchEvent(EventHub.Notifications["AuthenticationSuccess"])
		else
			local err = result and Errors.Authentication[result] or Errors.Default
			ScreenManager:OpenScreen(ErrorOverlay(err), false)
		end
	end

	local function validatePassword(silent)
		PasswordProcessingCount = PasswordProcessingCount + 1

		local reason = nil
		isPasswordValid, reason = AccountManager:IsValidPasswordAsync(myUsername or "", myPassword)
		if isPasswordValid then
			if myUsername and #myUsername > 0 then
				GuiService.SelectedCoreObject = this.SignInButton
			else
				GuiService.SelectedCoreObject = this.UsernameSelection
			end
		elseif isPasswordValid == false then
			if not silent then
				-- web returns long strings on password error. Lets create our own error type
				GuiService.SelectedCoreObject = this.PasswordSelection
				local err = Errors.Default;
				if reason then
					err = Errors.SignIn.InvalidPassword
					err.Msg = reason
				end
				ScreenManager:OpenScreen(ErrorOverlay(err), false)
			end
		else -- Http failure

		end

		PasswordProcessingCount = PasswordProcessingCount - 1
	end

	local function validateUsername(silent)
		UserNameProcessingCount = UserNameProcessingCount + 1

		-- 1. Check if valid user name
		local reason = nil
		isUsernameValid, reason = AccountManager:IsValidUsernameAsync(myUsername)
		if isUsernameValid then
			-- 2. if password set, need to recheck password rules
			if myPassword and #myPassword > 0 then
				validatePassword()
			else
				GuiService.SelectedCoreObject = this.PasswordSelection
			end
		elseif isUsernameValid == false then
			if not silent then
				GuiService.SelectedCoreObject = this.UsernameSelection
				-- NOTE: Web has changed username rules and the result of the ErrorMessage in the endpoint call.
				-- We key check reason vs. our current error table, if we don't find a key, we create an error out
				-- of the reason returned. If there is no reason, use default. This covers both old and new behavior
				local err = nil
				if Errors.SignIn[reason] then
					err = Errors.SignIn[reason]
				elseif reason then
					err = { Title = Strings:LocalizedString("InvalidUsernameTitle"), Msg = reason }
				else
					err = Errors.Default
				end
				ScreenManager:OpenScreen(ErrorOverlay(err), false)
			end
		else -- Http Request failed

		end

		UserNameProcessingCount = UserNameProcessingCount - 1
	end

	local function onUsernameChanged(text)
		myUsername = text
		if #myUsername > 0 then
			validateUsername()
		else
			GuiService.SelectedCoreObject = this.UsernameSelection
			isUsernameValid = false
		end
	end

	local function onPasswordChanged(text)
		myPassword = text
		if #myPassword > 0 then
			validatePassword()
		else
			GuiService.SelectedCoreObject = this.PasswordSelection
			isPasswordValid = false
		end
	end


	local isSettingCredentials = false
	this.SignInButton.MouseButton1Click:connect(function()
		if isSettingCredentials then return end
		isSettingCredentials = true

		local function stillValidatingUserInfo()
			return UserNameProcessingCount > 0 or PasswordProcessingCount > 0
		end
		local function awaitValidatingUserInfo()
			while stillValidatingUserInfo() do wait() end
		end

		SoundManager:Play('ButtonPress')


		local processingFunctions = nil
		-- Wait for current validation to finish
		if stillValidatingUserInfo() then

			processingFunctions = { awaitValidatingUserInfo }

		-- Retry our username and password validation
		elseif isUsernameValid == nil or isPasswordValid == nil then

			processingFunctions = {}
			if isUsernameValid == nil then
				table.insert(processingFunctions, function() validateUsername(true) awaitValidatingUserInfo() end)
			end
			if isPasswordValid == nil then
				table.insert(processingFunctions, function() validatePassword(true) awaitValidatingUserInfo() end)
			end

		end

		if processingFunctions then
			local processingLoader = LoadingWidget(
				{ Parent = ModalOverlay },
				processingFunctions)

			-- NOTE: may need to get a separate overlay for this spinner
			-- Also should we disable input while overlay is active?
			ModalOverlay.Parent = GuiRoot

			processingLoader:AwaitFinished()
			processingLoader:Cleanup()
			processingLoader = nil

			ModalOverlay.Parent = nil
		end

		if isUsernameValid and isPasswordValid then
			createAndSetCredentialsAsync()
		elseif isUsernameValid == false or isPasswordValid == false then
			local err = Errors.SignIn.NoUsernameOrPasswordEntered
			ScreenManager:OpenScreen(ErrorOverlay(err), false)
		else -- Http failed to validate your password or username
			local err = Errors.SignIn.ConnectionFailed
			ScreenManager:OpenScreen(ErrorOverlay(err), false)
		end
		isSettingCredentials = false
	end)


	--[[ Public API ]]--
	-- override
	local baseFocus = this.Focus
	function this:Focus()
		baseFocus(self)
		usernameChangedCn = this.UsernameObject.OnTextChanged:connect(onUsernameChanged)
		passwordChangedCn = this.PasswordObject.OnTextChanged:connect(onPasswordChanged)
	end

	-- override
	local baseRemoveFocus = this.RemoveFocus
	function this:RemoveFocus()
		baseRemoveFocus(self)
		Utility.DisconnectEvent(usernameChangedCn)
		Utility.DisconnectEvent(passwordChangedCn)
	end

	return this
end

return createSetAccountCredentialsScreen
