/* Copyright (C) Itseez3D, Inc. - All Rights Reserved
* You may not use this file except in compliance with an authorized license
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* UNLESS REQUIRED BY APPLICABLE LAW OR AGREED BY ITSEEZ3D, INC. 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.
* Written by Itseez3D, Inc. <support@avatarsdk.com>, October 2026
*/

using System;
using UnityEngine;
using UnityEngine.UI;

namespace AvatarSDKMove
{
	public class AccountPanel : MonoBehaviour
	{
		[Header("Sign In")]
		public GameObject signInView;
		public InputField emailInputField;
		public Button sendCodeButton;
		public InputField codeInputField;
		public Button signInButton;

		[Header("Account")]
		public GameObject accountBar;
		public Button accountButton;
		public GameObject accountView;
		public Text emailText;
		public Text balanceText;
		public Image balanceCoinIcon;
		public Text balanceDetailsText;
		public Button topUpButton;
		public Button logOutButton;

		public Text statusText;

		private readonly Connection connection = new Connection();
		private Image background;
		private bool isCodeRequested = false;
		private bool isBusy = false;
		private bool isExpanded = false;

		public event Action<bool> SignedInChanged;
		public event Action<UserInfoData> UserInfoChanged;

		public bool IsSignedIn { get; private set; }

		public UserInfoData UserInfo { get; private set; }

		private void Awake()
		{
			IsSignedIn = AuthUtils.LoadSession() != null;
			background = GetComponent<Image>();

			accountButton.onClick.AddListener(OnAccountClicked);
			sendCodeButton.onClick.AddListener(OnSendCodeClicked);
			signInButton.onClick.AddListener(OnSignInClicked);
			topUpButton.onClick.AddListener(OnTopUpClicked);
			logOutButton.onClick.AddListener(OnLogOutClicked);
			AuthUtils.SessionChanged += UpdateView;
		}

		private void Start()
		{
			UpdateView();
		}

		private void OnDestroy()
		{
			accountButton.onClick.RemoveListener(OnAccountClicked);
			sendCodeButton.onClick.RemoveListener(OnSendCodeClicked);
			signInButton.onClick.RemoveListener(OnSignInClicked);
			topUpButton.onClick.RemoveListener(OnTopUpClicked);
			logOutButton.onClick.RemoveListener(OnLogOutClicked);
			AuthUtils.SessionChanged -= UpdateView;
		}

		private void OnApplicationFocus(bool hasFocus)
		{
			if (hasFocus && IsSignedIn && !isBusy)
				RefreshUserInfo();
		}

		public async void RefreshUserInfo()
		{
			try
			{
				SetUserInfo(await connection.GetUserInfo());
			}
			catch (SignInRequiredException ex)
			{
				SetUserInfo(null);
				SetStatus(ex.Message);
			}
			catch (Exception ex)
			{
				Debug.LogWarningFormat("Could not update the account info: {0}", ex.Message);
			}
		}

		private void UpdateView()
		{
			UserSession session = AuthUtils.LoadSession();
			bool isSignedIn = session != null;
			bool isChanged = isSignedIn != IsSignedIn;
			IsSignedIn = isSignedIn;
			if (isChanged)
				isExpanded = false;

			UpdateLayout();

			if (isSignedIn)
			{
				isCodeRequested = false;
				codeInputField.text = string.Empty;
				emailText.text = AvatarSDKMoveServer.IsOverridden
					? string.Format("[{0}] {1}", AvatarSDKMoveServer.Name, session.email)
					: session.email;
				RefreshUserInfo();
			}
			else
			{
				SetUserInfo(null);
				if (string.IsNullOrEmpty(emailInputField.text))
					SetStatus("We will send a one-time code to your email.");
			}

			UpdateInteractable();

			if (isChanged)
				SignedInChanged?.Invoke(isSignedIn);
		}

		private void UpdateLayout()
		{
			bool isPanelVisible = !IsSignedIn || isExpanded;

			signInView.SetActive(!IsSignedIn);
			accountBar.SetActive(IsSignedIn);
			accountView.SetActive(IsSignedIn && isExpanded);
			if (statusText != null)
				statusText.gameObject.SetActive(isPanelVisible);
			if (background != null)
				background.enabled = isPanelVisible;
		}

		private void OnAccountClicked()
		{
			isExpanded = !isExpanded;
			UpdateLayout();
		}

		private void SetUserInfo(UserInfoData userInfo)
		{
			UserInfo = userInfo;
			balanceText.text = userInfo == null ? string.Empty : userInfo.balance.ToString();
			if (balanceCoinIcon != null)
				balanceCoinIcon.gameObject.SetActive(userInfo != null);
			if (balanceDetailsText != null)
				balanceDetailsText.text = FormatBalanceDetails(userInfo);
			UserInfoChanged?.Invoke(userInfo);
		}

		private static string FormatBalanceDetails(UserInfoData userInfo)
		{
			if (userInfo == null)
				return string.Empty;
			if (userInfo.have_asdk_subscription)
				return "· subscription";
			if (userInfo.free_move_animations > 0)
				return string.Format("· {0} free animations", userInfo.free_move_animations);
			return string.Empty;
		}

		private void UpdateInteractable()
		{
			emailInputField.interactable = !isBusy;
			sendCodeButton.interactable = !isBusy;
			codeInputField.interactable = !isBusy && isCodeRequested;
			signInButton.interactable = !isBusy && isCodeRequested;
			logOutButton.interactable = !isBusy;
		}

		private void SetBusy(bool busy)
		{
			isBusy = busy;
			UpdateInteractable();
		}

		private async void OnSendCodeClicked()
		{
			string email = emailInputField.text.Trim();
			if (string.IsNullOrEmpty(email))
			{
				SetStatus("Enter your email address.");
				return;
			}

			SetBusy(true);
			SetStatus("Sending the code...");
			try
			{
				RequestCodeResult result = await connection.RequestAuthenticationCode(email);
				if (result.success)
				{
					isCodeRequested = true;
					SetStatus("A code has been sent to " + email + ".");
				}
				else if (result.retry_after > RequestCodeResult.RetryAfterNotProvided)
				{
					SetStatus(string.Format("Too many requests. Try again in {0:0} seconds.", result.retry_after));
				}
				else
				{
					SetStatus(string.IsNullOrEmpty(result.error) ? "Could not send the code." : result.error);
				}
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
				SetStatus("Could not send the code: " + ex.Message);
			}
			finally
			{
				SetBusy(false);
			}
		}

		private async void OnSignInClicked()
		{
			string code = codeInputField.text.Trim();
			if (string.IsNullOrEmpty(code))
			{
				SetStatus("Enter the code from the email.");
				return;
			}

			SetBusy(true);
			SetStatus("Signing in...");
			try
			{
				AuthResult result = await connection.SubmitAuthenticationCode(emailInputField.text.Trim(), code);
				SetStatus(result.success ? string.Empty : "The code is invalid or expired.");
			}
			catch (Exception ex)
			{
				Debug.LogException(ex);
				SetStatus("Could not sign in: " + ex.Message);
			}
			finally
			{
				SetBusy(false);
			}
		}

		private void OnTopUpClicked()
		{
			MetaPersonCreatorLink.OpenShop();
		}

		private void OnLogOutClicked()
		{
			AuthUtils.ClearSession();
		}

		private void SetStatus(string message)
		{
			if (statusText != null)
				statusText.text = message;
		}
	}
}
