using Berufsschule_HAM.Services;
using Microsoft.AspNetCore.Mvc;
using Novell.Directory.Ldap;
using Berufsschule_HAM.Models;
using Berufsschule_HAM.Helpers;
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using System.Text.Json;
using System.Buffers.Text;

[Authorize(Roles = "CanManageUsers")]
[Route("[controller]")]
public class UsersController : Controller
{
    private readonly LdapService _ldap;
    private readonly ILogger<UsersController> _logger;

    public UsersController(LdapService ldap, ILogger<UsersController> logger)
    {
        _ldap = ldap;
        _logger = logger;
    }

    [HttpGet("GetAll")]
    public async Task<IEnumerable<UserModel>> GetAll(UsersGetAllRequestModel requestModel)
    {
        string? uid = requestModel.Uid;
        List<string> attributes = ["cn", "sn", "title", "uid", "jpegPhoto", "userPassword", "description"];
        if (!requestModel.Cn) attributes.Remove("cn");
        if (!requestModel.Sn) attributes.Remove("sn");
        if (!requestModel.Title) attributes.Remove("title");
        if (!requestModel.JpegPhoto) attributes.Remove("jpegPhoto");
        if (!requestModel.UserPassword) attributes.Remove("userPassword");
        if (!requestModel.Description) attributes.Remove("description");

        if (uid is null)
        {
            var users = await _ldap.ListUsersAsync([.. attributes]);
            return users;
        }
        else
        {
            var user = await _ldap.GetUserByUidAsync(uid, [.. attributes]);
            return [user];
        }
    }

    [HttpDelete("Delete")]
    public async Task<UsersDeleteRequestModel> Delete(string uid)
    {
        try
        {
            await SynchronizationHelper.SyncAssetOwnership(_ldap, uid, "");
            await _ldap.DeleteUserAsync(uid);
            return new UsersDeleteRequestModel(true);
        }
        catch (Exception ex)
        {
            return new UsersDeleteRequestModel(false, ex.Message);
        }
    }

    [HttpPost("Create")]
    public async Task<UsersCreateResponseModel> Create([FromBody] UsersCreateRequestModel requestModel)
    {
        if (requestModel is null)
        {
            return new() { Success = false, Exception = "The request model is null" };
        }
        try
        {
            string? jpegPhoto = requestModel.JpegPhoto;
            string? title = requestModel.Title;
            string userPassword = requestModel.UserPassword ?? "";
            UserDescription? description = requestModel.Description;
            jpegPhoto ??= ImageHelper.GetDefaultUserImageAsBase64();
            string uid = UsersHelper.CreateUsername(requestModel.Cn ?? "", requestModel.Sn ?? "");
            title ??= "";
            description ??= new() {Address = new(), BirthDate = "", Workplace = "", Groups = []};
            if (!userPassword.StartsWith('{'))
            {
                userPassword = await UsersHelper.HashPassword(_ldap, userPassword);
            }

            LdapAttributeSet attributeSet =
            [
                new LdapAttribute("objectClass", "inetOrgPerson"),
                new LdapAttribute("cn", requestModel.Cn),
                new LdapAttribute("sn", requestModel.Sn),
                new LdapAttribute("title", title),
                new LdapAttribute("uid", uid),
                new LdapAttribute("jpegPhoto", jpegPhoto),
                new LdapAttribute("description", JsonSerializer.Serialize(description)),
                new LdapAttribute("userPassword", userPassword),
            ];
            await _ldap.CreateUser(uid, attributeSet);
            return new(){Success = true, Uid = uid};
        }
        catch (Exception ex)
        {
            _logger.LogError("Unable to create user: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
            return new() {Success = false, Exception = ex.Message};
        }
    }

    [HttpPost("Update")]
    public async Task<UsersUpdateResponseModel> Update([FromBody] UsersModifyRequestModel requestModel)
    {
        if (requestModel is null)
        {
            _logger.LogError("Unable to update a user because the UsersModifyRequestModel is null");
            return new() { Success = false };
        }
        try
        {
            string uid = requestModel.Uid;
            UserModel? user = null;
            if (requestModel.NewUid is not null && requestModel.NewUid.Length > 0)
            {
                await SynchronizationHelper.SyncAssetOwnership(_ldap, uid, requestModel.NewUid);
                await _ldap.UpdateUser(uid, "uid", requestModel.NewUid);
                uid = requestModel.NewUid;
            }
            if (requestModel.Title is not null)
            {
                await _ldap.UpdateUser(uid, "title", requestModel.Title);
            }
            if (requestModel.Description is not null)
            {
                await _ldap.UpdateUser(uid, "description", JsonSerializer.Serialize(requestModel.Description));
            }
            if (requestModel.JpegPhoto is not null && requestModel.JpegPhoto.Length > 0)
            {
                await _ldap.UpdateUser(uid, "jpegPhoto", requestModel.JpegPhoto);
            }
            if (requestModel.UserPassword is not null && requestModel.UserPassword.Length > 0)
            {                
                requestModel.UserPassword = await UsersHelper.HashPassword(_ldap, requestModel.UserPassword);
                await _ldap.UpdateUser(uid, "userPassword", requestModel.UserPassword);
            }

            string newUid = uid;
            if (requestModel.Cn is not null)
            {
                await _ldap.UpdateUser(uid, "cn", requestModel.Cn);
                user ??= await _ldap.GetUserByUidAsync(uid);
                newUid = UsersHelper.CreateUsername(requestModel.Cn, user.Sn ?? "");
                
            }
            if (requestModel.Sn is not null)
            {
                await _ldap.UpdateUser(uid, "sn", requestModel.Sn);
                user ??= await _ldap.GetUserByUidAsync(uid);
                newUid = UsersHelper.CreateUsername(user.Cn ?? "", requestModel.Sn);
            }
            if (newUid.Length == 0)
            {
                throw new Exception("Username cannot be empty");
            }
            if (newUid != uid)
            {
                await SynchronizationHelper.SyncAssetOwnership(_ldap, uid, newUid);
                await _ldap.UpdateUser(uid, "uid", newUid);
                uid = newUid;
            }
            return new() { Success = true, NewUid = uid };
        } catch (Exception ex)
        {
            return new() { Success = false, Exception = ex.Message };
        }
    }

    [HttpPost("AddGroup")]
    public async Task<bool> AddGroup([FromBody]UsersAddGroupRequestModel requestModel)
    {
        try
        {
            UserModel userModel = await _ldap.GetUserByUidAsync(requestModel.Uid);
            userModel.Description ??= new() { Address = new(), BirthDate = "", Workplace = "" };
            userModel.Description.Groups ??= [];
            try
            {
                GroupModel group = await _ldap.GetGroupByCnAsync(requestModel.GroupUid, _ldap.GroupsAttributes);
            } catch (Exception)
            {
                return false;
            }
            userModel.Description.Groups.Add(requestModel.GroupUid);
            await _ldap.UpdateUser(requestModel.Uid, "description", JsonSerializer.Serialize(userModel.Description));
            return true;                
            } catch (Exception ex)
            {
                _logger.LogError("Unable to add group {} to user {}: {ex.Message} - {ex.StackTrace}", [requestModel.GroupUid, requestModel.Uid, ex.Message, ex.StackTrace]);
                return false;
            }
    }

    [HttpPost("RemoveGroup")]
    public async Task<bool> RemoveGroup([FromBody]UsersRemoveGroupRequestModel requestModel)
    {
        try
        {
            UserModel userModel = await _ldap.GetUserByUidAsync(requestModel.Uid);
            userModel.Description ??= new() { Address = new(), BirthDate = "", Workplace = "" };
            userModel.Description.Groups ??= [];
            userModel.Description.Groups.Remove(requestModel.GroupUid);
            await _ldap.UpdateUser(requestModel.Uid, "description", JsonSerializer.Serialize(userModel.Description));
            return true;
        } catch (Exception ex)
        {
            _logger.LogError("Unable to remove group {} from user {}: {ex.Message} - {ex.StackTrace}", [requestModel.GroupUid, requestModel.Uid, ex.Message, ex.StackTrace]);
            return false;
        }
    }
}
