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

using Plugin.Firebase.Firestore;

using DXLib.Data.Model;

namespace iStatVball3;

/*
 * Represents an individual permission, which defines the access level of a user for a specific season. Stored as an
 * array of maps in the parent User document NOT as a root collection.
 */
public class Permission : DXModel
{
	/* Constants */
	
	// Available permission levels (MUST map to User.LevelType)
	public enum LevelType
	{
		None = User.LevelType.None,

		Media,
		Player,
		Fan,
		Statistician,
		Coach,
		Director,

		Owner,			// Only for Permissions, not in User
		ALL				// Only used internally for queries
	};

	// Permission level keys
	public const string MediaKey = "media";
	public const string PlayerKey = "player";
	public const string FanKey = "fan";
	public const string StatisticianKey = "statistician";
	public const string CoachKey = "coach";
	public const string DirectorKey = "director";

    /* Properties */
    [FirestoreProperty("TeamId")] public string TeamId { get; set; }
    [FirestoreProperty("OrganizationId")] public string OrganizationId { get; set; }

    [FirestoreProperty("LevelEnum")] public int LevelEnum { get; set; }

    // ID of team/coach/player/etc matching level
    [FirestoreProperty("EntityId")] public string EntityId { get; set; }

	/* Ignored */
	public LevelType Level { get => (LevelType)LevelEnum; init => LevelEnum = (int)value; }

	public bool IsOwner => Level == LevelType.Owner;
	public bool HasPlayer => Level is LevelType.Player or LevelType.Fan;

	/* Methods */
	public Permission()
	{}

	// Tests equality based on unique identifier
	public override bool Equals( object obj )
	{
		return (obj is Permission perm) && perm.UniqueId.Equals( UniqueId );
	}

	// Generates unique hash code (required)
	public override int GetHashCode()
	{
		return UniqueId.GetHashCode();
	}

	// Maps specified requested role to internal permission level
	public static LevelType LevelFromRole( string role )
	{
		return role switch
		{
			DirectorKey => LevelType.Director,
			CoachKey => LevelType.Coach,
			StatisticianKey => LevelType.Statistician,
			FanKey => LevelType.Fan,
			PlayerKey => LevelType.Player,
			MediaKey => LevelType.Media,

			_ => LevelType.None,
		};
	}

	// Grants all pending permissions below subscribed level for specified user
	public static async Task<bool> Grant( User user, LevelType grantLevel )
	{
		bool granted = false;

		string username = user.Username;
		
		// Find any open invites
		IEnumerable<Invite> invites = await Invite.ReadByUsername( username );

		// Grant permission for all invites below subscribed level
		foreach ( Invite invite in invites )
		{
			LevelType level = invite.Level;

			if ( level <= grantLevel )
			{
				string entityId = invite.EntityId;

				// Create permission
				await Create( user, new PermissionKeys
				{
					TeamId = invite.TeamId,
					OrganizationId = invite.OrganizationId,

					Level = level,
					EntityId = entityId
				});

				// Update link status
				await Link.Update( level, entityId, username, Link.StatusType.Granted );

				// No longer need invite
				await invite.Delete();

				granted = true;
			}
		}

		return granted;
	}

	/* CRUD */

	// Creates new permission (pending or granted) for existing user
	public static async Task Create( User user, PermissionKeys keys, IWriteBatch batch = null )
	{
		Permission perm = new()
		{
			TeamId = keys.TeamId,
			OrganizationId = keys.OrganizationId,

			Level = keys.Level,
			EntityId = keys.EntityId
		};

		// Persist
		await user.CreatePermission( perm, batch );
	}

	// Adds owner permission to given user for specified Team
	public static async Task CreateOwner( User user, Team team, IWriteBatch batch = null )
	{
		PermissionKeys keys = new()
		{
			Team = team,

			TeamId = team.UniqueId,
			OrganizationId = team.Organization.UniqueId,

			Level = LevelType.Owner,
			EntityId = team.UniqueId
		};

		// Persist
		await Create( user, keys, batch );

		// Must save for later deletion
		team.OwnerId = user.UniqueId;
	}

	// Removes permission from user corresponding to account link
	public static async Task Delete( string username, PermissionKeys keys )
	{
		User user = await User.ReadByUsername( username );

		user?.DeletePermission( keys.EntityId, keys.Level );
	}

	// Removes team owner permission from owning user
	public static async Task DeleteOwner( Team team )
	{
		User owner = await User.Read( team.OwnerId );

		// Remove
		await owner?.DeletePermission( team.UniqueId, LevelType.ALL )!;
	}

	// Removes all permissions to specified entity from linked user
	public static async Task Delete( string entityId, Link link )
	{
		if ( link != null )
		{
			string username = link.Username;

			// Remove all permission levels
			await Delete( username, new PermissionKeys
			{
				EntityId = entityId,
				Level = LevelType.ALL
			});

			// Delete pending invites
			await Invite.Delete( username, entityId, LevelType.ALL );
		}
	}

	// Removes all permissions to specified entity from all users in list
	public static async Task Delete( string entityId, IList<Link> links )
	{
		foreach ( Link link in links )
		{
			await Delete( entityId, link );
		}
	}
}

//
