/*
 * Copyright (c)2009-2025 DemiVision, LLC. All Rights Reserved. The information
 * herein is the CONFIDENTIAL and PROPRIETARY information of DemiVision, LLC.
 */

using Plugin.Firebase.Auth;
using Plugin.Firebase.Core.Exceptions;

using DXLib.Log;
using DXLib.Utils;

namespace DXLib.Data;

/*
 * Provides authorization functionality for user account creation and sign in. Auth is performed remotely via the
 * Firebase Auth SDK.
 */
public static class DXAuth
{
	/* Constants */

	// All possible auth call results
	public enum AuthResult
	{
		Unknown,
		Success,

		ErrorExists,
		ErrorNotFound,
		ErrorPassword,
		ErrorTimeout,
		ErrorUnknown
	};

	// Timeout duration (ms) for all calls
	private const int Timeout = (60 * 1000);

	/* Properties */
	private static IFirebaseAuth Auth => CrossFirebaseAuth.Current;

	/* Methods */

	// Creates new account with specified credentials, will callback listener once complete
	public static async void CreateAccount( string username, string password, Action<AuthResult,string> callback )
	{
		DXTimer timer = null;

		AuthResult result;
		string resultStr = null;

		try
		{
			// Timeout if call never returns
			timer = DXTimer.Delay( Timeout, () =>
			{
				callback.Invoke( AuthResult.ErrorTimeout, null );
			});

			// Block waiting for Firebase auth
			IFirebaseUser user = await Auth.CreateUserAsync( username, password );

			// Error
			if ( user == null )
			{
				result = AuthResult.ErrorUnknown;
			}
			// Success, validate email
			else
			{
				VerifyEmail( user );

				result = AuthResult.Success;
				resultStr = user.Uid;
			}
		}
		// Auth error
		catch ( FirebaseAuthException ex )
		{
			switch ( ex.Reason )
			{
				// Account already exists
				case FIRAuthError.EmailAlreadyInUse:
				{
					result = AuthResult.ErrorExists;
					resultStr = username;
					break;
				}
				// Unknown
				default:
				{
					result = AuthResult.ErrorUnknown;
					resultStr = ex.Reason.ToString();

					DXLog.Exception( $"auth.create.{ex.Reason}", ex );
					break;
				}
			}
		}
		// Unknown error
		catch ( Exception ex )
		{
			result = AuthResult.ErrorUnknown;
			resultStr = ex.Message;
		}

		// No timeout
		timer?.Stop();
		
		// Notify listener
		callback.Invoke( result, resultStr );
	}

	// Sends account verification email to specified user
	public static void VerifyEmail( IFirebaseUser user )
	{
		user.SendEmailVerificationAsync();
	}

	// Logs in user with specified credentials, will callback listener once complete 
	public static async void Login( string username, string password, Action<AuthResult,string,IFirebaseUser> callback )
	{
		DXTimer timer = null;
		
		AuthResult result;
		string resultStr = null;

		IFirebaseUser user = null;

		try
		{
			// Timeout if call never returns
			timer = DXTimer.Delay( Timeout, () =>
			{
				callback.Invoke( AuthResult.ErrorTimeout, null, null );
			});

            // Block waiting for Firebase auth
            user = await Auth.SignInWithEmailAndPasswordAsync( username, password );

			// Error
			if ( user == null )
			{
				result = AuthResult.ErrorUnknown;
			}
			// Success (email may not yet be verified)
			else
			{
				result = AuthResult.Success;
				resultStr = user.Uid;
			}
		}
		// Auth error
		catch ( FirebaseAuthException ex )
		{
			switch ( ex.Reason )
			{
				// Account not found
				case FIRAuthError.UserNotFound:
				{
					result = AuthResult.ErrorNotFound;
					resultStr = username;
					break;
				}
				// Bad password
				case FIRAuthError.WrongPassword:
				{
					result = AuthResult.ErrorPassword;
					resultStr = username;
					break;
				}
				// Unknown
				default:
				{
					result = AuthResult.ErrorUnknown;
					resultStr = ex.Reason.ToString();

					DXLog.Exception( $"auth.login.{ex.Reason}", ex );
					break;
				}
			}
		}
		// Unknown error
		catch ( Exception ex )
		{
			result = AuthResult.ErrorUnknown;
			resultStr = ex.Message;
		}

		// No timeout
		timer?.Stop();
		
		// Notify listener
		callback.Invoke( result, resultStr, user );
	}

	// Requests password reset instructions be sent to specified email address
	public static async void ResetPassword( string email, Action<AuthResult> callback )
	{
		DXTimer timer = null;
		
		AuthResult result = AuthResult.Unknown;

		try
		{
			// Timeout if call never returns
			timer = DXTimer.Delay( Timeout, () =>
			{
				callback.Invoke( AuthResult.ErrorTimeout );
			});

			// Block waiting for Firebase to send email
			await Auth.SendPasswordResetEmailAsync( email );

			// Success
			result = AuthResult.Success;
		}
		// Auth error
		catch ( FirebaseAuthException ex )
		{
			switch ( ex.Reason )
			{
				// Account not found
				case FIRAuthError.UserNotFound:
				{
					result = AuthResult.ErrorNotFound;
					break;
				}
			}
		}
		// Error
		catch ( Exception )
		{
			result = AuthResult.ErrorUnknown;
		}

		// No timeout
		timer?.Stop();
		
		// Notify listener
		callback.Invoke( result );
	}

	// Logs out currently active user
	public static async void Logout( Action<AuthResult> callback )
	{
		DXTimer timer = null;
		
		AuthResult result = AuthResult.Unknown;

		try
		{
			AuthResult localResult = result;
			
			// Timeout if call never returns
			timer = DXTimer.Delay( Timeout, () =>
			{
				if ( localResult == AuthResult.Unknown )
				{
					callback.Invoke( AuthResult.ErrorTimeout );
				}
			});

			// Non-blocking Firebase call
			await Auth.SignOutAsync();

			result = AuthResult.Success;
		}
		// Error
		catch ( Exception )
		{
			callback.Invoke( AuthResult.ErrorUnknown );
		}

		// No timeout
		timer?.Stop();
		
		// Notify listener
		callback.Invoke( result );
	}

	// Permanently delete current auth account
	public static async Task<bool> DeleteAccount()
	{
		try
		{
			// Block on delete
			await Auth.CurrentUser.DeleteAsync();

			return true;
		}
		// Error
		catch ( Exception ex )
		{
			DXLog.Exception( "auth.delete", ex );

			return false;
        }
	}
}

//
