using AnotherReplayReader.PluginSystem;
using AnotherReplayReader.Utils;
using Microsoft.Win32;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading.Tasks;

namespace AnotherReplayReader
{

    internal static class Plugin
    {
        public static Task<bool> LoadPlayerIdentityPlugin(PlayerIdentity playerIdentity)
        {
            return Task.Run(async () =>
            {
                var name = $"{nameof(AnotherReplayReader)}.{nameof(PlayerIdentity)}";
                var fileName = Path.Combine(App.LibsFolder, $"{name}.dll");
                if (File.Exists(fileName))
                {
                    var assembly = await Load(fileName);
                    var pluginType = assembly.GetType($"{name}.Plugin", true);
                    var plugin = (IPlugin)Activator.CreateInstance(pluginType);
                    Auth.LoadPlugin(plugin);
                    playerIdentity.LoadPlugin(plugin);
                    return true;
                }
                return false;
            });
        }

        public static async Task<Assembly> Load(string path)
        {
            using var file = File.OpenRead(path);
            using var metaFile = File.OpenRead($"{path}.meta");
            var metaTask = JsonSerializer.DeserializeAsync<VerifierPayload>(metaFile, Network.CommonJsonOptions);
            using var memory = new MemoryStream();
            await file.CopyToAsync(memory);
            var array = memory.ToArray();
            using var sha256 = SHA256.Create();
            var hash = sha256.ComputeHash(array);
            var meta = await metaTask ?? throw new InvalidDataException("Failed to load meta file");
            if (!Enumerable.SequenceEqual(hash, meta.ByteData.Value))
            {
                throw new InvalidDataException($"Hash does not match, might be corrupt {Convert.ToBase64String(hash)}  vs  {meta.Data}");
            }
            if (!Verifier.Verify(meta))
            {
                throw new InvalidDataException("Invalid metadata, might be corrupt");
            }
            return Assembly.Load(array);
        }

        public static void Sign()
        {
            var openFileDialog = new OpenFileDialog
            {
                Filter = "dll (*.dll)|*.dll|所有文件 (*.*)|*.*",
                InitialDirectory = AppContext.BaseDirectory,
            };

            var result = openFileDialog.ShowDialog();
            if (result is true)
            {
                using var file = openFileDialog.OpenFile();
                using var memory = new MemoryStream();
                file.CopyTo(memory);
                using var sha256 = SHA256.Create();
                var hash = sha256.ComputeHash(memory.ToArray());
                Verifier.Sign(hash);
            }
        }
    }
}