@namespace Oqtane.Modules.Controls
@using System.Text.Json
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@inject IUserRoleService UserRoleService
@inject IStringLocalizer<PermissionGrid> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer

@if (_permissions != null)
{
<div class="container">
    <div class="row">
        <div class="col">
            <table class="table table-borderless">
                <tbody>
                    <tr>
                        <th scope="col">@Localizer["Role"]</th>
                        @foreach (var permissionname in _permissionnames)
                        {
							<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
						}
                    </tr>
                    @foreach (Role role in _roles)
                    {
                        <tr>
                            <td>@role.Name</td>
                            @foreach (var permissionname in _permissionnames)
                            {
                                <td style="text-align: center;">
									<TriStateCheckBox Value=@GetPermissionValue(permissionname, role.Name, -1) Disabled="@GetPermissionDisabled(permissionname, role.Name)" OnChange="@(e => PermissionChanged(e, permissionname, role.Name, -1))" />
                                </td>
                            }
                        </tr>
                    }
                </tbody>
            </table>
            <br />
        </div>
    </div>
    <div class="row">
        <div class="col">
            @if (_users.Count != 0)
            {
                <div class="row">
                    <div class="col">
                    </div>
                </div>
                <table class="table table-borderless">
                    <thead>
                        <tr>
                            <th scope="col">@Localizer["User"]</th>
								@foreach (var permissionname in _permissionnames)
								{
									<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
								}
							</tr>
                    </thead>
                    <tbody>
                        @foreach (User user in _users)
                        {
                            <tr>
                                <td>@user.DisplayName</td>
                                @foreach (var permissionname in _permissionnames)
                                {
                                    <td style="text-align: center; width: 1px;">
										<TriStateCheckBox Value=@GetPermissionValue(permissionname, "", user.UserId) Disabled="@GetPermissionDisabled(permissionname, "")" OnChange="@(e => PermissionChanged(e, permissionname, "", user.UserId))" />
                                    </td>
                                }
                            </tr>
                        }
                    </tbody>
                </table>
                <br />
            }
        </div>
    </div>
    <div class="row">
        <div class="col-11">
			<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["Username.Enter"]" @ref="_user" />
		</div>
		<div class="col-1">
			<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
        </div>
    </div>
    <div class="row">
        <div class="col">
            <ModuleMessage Type="MessageType.Warning" Message="@_message" />
        </div>
    </div>
</div>
}

@code {
	private List<string> _permissionnames;
	private List<Permission> _permissions;
	private List<Role> _roles;
	private List<User> _users = new List<User>();
	private AutoComplete _user;
	private string _message = string.Empty;

	[Parameter]
	public string EntityName { get; set; }

	[Parameter]
	public string PermissionNames { get; set; }

	[Parameter]
	public string Permissions { get; set; } // deprecated - use PermissionList instead

	[Parameter]
	public List<Permission> PermissionList { get; set; }

	protected override async Task OnInitializedAsync()
	{
		if (!string.IsNullOrEmpty(Permissions))
		{
			PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);			
		}

		_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
		if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
		{
			_roles.RemoveAll(item => item.Name == RoleNames.Host);
		}

		// get permission names
		if (string.IsNullOrEmpty(PermissionNames))
		{
			_permissionnames = new List<string>();
			_permissionnames.Add(Shared.PermissionNames.View);
			_permissionnames.Add(Shared.PermissionNames.Edit);
		}
		else
		{
			_permissionnames = PermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
		}

		// initialize permissions
		_permissions = new List<Permission>();
		if (PermissionList != null && PermissionList.Any())
		{
			foreach (var permission in PermissionList)
			{
				_permissions.Add(permission);
				if (permission.UserId != null)
				{
					if (!_users.Any(item => item.UserId == permission.UserId.Value))
					{
						_users.Add(await UserService.GetUserAsync(permission.UserId.Value, ModuleState.SiteId));
					}
				}
			}
		}
		else
		{
			foreach (string permissionname in _permissionnames)
			{
				// permission names can be in the form of "EntityName:PermissionName:Roles"
				if (permissionname.Contains(":"))
				{
					var segments = permissionname.Split(':');
					if (segments.Length == 3)
					{
						foreach (var role in segments[2].Split(';'))
						{
							_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], role, null, true));
						}
						// ensure admin access
						if (!_permissions.Any(item => item.EntityName == segments[0] && item.PermissionName == segments[1] && item.RoleName == RoleNames.Admin))
						{
							_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], RoleNames.Admin, null, true));
						}
					}
				}
				else
				{
					_permissions.Add(new Permission(ModuleState.SiteId, EntityName, permissionname, RoleNames.Admin, null, true));
				}
			}
		}
	}

	private string GetPermissionName(string permissionName)
	{
		return (permissionName.Contains(":")) ? permissionName.Split(':')[1] : permissionName;
	}

	private string GetEntityName(string permissionName)
	{
		return (permissionName.Contains(":")) ? permissionName.Split(':')[0] : EntityName;
	}

	private string DisplayPermissionName(string permissionName)
	{
		var name = Localizer[GetPermissionName(permissionName)].ToString();
		name += " " + Localizer[GetEntityName(permissionName)].ToString();
		return name;
	}

	private bool? GetPermissionValue(string permissionName, string roleName, int userId)
	{
		bool? isauthorized = null;
		if (roleName != "")
		{
			var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
			if (permission != null)
			{
				isauthorized = permission.IsAuthorized;
			}
		}
		else
		{
			var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
			if (permission != null)
			{
				isauthorized = permission.IsAuthorized;
			}			
		}
		return isauthorized;
	}

	private bool GetPermissionDisabled(string permissionName, string roleName)
	{
		if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
		{
			return true;
		}
		else
		{
			if (GetEntityName(permissionName) != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
	}

	private void PermissionChanged(bool? value, string permissionName, string roleName, int userId)
	{
		if (roleName != "")
		{
			var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
			if (permission != null)
			{
				_permissions.Remove(permission);
			}
			if (value != null)
			{
				_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), roleName, null, value.Value));
			}
		}
		else
		{
			var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
			if (permission != null)
			{
				_permissions.Remove(permission);
			}
			if (value != null)
			{
				_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), null, userId, value.Value));
			}
		}
	}

	private async Task<Dictionary<string, string>> GetUsers(string filter)
	{
		var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
		return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
			.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
	}

	private async Task AddUser()
	{
		if (!string.IsNullOrEmpty(_user.Key))
		{
			var user = await UserService.GetUserAsync(int.Parse(_user.Key), ModuleState.SiteId);
			if (user != null && !_users.Any(item => item.UserId == user.UserId))
			{
				_users.Add(user);
			}
		}
		else
		{
			_message = Localizer["Message.Username.DontExist"];
		}
		_user.Clear();
	}

	public string GetPermissions()
	{
		ValidatePermissions();
		return JsonSerializer.Serialize(_permissions);
	}

	public List<Permission> GetPermissionList()
	{
		ValidatePermissions();
		return _permissions;
	}

	private void ValidatePermissions()
	{
		// remove deny all users, unauthenticated, and registered users
		var permissions = _permissions.Where(item => !item.IsAuthorized && 
			(item.RoleName == RoleNames.Everyone || item.RoleName == RoleNames.Unauthenticated || item.RoleName == RoleNames.Registered)).ToList();
		foreach (var permission in permissions)
		{
			_permissions.Remove(permission);
		}
		if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
		{
			// remove deny administrators and host users
			permissions = _permissions.Where(item => !item.IsAuthorized &&
				(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)).ToList();
			foreach (var permission in permissions)
			{
				_permissions.Remove(permission);
			}
			foreach (var permissionname in _permissionnames)
			{
				// add administrators role if neither host or administrator is assigned
				if (!_permissions.Any(item => item.EntityName == GetEntityName(permissionname) && item.PermissionName == GetPermissionName(permissionname) &&
					(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)))
				{
					_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionname), GetPermissionName(permissionname), RoleNames.Admin, null, true));
				}
			}
		}
    }
}
