using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Cyrex2.HelpersCS
{
    public static class ErenAuthV2
    {
        private static string _baseUrl;
        private static string _apiToken;
        private static HttpClient _http;

        private const string AesKey = "92502cdd674d410ef37894c11fe9edfaf90de1cba06ff1b9b28f6a53b93479b8";

        // ── Properties ──────────────────────────────────────────────────────────
        public static string SessionToken { get; private set; }
        public static bool IsLoggedIn => !string.IsNullOrEmpty(SessionToken);
        public static JObject CurrentUser { get; private set; }

        // ── Init ────────────────────────────────────────────────────────────────
        public static void Initialize(string baseUrl, string apiToken)
        {
            _baseUrl = baseUrl.TrimEnd('/');
            _apiToken = apiToken;
            _http = new HttpClient();
            _http.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Bearer", _apiToken);
            _http.Timeout = TimeSpan.FromSeconds(15);
        }

        // ── Login ───────────────────────────────────────────────────────────────
        public static async Task<JObject> Login(string username, string password)
        {
            EnsureInit();

            string hwid = GetHwid();
            var body = JsonConvert.SerializeObject(new { action = "login", username, password, hwid });
            var content = new StringContent(body, Encoding.UTF8, "application/json");

            try
            {
                var resp = await _http.PostAsync($"{_baseUrl}/fetch", content);
                string raw = await resp.Content.ReadAsStringAsync();
                var obj = JObject.Parse(raw);

                if (!resp.IsSuccessStatusCode)
                {
                    obj["_error"] = true;
                    return obj;
                }

                string token = obj["token"]?.ToString();
                if (string.IsNullOrEmpty(token))
                    return MakeError(-1, "No session token received.");

                SessionToken = token;

                var jwtHandler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
                var jwt = jwtHandler.ReadJwtToken(token);

                string serverHwidHash = jwt.Payload["hwidHash"]?.ToString() ?? "";
                string localHwidHash = Sha256(hwid);
                bool isFree = jwt.Payload["free"]?.ToString()?.ToLower() == "true";

                if (!isFree && serverHwidHash != localHwidHash)
                {
                    SessionToken = null;
                    return MakeError(10012, "HWID verification failed.");
                }

                CurrentUser = new JObject
                {
                    ["username"] = jwt.Payload["username"]?.ToString() ?? username,
                    ["category"] = jwt.Payload["category"]?.ToString() ?? "",
                    ["free"] = isFree,
                    ["hwidHash"] = serverHwidHash,
                    ["loginMessage"] = jwt.Payload["loginMessage"]?.ToString() ?? "",
                };

                return obj;
            }
            catch (HttpRequestException ex) { return MakeError(19001, $"Connection error: {ex.Message}"); }
            catch (TaskCanceledException) { return MakeError(19001, "Request timed out."); }
            catch (Exception ex) { return MakeError(-1, ex.Message); }
        }

        // ── FetchUser ───────────────────────────────────────────────────────────
        public static async Task<JObject> FetchUser(string username)
        {
            EnsureInit();

            string encryptedPayload = EncryptAES(
                JsonConvert.SerializeObject(new { username })
            );

            var body = JsonConvert.SerializeObject(new { action = "data", payload = encryptedPayload });
            var content = new StringContent(body, Encoding.UTF8, "application/json");

            try
            {
                var resp = await _http.PostAsync($"{_baseUrl}/fetch", content);
                string raw = await resp.Content.ReadAsStringAsync();
                string decrypted = DecryptAES(raw);
                var obj = JObject.Parse(decrypted);

                if (!resp.IsSuccessStatusCode)
                {
                    obj["_error"] = true;
                    return obj;
                }

                return obj;
            }
            catch (HttpRequestException ex) { return new JObject { ["error"] = "Connection error: " + ex.Message, ["_error"] = true }; }
            catch (TaskCanceledException) { return new JObject { ["error"] = "Request timed out.", ["_error"] = true }; }
            catch (Exception ex) { return new JObject { ["error"] = ex.Message, ["_error"] = true }; }
        }

        // ── AutoLogin ───────────────────────────────────────────────────────────
        public static async Task<JObject> AutoLogin()
        {
            EnsureInit();

            string hwid = GetHwid();
            string encryptedPayload = EncryptAES(
                JsonConvert.SerializeObject(new { hwid })
            );
            var body = JsonConvert.SerializeObject(new { action = "autologin", payload = encryptedPayload });
            var content = new StringContent(body, Encoding.UTF8, "application/json");

            try
            {
                var resp = await _http.PostAsync($"{_baseUrl}/fetch", content);
                string raw = await resp.Content.ReadAsStringAsync();
                string decrypted = DecryptAES(raw);
                return JObject.Parse(decrypted);
            }
            catch (Exception ex) { return new JObject { ["error"] = ex.Message, ["_error"] = true }; }
        }

        // ── Validate Session ────────────────────────────────────────────────────
        public static async Task<bool> ValidateSession()
        {
            EnsureInit();
            if (string.IsNullOrEmpty(SessionToken)) return false;

            try
            {
                var req = new HttpRequestMessage(HttpMethod.Post, $"{_baseUrl}/validateToken");
                req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", SessionToken);
                req.Headers.Add("x-hwid", GetHwid());
                var resp = await _http.SendAsync(req);
                return resp.IsSuccessStatusCode;
            }
            catch { return false; }
        }

        // ── Logout ──────────────────────────────────────────────────────────────
        public static void Logout()
        {
            SessionToken = null;
            CurrentUser = null;
        }

        // ── HWID ────────────────────────────────────────────────────────────────
        public static string GetHwid()
        {
            try
            {
                string sid = System.Security.Principal.WindowsIdentity.GetCurrent()?.User?.Value;
                if (string.IsNullOrEmpty(sid)) return "UNKNOWN_SID";

                using (var sha = SHA256.Create())
                {
                    byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(sid));
                    var sb = new StringBuilder();
                    foreach (byte b in bytes) sb.Append(b.ToString("x2"));
                    return sb.ToString();
                }
            }
            catch { return "UNKNOWN_SID"; }
        }

        // ── AES ────────────────────────────────────────────────────────────────
        public static string EncryptAES(string plaintext)
        {
            byte[] key = FromHex(AesKey);
            byte[] iv = new byte[16];
            using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(iv); }

            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;
                aes.Mode = CipherMode.CBC;
                using (var encryptor = aes.CreateEncryptor())
                {
                    byte[] input = Encoding.UTF8.GetBytes(plaintext);
                    byte[] encrypted = encryptor.TransformFinalBlock(input, 0, input.Length);
                    return ToHex(iv) + ":" + ToHex(encrypted);
                }
            }
        }

        public static string DecryptAES(string encryptedPayload)
        {
            string[] parts = encryptedPayload.Split(':');
            byte[] iv = FromHex(parts[0]);
            byte[] data = FromHex(parts[1]);
            byte[] key = FromHex(AesKey);

            using (var aes = Aes.Create())
            {
                aes.Key = key;
                aes.IV = iv;
                aes.Mode = CipherMode.CBC;
                using (var decryptor = aes.CreateDecryptor())
                {
                    byte[] decrypted = decryptor.TransformFinalBlock(data, 0, data.Length);
                    return Encoding.UTF8.GetString(decrypted);
                }
            }
        }

        // ── Helpers ─────────────────────────────────────────────────────────────
        private static byte[] FromHex(string hex)
        {
            byte[] bytes = new byte[hex.Length / 2];
            for (int i = 0; i < bytes.Length; i++)
                bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
            return bytes;
        }

        private static string ToHex(byte[] bytes)
        {
            var sb = new StringBuilder();
            foreach (byte b in bytes) sb.Append(b.ToString("x2"));
            return sb.ToString();
        }

        private static string Sha256(string input)
        {
            using (var sha = SHA256.Create())
            {
                byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input));
                return BitConverter.ToString(bytes).Replace("-", "").ToLower();
            }
        }

        private static void EnsureInit()
        {
            if (_http == null)
                throw new InvalidOperationException("ErenAuthV2.Initialize() must be called first.");
        }

        private static JObject MakeError(int code, string message)
        {
            return new JObject
            {
                ["error"] = message,
                ["code"] = code,
                ["_error"] = true
            };
        }
    }
}
