using Microsoft.Win32;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace AnotherReplayReader
{
    internal static class Auth
    {
        public static string? Id { get; }

        static Auth()
        {
            Id = null;

            string? windowsID;
            try
            {
                using var view64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
                using var winNt = view64?.OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion", false);
                windowsID = winNt?.GetValue("ProductId") as string;
            }
            catch
            {
                return;
            }

            string? randomKey;
            try
            {
                var folderPath = Cache.CacheDirectory;
                if (!Directory.Exists(folderPath))
                {
                    Directory.CreateDirectory(folderPath);
                }

                var keyPath = Path.Combine(folderPath, "id");
                if (!File.Exists(keyPath))
                {
                    File.WriteAllText(keyPath, Guid.NewGuid().ToString());
                }

                randomKey = File.ReadAllText(keyPath);
            }
            catch
            {
                return;
            }

            if (string.IsNullOrWhiteSpace(windowsID) || string.IsNullOrWhiteSpace(randomKey))
            {
                return;
            }

            using var sha = SHA256.Create();
            var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(windowsID + randomKey));
            Id = string.Concat(hash.Skip(3).Take(10).Select(x => $"{x:X2}"));
        }

        public static string GetKey()
        {
            if (Id == null)
            {
                return string.Empty;
            }

            var text = $"{Id}{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}";
            var bytes = Encoding.UTF8.GetBytes(text);
            var pre = Encoding.UTF8.GetBytes("playertable!");
            var salt = new byte[9];
            using (var rng = new RNGCryptoServiceProvider())
            {
                rng.GetNonZeroBytes(salt);
            }

            for (var i = 0; i < bytes.Length; ++i)
            {
                bytes[i] = (byte)(bytes[i] ^ salt[i % salt.Length]);
            }

            return Convert.ToBase64String(salt.Concat(bytes).Select((x, i) => (byte)(x ^ pre[i % pre.Length])).ToArray());
        }

        public static byte[]? IdAsKey()
        {
            if (string.IsNullOrEmpty(Id))
            {
                return null;
            }
            var bytes = Encoding.UTF8.GetBytes(Id);
            var destination = Enumerable.Repeat<byte>(0xEA, 24).ToArray();
            Array.Copy(bytes, destination, Math.Min(bytes.Length, destination.Length));
            return destination;
        }
    }
}