using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Dacryptero.Notifications;

namespace Dacryptero.Web.Events.Receive;

public class DatabaseUnlock : ReceiveBase
{
    public string Database { get; private set; } = "";

    public string? MasterKey { get; private set; }

    public override async Task Execute(ExecuteArgs args)
    {
        var controller = args.Connection.DB.GetController(Database);
        if (controller is null)
        {
            args.SendNotification(Notification.Error(
                "Unlock Database failed",
                "Database not found"
            ));
            return;
        }

        if (MasterKey is not null)
        {
            ReadOnlyMemory<byte> key;
            try { key = Convert.FromHexString(FormatMasterKey(MasterKey)); }
            catch (Exception e)
            {
                Serilog.Log.Error(e, "Invalid master key");
                args.SendNotification(Notification.Error(
                    "Unlock Database failed",
                    "Invalid formated Master key"
                ));
                return;
            }

            var hash = System.Security.Cryptography.SHA512.HashData(key.Span);
            var original = controller.Config.RecoveryKeyHash;
            if (original.Length != hash.Length || !original.Span.StartsWith(hash))
            {
                args.SendNotification(Notification.Error(
                    "Unlock Database failed",
                    "Invalid Master key"
                ));
                return;
            }

            var info = new DB.Creation.UnlockedDBInfo(controller, key);
            args.Connection.UnlockedDBs.AddOrUpdate(Database, _ => info, (_, _) => info);

            await args.Connection.Send(new Send.InfoSend(args.Connection))
                .ConfigureAwait(false);
        }
        else
        {

            args.Connection.FidoLogin = new Session.Fido.PreLoginUser(
                args.Connection,
#if DEBUG
                Session.Fido.UserBase.GetConfiguration("localhost:8000"),
#else
                Session.Fido.UserBase.GetConfiguration(args.Connection.Host),
#endif
                Database,
                new List<DB.DBSingleController> { controller },
                true
            );
            await args.Connection.Send(new Send.LoginRequest(Database))
                .ConfigureAwait(false);
        }
    }

    private string FormatMasterKey(string masterKey)
    {
        var sb = new StringBuilder(masterKey.Length);
        foreach (var c in masterKey)
        {
            if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'))
            {
                sb.Append(c);
            }
        }
        return sb.ToString();
    }

    public override void ReadJsonContent(JsonElement json)
    {
        Database = json.GetProperty("database").GetString() ?? "";
        MasterKey = json.GetProperty("master-key").GetString();
    }
}