using Microsoft.AspNetCore.Mvc;
using Berufsschule_HAM.Models;
using Berufsschule_HAM.Services;
using System.Text.Json;
using Novell.Directory.Ldap;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ModelBinding;

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

    public AssetsController(LdapService ldap, ILogger<AssetsController> logger)
    {
        _ldap = ldap ?? throw new ArgumentNullException(nameof(ldap));
        _logger = logger;
    }

    [HttpGet("GetAll")]
    public async Task<AssetsIndexResponseModel> GetAllAssetModelAsync()
    {
        AssetsIndexResponseModel result;
        try
        {
            var assetList = await _ldap.ListDeviceAsync();
            result = new AssetsIndexResponseModel(successful: true, assetsModel: assetList);
        }
        catch (Exception e)
        {
            result = new AssetsIndexResponseModel(successful: false, exception: e.Message);
        }
        
        return result;
    }

    [HttpPost("Create")]
    public async Task<AssetsCreateResponseModel> Create(AssetsCreateRequestModel assetModel)
    {
        AssetsCreateResponseModel result;
        if (assetModel is null)
        {
            result = new AssetsCreateResponseModel(
                successful: false,
                exception: "Unable to create an asset because the AssetsCreateRequestModel is null.");

            return result;
        }

        try
        {
            LdapAttributeSet attributeSet =
            [
                new LdapAttribute("objectClass", new[] {"top", "device", "extensibleObject"}),
            ];

            if (assetModel.Cn != null)
            {
                attributeSet.Add(new LdapAttribute("cn", assetModel.Cn));
            }
            if (assetModel.SerialNumber != null)
            {
                attributeSet.Add(new LdapAttribute("serialNumber", assetModel.SerialNumber));
            }
            if (assetModel.Location != null)
            {
                attributeSet.Add(new LdapAttribute("l", assetModel.Location));
            }
            if (assetModel.Owner != null)
            {
                var ownerDn = $"uid={assetModel.Owner}";
                attributeSet.Add(new LdapAttribute("owner", ownerDn));
            }
            if (assetModel.Name != null)
            {
                attributeSet.Add(new LdapAttribute("name", assetModel.Name));
            }
            if (assetModel.Description != null)
            {
                var assetDescription = new AssetDescription();

                if (assetModel.Description.Type != null)
                {
                    assetDescription.Type = assetModel.Description.Type;
                }
                if (assetModel.Description.Purchase != null)
                {
                    var purchase = new AssetPurchase();
                    if (assetModel.Description.Purchase.PurchasedAt != null)
                    {
                        purchase.PurchasedAt = assetModel.Description.Purchase.PurchasedAt;
                    }
                    if (assetModel.Description.Purchase.PurchaseDate != null)
                    {
                        purchase.PurchaseDate = assetModel.Description.Purchase.PurchaseDate;
                    }
                    if (assetModel.Description.Purchase.PurchasedBy != null)
                    {
                        purchase.PurchasedBy = assetModel.Description.Purchase.PurchasedBy;
                    }
                    if (assetModel.Description.Purchase.PurchaseValue != null)
                    {
                        purchase.PurchaseValue = assetModel.Description.Purchase.PurchaseValue;
                    }

                    assetDescription.Purchase = purchase;
                }

                attributeSet.Add(new LdapAttribute("description", JsonSerializer.Serialize(assetDescription)));
            }

            await _ldap.CreateAsset(attributeSet);
            result = new AssetsCreateResponseModel(successful: true);
        }
        catch (Exception e)
        {
            result = new AssetsCreateResponseModel(successful: false, exception: e.Message);
        }

        return result;
    }

    [HttpDelete("Delete")]
    public async Task<AssetsDeleteResponseModel> Delete([BindRequired] string cn)
    {        
        AssetsDeleteResponseModel response;
        return await Task.Run(async () =>
        {
            if (cn is null)
            {
                response = new AssetsDeleteResponseModel(successful: false, exception: "Cn was not provided.");
                return response;
            }

            try
            {
                await _ldap.DeleteAssetAsync(cn);
                response = new AssetsDeleteResponseModel(true);
            }
            catch (Exception e)
            {
                response = new AssetsDeleteResponseModel(successful: false, exception: e.Message);
            }

            return response;
        });
    }

    [HttpPatch("Update")]
    public async Task<AssetsUpdateResponseModel> Update(AssetsModifyRequestModel requestModel)
    {
        AssetsUpdateResponseModel result;
        if (requestModel is null)
        {
            result = new AssetsUpdateResponseModel(
                successful: false,
                exception: "Unable to update an asset because the AssetsModifyRequestModel is null");

            return result;
        }

        try
        {
            var cn = requestModel.Cn;
            if (requestModel.NewCn is not null)
            {
                await _ldap.UpdateAsset(cn, "cn", requestModel.NewCn);
                cn = requestModel.NewCn;
            }
            if (requestModel.Location is not null)
            {
                await _ldap.UpdateAsset(cn, "l", requestModel.Location);
            }
            if (requestModel.Name is not null)
            {
                await _ldap.UpdateAsset(cn, "name", requestModel.Name);
            }
            if (requestModel.Owner is not null)
            {
                await _ldap.UpdateAsset(cn, "owner", requestModel.Owner);
            }
            if (requestModel.SerialNumber is not null)
            {
                await _ldap.UpdateAsset(cn, "serialNumber", requestModel.SerialNumber);
            }
            if (requestModel.Description is not null)
            {
                AssetModel? asset = null;
                if (requestModel.Description.Type is null)
                {
                    asset ??= await _ldap.GetAssetByCnAsync(cn);
                    requestModel.Description.Type = asset.Description?.Type;
                }
                if (requestModel.Description.Purchase is not null)
                {
                    asset ??= await _ldap.GetAssetByCnAsync(cn);
                    requestModel.Description.Purchase.PurchasedAt ??= asset.Description?.Purchase?.PurchasedAt;
                    requestModel.Description.Purchase.PurchaseDate ??= asset.Description?.Purchase?.PurchaseDate;
                    requestModel.Description.Purchase.PurchasedBy ??= asset.Description?.Purchase?.PurchasedBy;
                    requestModel.Description.Purchase.PurchaseValue ??= asset.Description?.Purchase?.PurchaseValue;
                }

                if (requestModel.Description.Purchase is null)
                {
                    asset ??= await _ldap.GetAssetByCnAsync(cn);
                    requestModel.Description.Purchase = asset.Description?.Purchase;
                }
                await _ldap.UpdateAsset(cn, "description", JsonSerializer.Serialize(requestModel.Description));
            }

            result = new AssetsUpdateResponseModel(successful: true);
        }
        catch (Exception e)
        {
            result = new AssetsUpdateResponseModel(successful: false, exception: e.Message);
        }
        
        return result;
    }
}