using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace KeycloakDemo
{
    /// <summary>
    /// Token 安全存储类
    /// 使用 Windows DPAPI 加密存储，与当前用户账户绑定
    /// </summary>
    public static class TokenStorage
    {
        // 附加熵，增加安全性
        private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("KeycloakDemo_Salt_2024");

        /// <summary>
        /// 获取 Token 存储目录
        /// </summary>
        private static string StorageDirectory
        {
            get
            {
                var path = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    "KeycloakDemo",
                    "tokens"
                );
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                return path;
            }
        }

        /// <summary>
        /// Access Token 存储路径
        /// </summary>
        private static string AccessTokenPath => Path.Combine(StorageDirectory, "access_token.dat");

        /// <summary>
        /// Refresh Token 存储路径
        /// </summary>
        private static string RefreshTokenPath => Path.Combine(StorageDirectory, "refresh_token.dat");

        /// <summary>
        /// ID Token 存储路径
        /// </summary>
        private static string IdTokenPath => Path.Combine(StorageDirectory, "id_token.dat");

        /// <summary>
        /// 过期时间存储路径
        /// </summary>
        private static string ExpiresAtPath => Path.Combine(StorageDirectory, "expires_at.dat");

        /// <summary>
        /// 保存 Token
        /// </summary>
        public static void SaveTokens(string accessToken, string refreshToken, string idToken, DateTime expiresAt)
        {
            SaveEncrypted(AccessTokenPath, accessToken);
            SaveEncrypted(RefreshTokenPath, refreshToken);
            SaveEncrypted(IdTokenPath, idToken);
            SaveEncrypted(ExpiresAtPath, expiresAt.ToString("O")); // ISO 8601 格式
        }

        /// <summary>
        /// 加载 Access Token
        /// </summary>
        public static string LoadAccessToken()
        {
            return LoadEncrypted(AccessTokenPath);
        }

        /// <summary>
        /// 加载 Refresh Token
        /// </summary>
        public static string LoadRefreshToken()
        {
            return LoadEncrypted(RefreshTokenPath);
        }

        /// <summary>
        /// 加载 ID Token
        /// </summary>
        public static string LoadIdToken()
        {
            return LoadEncrypted(IdTokenPath);
        }

        /// <summary>
        /// 加载过期时间
        /// </summary>
        public static DateTime? LoadExpiresAt()
        {
            var expiresAtStr = LoadEncrypted(ExpiresAtPath);
            if (string.IsNullOrEmpty(expiresAtStr))
                return null;
            
            if (DateTime.TryParse(expiresAtStr, out var expiresAt))
                return expiresAt;
            
            return null;
        }

        /// <summary>
        /// 检查 Token 是否已过期
        /// </summary>
        public static bool IsTokenExpired()
        {
            var expiresAt = LoadExpiresAt();
            if (!expiresAt.HasValue)
                return true;  // 没有过期时间则视为已过期

            // 提前 TokenRefreshBuffer 秒刷新
            return DateTime.UtcNow >= expiresAt.Value.AddSeconds(-KeycloakConfig.TokenRefreshBuffer);
        }

        /// <summary>
        /// 清除所有存储的 Token
        /// </summary>
        public static void ClearTokens()
        {
            TryDeleteFile(AccessTokenPath);
            TryDeleteFile(RefreshTokenPath);
            TryDeleteFile(IdTokenPath);
            TryDeleteFile(ExpiresAtPath);
        }

        /// <summary>
        /// 检查是否有缓存的 Token
        /// </summary>
        public static bool HasCachedTokens()
        {
            return File.Exists(AccessTokenPath) && File.Exists(RefreshTokenPath);
        }

        /// <summary>
        /// 加密并保存数据
        /// </summary>
        private static void SaveEncrypted(string path, string data)
        {
            if (string.IsNullOrEmpty(data))
            {
                TryDeleteFile(path);
                return;
            }

            try
            {
                var plainBytes = Encoding.UTF8.GetBytes(data);
                var encryptedBytes = ProtectedData.Protect(plainBytes, Entropy, DataProtectionScope.CurrentUser);
                File.WriteAllBytes(path, encryptedBytes);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"保存 Token 失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 加载并解密数据
        /// </summary>
        private static string LoadEncrypted(string path)
        {
            if (!File.Exists(path))
                return null;

            try
            {
                var encryptedBytes = File.ReadAllBytes(path);
                var plainBytes = ProtectedData.Unprotect(encryptedBytes, Entropy, DataProtectionScope.CurrentUser);
                return Encoding.UTF8.GetString(plainBytes);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"加载 Token 失败: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// 尝试删除文件
        /// </summary>
        private static void TryDeleteFile(string path)
        {
            try
            {
                if (File.Exists(path))
                    File.Delete(path);
            }
            catch { /* 忽略删除失败 */ }
        }
    }
}
