// Controllers/UsersController.cs
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;

[Authorize]
[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("Index")]
    public async Task<IEnumerable<UserModel>> Index(UsersIndexRequestModel 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];
        }
    }

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

    [HttpGet("Create")]
    public async Task<bool> Create(string cn, string sn, string? title, string? uid, string userPassword, string? description, string jpegPhoto)
    {
        try
        {
            jpegPhoto ??= Convert.ToBase64String(System.IO.File.ReadAllBytes("wwwroot/user_default.jpeg")); // TODO: cleanup - make this a config setting
            uid ??= sn.ToLower() + cn.ToLower();
            title ??= "";
            description ??= "{}";
            if (!userPassword.StartsWith('{'))
            {
                byte[] passwordBytes = Encoding.UTF8.GetBytes(userPassword);
                byte[] hashedPassword = SHA256.HashData(passwordBytes);
                userPassword = "{SHA256}" + Convert.ToBase64String(hashedPassword);
            }

            LdapAttributeSet attributeSet =
            [
                new LdapAttribute("objectClass", "inetOrgPerson"),
                new LdapAttribute("cn", cn),
                new LdapAttribute("sn", sn),
                new LdapAttribute("title", title),
                new LdapAttribute("uid", uid),
                new LdapAttribute("jpegPhoto", jpegPhoto),
                new LdapAttribute("description", description),
                new LdapAttribute("userPassword", userPassword),
            ];
            await _ldap.CreateUser(uid, attributeSet);
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError("Unable to create user: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
            return false;
        }
    }
    
    [HttpPost("Update")]
    public async Task<bool> Update([FromBody]UsersModifyRequestModel requestModel)
    {
        if (requestModel is null)
        {
            _logger.LogError("Unable to update a user because the UsersModifyRequestModel is null");
            return false;
        }
        string uid = requestModel.uid;
        UserModel? user = null;
        if (requestModel.Cn is not null)
        {
            await _ldap.UpdateUser(uid, "cn", requestModel.Cn);
            user ??= await _ldap.GetUserByUidAsync(uid);
            string newUid = user.Sn?.ToLower() + requestModel.Cn.ToLower();
            await _ldap.UpdateUser(uid, "uid", newUid);
            uid = newUid;
        }
        if (requestModel.Sn is not null)
        {
            await _ldap.UpdateUser(uid, "sn", requestModel.Sn);
            user ??= await _ldap.GetUserByUidAsync(uid);
            string newUid = requestModel.Sn.ToLower() + user.Cn?.ToLower();
            await _ldap.UpdateUser(uid, "uid", newUid);
            uid = newUid;
        }
        if (requestModel.NewUid is not null)
        {
            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", requestModel.Description);
        }
        if (requestModel.JpegPhoto is not null)
        {
            await _ldap.UpdateUser(uid, "jpegPhoto", requestModel.JpegPhoto);
        }
        if (requestModel.UserPassword is not null)
        {
            await _ldap.UpdateUser(uid, "userPassword", requestModel.UserPassword);
        }
        return true;
    }
}
