using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Web.Script.Serialization;

namespace KeycloakDemo
{
    /// <summary>
    /// Keycloak 认证服务
    /// 实现 OAuth 2.0 Authorization Code Flow with PKCE
    /// </summary>
    public class KeycloakAuthService : IDisposable
    {
        private PkceHelper _pkce;
        private string _state;
        private HttpListener _httpListener;
        private readonly HttpClient _httpClient;

        public KeycloakAuthService()
        {
            _httpClient = new HttpClient();
        }

        #region 公开方法

        /// <summary>
        /// 执行登录流程
        /// </summary>
        /// <returns>登录结果</returns>
        public async Task<LoginResult> LoginAsync()
        {
            try
            {
                // 如果有缓存的 Token 且未过期，直接使用
                if (KeycloakConfig.EnableTokenCache && TokenStorage.HasCachedTokens())
                {
                    if (!TokenStorage.IsTokenExpired())
                    {
                        // Token 仍然有效
                        var cachedAccessToken = TokenStorage.LoadAccessToken();
                        var cachedIdToken = TokenStorage.LoadIdToken();
                        var userInfo = ParseIdToken(cachedIdToken);
                        
                        return new LoginResult
                        {
                            IsSuccess = true,
                            AccessToken = cachedAccessToken,
                            IdToken = cachedIdToken,
                            RefreshToken = TokenStorage.LoadRefreshToken(),
                            UserInfo = userInfo,
                            Message = "使用缓存的 Token（SSO 自动登录）"
                        };
                    }
                    else
                    {
                        // Token 已过期，尝试刷新
                        var refreshResult = await RefreshTokenAsync();
                        if (refreshResult.IsSuccess)
                        {
                            return refreshResult;
                        }
                        // 刷新失败，继续走完整登录流程
                    }
                }

                // 步骤1: 生成 PKCE 密钥对和 state
                _pkce = PkceHelper.Generate();
                _state = Guid.NewGuid().ToString("N");

                // 步骤2: 构建授权 URL
                var authUrl = BuildAuthorizationUrl();

                // 步骤3: 启动本地 HTTP 监听器
                StartHttpListener();

                // 步骤4: 打开系统浏览器
                Process.Start(new ProcessStartInfo(authUrl) { UseShellExecute = true });

                // 步骤5: 等待回调
                var callbackResult = await WaitForCallbackAsync();
                if (!callbackResult.IsSuccess)
                {
                    return new LoginResult { IsSuccess = false, Message = callbackResult.Error };
                }

                // 步骤6: 验证 state
                if (callbackResult.State != _state)
                {
                    return new LoginResult { IsSuccess = false, Message = "安全验证失败：State 不匹配，可能遭受 CSRF 攻击" };
                }

                // 步骤7: 用 code + code_verifier 换取 Token
                var tokenResponse = await ExchangeCodeForTokenAsync(callbackResult.Code);
                if (!tokenResponse.IsSuccess)
                {
                    return new LoginResult { IsSuccess = false, Message = $"获取 Token 失败: {tokenResponse.ErrorDescription}" };
                }

                // 步骤8: 解析用户信息
                var user = ParseIdToken(tokenResponse.IdToken);

                // 步骤9: 保存 Token
                if (KeycloakConfig.EnableTokenCache)
                {
                    TokenStorage.SaveTokens(
                        tokenResponse.AccessToken,
                        tokenResponse.RefreshToken,
                        tokenResponse.IdToken,
                        tokenResponse.GetExpiresAt()
                    );
                }

                return new LoginResult
                {
                    IsSuccess = true,
                    AccessToken = tokenResponse.AccessToken,
                    RefreshToken = tokenResponse.RefreshToken,
                    IdToken = tokenResponse.IdToken,
                    ExpiresAt = tokenResponse.GetExpiresAt(),
                    UserInfo = user,
                    Message = "登录成功"
                };
            }
            catch (Exception ex)
            {
                return new LoginResult { IsSuccess = false, Message = $"登录异常: {ex.Message}" };
            }
            finally
            {
                StopHttpListener();
            }
        }

        /// <summary>
        /// 刷新 Token
        /// </summary>
        public async Task<LoginResult> RefreshTokenAsync()
        {
            var refreshToken = TokenStorage.LoadRefreshToken();
            if (string.IsNullOrEmpty(refreshToken))
            {
                return new LoginResult { IsSuccess = false, Message = "没有可用的 Refresh Token" };
            }

            try
            {
                var content = new FormUrlEncodedContent(new Dictionary<string, string>
                {
                    ["grant_type"] = "refresh_token",
                    ["client_id"] = KeycloakConfig.ClientId,
                    ["refresh_token"] = refreshToken
                });

                var response = await _httpClient.PostAsync(KeycloakConfig.TokenEndpoint, content);
                var json = await response.Content.ReadAsStringAsync();
                var tokenResponse = DeserializeJson<TokenResponse>(json);

                if (!tokenResponse.IsSuccess)
                {
                    TokenStorage.ClearTokens();
                    return new LoginResult { IsSuccess = false, Message = $"刷新 Token 失败: {tokenResponse.ErrorDescription}" };
                }

                // 保存新 Token
                TokenStorage.SaveTokens(
                    tokenResponse.AccessToken,
                    tokenResponse.RefreshToken,
                    tokenResponse.IdToken,
                    tokenResponse.GetExpiresAt()
                );

                var user = ParseIdToken(tokenResponse.IdToken);

                return new LoginResult
                {
                    IsSuccess = true,
                    AccessToken = tokenResponse.AccessToken,
                    RefreshToken = tokenResponse.RefreshToken,
                    IdToken = tokenResponse.IdToken,
                    ExpiresAt = tokenResponse.GetExpiresAt(),
                    UserInfo = user,
                    Message = "Token 刷新成功"
                };
            }
            catch (Exception ex)
            {
                return new LoginResult { IsSuccess = false, Message = $"刷新 Token 异常: {ex.Message}" };
            }
        }

        /// <summary>
        /// 登出
        /// </summary>
        public async Task<bool> LogoutAsync()
        {
            try
            {
                var idToken = TokenStorage.LoadIdToken();
                
                // 清除本地 Token
                TokenStorage.ClearTokens();

                // 调用 Keycloak 登出端点
                if (!string.IsNullOrEmpty(idToken))
                {
                    var logoutUrl = $"{KeycloakConfig.LogoutEndpoint}?" +
                        $"id_token_hint={Uri.EscapeDataString(idToken)}&" +
                        $"post_logout_redirect_uri={Uri.EscapeDataString("http://localhost")}";
                    
                    // 在浏览器中打开登出页面
                    Process.Start(new ProcessStartInfo(logoutUrl) { UseShellExecute = true });
                }

                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 获取当前有效的 Access Token
        /// 如果已过期会自动刷新
        /// </summary>
        public async Task<string> GetValidAccessTokenAsync()
        {
            if (TokenStorage.IsTokenExpired())
            {
                var refreshResult = await RefreshTokenAsync();
                if (!refreshResult.IsSuccess)
                {
                    throw new UnauthorizedAccessException("Token 已过期且无法刷新，请重新登录");
                }
            }
            return TokenStorage.LoadAccessToken();
        }

        #endregion

        #region 私有方法

        /// <summary>
        /// 构建授权 URL
        /// </summary>
        private string BuildAuthorizationUrl()
        {
            var queryParams = new Dictionary<string, string>
            {
                ["client_id"] = KeycloakConfig.ClientId,
                ["response_type"] = "code",
                ["redirect_uri"] = KeycloakConfig.RedirectUri,
                ["scope"] = KeycloakConfig.Scope,
                ["state"] = _state,
                ["code_challenge"] = _pkce.CodeChallenge,
                ["code_challenge_method"] = _pkce.CodeChallengeMethod
            };

            var queryString = string.Join("&", 
                queryParams.Select(kv => $"{kv.Key}={Uri.EscapeDataString(kv.Value)}"));
            
            return $"{KeycloakConfig.AuthorizationEndpoint}?{queryString}";
        }

        /// <summary>
        /// 启动本地 HTTP 监听器
        /// </summary>
        private void StartHttpListener()
        {
            _httpListener = new HttpListener();
            _httpListener.Prefixes.Add($"http://localhost:{KeycloakConfig.CallbackPort}/");
            _httpListener.Start();
        }

        /// <summary>
        /// 停止 HTTP 监听器
        /// </summary>
        private void StopHttpListener()
        {
            try
            {
                _httpListener?.Stop();
                _httpListener?.Close();
            }
            catch { }
        }

        /// <summary>
        /// 等待回调
        /// </summary>
        private async Task<CallbackResult> WaitForCallbackAsync()
        {
            try
            {
                var context = await _httpListener.GetContextAsync();
                var request = context.Request;
                var response = context.Response;

                // 解析回调参数
                var code = request.QueryString["code"];
                var state = request.QueryString["state"];
                var error = request.QueryString["error"];
                var errorDescription = request.QueryString["error_description"];

                // 返回响应给浏览器
                string responseHtml;
                if (!string.IsNullOrEmpty(error))
                {
                    responseHtml = $@"
<!DOCTYPE html>
<html>
<head><meta charset='utf-8'><title>登录失败</title></head>
<body style='font-family: Arial; text-align: center; padding-top: 50px;'>
    <h1 style='color: #e74c3c;'>❌ 登录失败</h1>
    <p>{errorDescription ?? error}</p>
    <p>请关闭此窗口并重试</p>
</body>
</html>";
                }
                else
                {
                    responseHtml = @"
<!DOCTYPE html>
<html>
<head><meta charset='utf-8'><title>登录成功</title></head>
<body style='font-family: Arial; text-align: center; padding-top: 50px;'>
    <h1 style='color: #27ae60;'>✅ 登录成功</h1>
    <p>您已成功登录，请关闭此窗口返回应用程序</p>
    <script>setTimeout(function(){ window.close(); }, 2000);</script>
</body>
</html>";
                }

                var buffer = Encoding.UTF8.GetBytes(responseHtml);
                response.ContentType = "text/html; charset=utf-8";
                response.ContentLength64 = buffer.Length;
                await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
                response.Close();

                if (!string.IsNullOrEmpty(error))
                {
                    return new CallbackResult { IsSuccess = false, Error = errorDescription ?? error };
                }

                return new CallbackResult { IsSuccess = true, Code = code, State = state };
            }
            catch (Exception ex)
            {
                return new CallbackResult { IsSuccess = false, Error = ex.Message };
            }
        }

        /// <summary>
        /// 用授权码换取 Token
        /// </summary>
        private async Task<TokenResponse> ExchangeCodeForTokenAsync(string code)
        {
            var content = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                ["grant_type"] = "authorization_code",
                ["client_id"] = KeycloakConfig.ClientId,
                ["code"] = code,
                ["redirect_uri"] = KeycloakConfig.RedirectUri,
                ["code_verifier"] = _pkce.CodeVerifier  // PKCE 验证的关键！
            });

            var response = await _httpClient.PostAsync(KeycloakConfig.TokenEndpoint, content);
            var json = await response.Content.ReadAsStringAsync();
            
            return DeserializeJson<TokenResponse>(json);
        }

        /// <summary>
        /// 解析 ID Token 获取用户信息
        /// JWT 格式: header.payload.signature，我们只需要解析 payload 部分
        /// </summary>
        private UserInfo ParseIdToken(string idToken)
        {
            if (string.IsNullOrEmpty(idToken))
                return null;

            try
            {
                // JWT 由三部分组成，用 . 分隔
                var parts = idToken.Split('.');
                if (parts.Length != 3)
                    return null;

                // payload 是第二部分，Base64Url 编码
                var payload = parts[1];
                
                // Base64Url 转 Base64
                payload = payload.Replace('-', '+').Replace('_', '/');
                switch (payload.Length % 4)
                {
                    case 2: payload += "=="; break;
                    case 3: payload += "="; break;
                }

                var jsonBytes = Convert.FromBase64String(payload);
                var json = Encoding.UTF8.GetString(jsonBytes);

                // 使用 JavaScriptSerializer 解析 JSON
                var serializer = new JavaScriptSerializer();
                var claims = serializer.Deserialize<Dictionary<string, object>>(json);

                return new UserInfo
                {
                    Subject = GetClaimValue(claims, "sub"),
                    Username = GetClaimValue(claims, "preferred_username"),
                    Name = GetClaimValue(claims, "name"),
                    Email = GetClaimValue(claims, "email"),
                    EmailVerified = GetClaimValue(claims, "email_verified") == "true" || 
                                    GetClaimValue(claims, "email_verified") == "True"
                };
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 从 claims 字典中获取值
        /// </summary>
        private string GetClaimValue(Dictionary<string, object> claims, string key)
        {
            if (claims.TryGetValue(key, out var value))
                return value?.ToString();
            return null;
        }

        /// <summary>
        /// JSON 反序列化
        /// </summary>
        private T DeserializeJson<T>(string json) where T : class
        {
            using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
            {
                var serializer = new DataContractJsonSerializer(typeof(T));
                return serializer.ReadObject(ms) as T;
            }
        }

        #endregion

        public void Dispose()
        {
            _httpClient?.Dispose();
            StopHttpListener();
        }

        #region 内部类

        private class CallbackResult
        {
            public bool IsSuccess { get; set; }
            public string Code { get; set; }
            public string State { get; set; }
            public string Error { get; set; }
        }

        #endregion
    }

    /// <summary>
    /// 登录结果
    /// </summary>
    public class LoginResult
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }
        public string IdToken { get; set; }
        public DateTime ExpiresAt { get; set; }
        public UserInfo UserInfo { get; set; }
    }
}
