using IdentityModel.OidcClient;
using IdentityModel.OidcClient.Results;
using System.Net.Http.Headers;
using System.Text.Json;
using AudioWallpaper.Sso.Exceptions;

namespace AudioWallpaper.SSO {
    public class AuthManager {
        private OidcClient _oidcClient;
        private readonly AuthConfig authConfig = new();
        public static readonly AuthManager Instance = new AuthManager();
        private AuthStatus authStatus = AuthStatus.Unknown;

        //登录成功回调
        public Action<TokenSet>? OnLoginSuccess;
        //登录失败回调
        public Action<string>? OnLoginFailed;
        //刷新令牌成功回调
        public Action<TokenSet>? OnRefreshTokenSuccess;
        //刷新令牌失败回调
        public Action<string>? OnRefreshTokenFailed;
        //获取用户信息成功回调
        public Action<UserInfoSet>? OnGetUserInfoSuccess;
        //获取用户信息失败回调
        public Action<string>? OnGetUserInfoFailed;
        //撤销令牌成功回调
        public Action? OnRevokeTokenSuccess;
        //撤销令牌失败回调
        public Action<string>? OnRevokeTokenFailed;
        //注销登录成功回调
        public Action? OnLogoutSuccess;
        //注销登录失败回调
        public Action<string>? OnLogoutFailed;
        //登录状态改变回调
        public Action<AuthStatus> OnAuthStatusChanged;

        public AuthStatus AuthStatus {
            get => authStatus; set {
                authStatus = value;
                OnAuthStatusChanged?.Invoke(value);
            }
        }

        public AuthManager() {
            var options = new OidcClientOptions {
                Authority = authConfig.authorityUrl,
                ClientId = authConfig.clientId,
                RedirectUri = authConfig.redirectUri.ToString(),
                Scope = authConfig.scope,
                Policy = new Policy {
                    RequireAccessTokenHash = false,
                },

                LoadProfile = true
            };

            _oidcClient = new OidcClient(options);
            Console.WriteLine("OIDC 客户端初始化");
        }

        public async void Login(Form? from = null) {
            if (from == null) {
                OnLoginFailed?.Invoke("登录窗口未初始化");
                return;
            }
            if (_oidcClient == null) {
                OnLoginFailed?.Invoke("OIDC客户端未初始化");
                return;
            }
            if (AuthStatus == AuthStatus.Success) {
                OnLoginFailed?.Invoke("已登录，请先注销");
                return;
            }

            AuthStatus = AuthStatus.InProgress;
            _oidcClient.Options.Browser = new WebView2Browser(from);

            try {

                var result = await _oidcClient.LoginAsync(new LoginRequest());
                if (result.IsError) {
                    AuthStatus = AuthStatus.Failed;
                    OnLoginFailed?.Invoke(result.Error);
                } else {
                    TokenSet tokenSet = new(
                        result.AccessToken,
                        result.RefreshToken,
                        result.IdentityToken
                    );
                    SsoException? exception = CheckToken(tokenSet);
                    if (exception != null) {
                        throw exception;
                    }
                    AuthStatus = AuthStatus.Success;
                    OnLoginSuccess?.Invoke(tokenSet);

                }
            } catch (Exception ex) {
                AuthStatus = AuthStatus.Failed;
                OnLoginFailed?.Invoke("登录异常：" + ex.Message);
            }
        }


        /// <summary>
        /// 获取用户信息
        /// </summary>
        /// <param name="accessToken">访问令牌</param>
        /// <returns>用户信息</returns>
        public UserInfoSet? GetUserInfo(string? accessToken) {
            try {
                if (string.IsNullOrWhiteSpace(accessToken)) {
                    throw new SsoException("AccessToken 为空");
                }

                using var httpClient = new HttpClient();
                var request = new HttpRequestMessage(HttpMethod.Get, authConfig.userInfoEndpoint);
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

                var response = httpClient.Send(request);
                if (!response.IsSuccessStatusCode) {
                    OnGetUserInfoFailed?.Invoke($"获取用户信息失败，状态码: {response.StatusCode}");
                    return null;
                }

                var content = response.Content.ReadAsStringAsync().Result;
                var userInfoSet = JsonSerializer.Deserialize<UserInfoSet>(content);
                if (userInfoSet == null) {
                    throw new SsoException("获取用户信息失败，内容解析失败");
                }
                OnGetUserInfoSuccess?.Invoke(userInfoSet);
                return userInfoSet;
            } catch (Exception ex) {
                OnGetUserInfoFailed?.Invoke($"获取用户信息异常: {ex.Message}");
                return null;
            }
        }
        /// <summary>
        /// 刷新令牌
        /// </summary>
        /// <param name="refreshToken">刷新令牌</param>
        /// <returns>令牌信息</returns>
        public async Task<TokenSet?> RefreshToken(string? refreshToken) {
            try {
                if (_oidcClient == null) {
                    OnRefreshTokenFailed?.Invoke("OidcClient 未初始化");
                    return null;
                }
                if (string.IsNullOrWhiteSpace(refreshToken)) {
                    OnRefreshTokenFailed?.Invoke("刷新令牌失败：refreshToken 不能为空");
                    return null;
                }
                // 调用 OidcClient 的刷新令牌方法
                RefreshTokenResult result = await _oidcClient.RefreshTokenAsync(refreshToken);

                if (result.IsError) {
                    OnRefreshTokenFailed?.Invoke($"刷新令牌失败: {result.Error}");
                    return null;
                }
                var tokenSet = new TokenSet(
                    result.AccessToken,
                    result.RefreshToken,
                    result.IdentityToken
                );
                SsoException? exception = CheckToken(tokenSet);
                if (exception != null) {
                    throw exception;
                }
                AuthStatus = AuthStatus.Success;
                OnRefreshTokenSuccess?.Invoke(tokenSet);
                return tokenSet;
            } catch (Exception ex) {
                OnRefreshTokenFailed?.Invoke($"刷新令牌异常: {ex.Message}");
                return null;
            }
        }
        /// <summary>
        /// 撤销令牌
        /// </summary>
        /// <param name="token">token</param>
        /// <param name="tokenTypeHint">token id</param>
        public async void RevokeToken(string? token, string? tokenTypeHint, bool form = false) {
            if (string.IsNullOrWhiteSpace(token)) {
                OnRevokeTokenFailed?.Invoke("撤销令牌失败：token 不能为空");
                if (form) {
                    OnLogoutFailed?.Invoke("token 不能为空");
                }
                return;
            }
            if (string.IsNullOrWhiteSpace(tokenTypeHint)) {
                OnRevokeTokenFailed?.Invoke("撤销令牌失败：tokenTypeHint 不能为空");
                if (form) {
                    OnLogoutFailed?.Invoke("tokenTypeHint 不能为空");
                }
                return;
            }

            try {
                using var httpClient = new HttpClient();
                var parameters = new Dictionary<string, string>
                {
                    { "token", token }
                };
                if (!string.IsNullOrWhiteSpace(tokenTypeHint)) {
                    parameters.Add("token_type_hint", tokenTypeHint);
                }
                parameters.Add("client_id", authConfig.clientId);

                // 如果有 client_secret，说明是 confidential client
                var clientSecretProperty = authConfig.GetType().GetProperty("clientSecret");
                if (clientSecretProperty != null) {
                    var clientSecret = clientSecretProperty.GetValue(authConfig) as string;
                    if (!string.IsNullOrWhiteSpace(clientSecret)) {
                        parameters.Add("client_secret", clientSecret);
                    }
                }

                var content = new FormUrlEncodedContent(parameters);
                var response = await httpClient.PostAsync(authConfig.revocationEndpoint, content);
                var respContent = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode) {
                    // 撤销令牌成功，修改登录状态
                    AuthStatus = AuthStatus.LoginOut;
                    OnRevokeTokenSuccess?.Invoke();
                    if (form) {
                        OnLogoutSuccess?.Invoke();
                    }
                    return;
                } else {
                    OnRevokeTokenFailed?.Invoke($"撤销令牌失败，状态码: {response.StatusCode}，内容: {respContent}");
                    if (form) {
                        OnLogoutFailed?.Invoke($"撤销令牌失败，状态码: {response.StatusCode}，内容: {respContent}");
                    }
                    return;
                }
            } catch (Exception ex) {
                OnRevokeTokenFailed?.Invoke($"撤销令牌异常: {ex.Message}");
                if (form) {
                    OnLogoutFailed?.Invoke($"撤销令牌异常: {ex.Message}");
                }
            }
        }
        /// <summary>
        /// 注销登录
        /// </summary>
        public void Logout(TokenSet? set) {
            SsoException? exception = CheckToken(set);
            if (exception != null) {
                OnLogoutFailed?.Invoke(exception.Message);
                return;
            }

#pragma warning disable CS8602 // 解引用可能出现空引用。
            RevokeToken(set.RefreshToken, set.IdToken, true);
#pragma warning restore CS8602 // 解引用可能出现空引用。
            //TokenManager
        }
        /// <summary>
        /// 检查 TokenSet 是否有效
        /// </summary>
        /// <param name="tokenSet">TokenSet 对象</param>
        /// <returns>如果有效返回Null，如果无效返回对应的Exception</returns>
        public static SsoException? CheckToken(TokenSet? tokenSet) {
            if (tokenSet == null) {
                return new SsoException("TokenSet 为空");
            }
            if (string.IsNullOrWhiteSpace(tokenSet.AccessToken)) {
                return new SsoException("AccessToken 为空");
            }
            if (string.IsNullOrWhiteSpace(tokenSet.RefreshToken)) {
                return new SsoException("RefreshToken 为空");
            }
            if (string.IsNullOrWhiteSpace(tokenSet.IdToken)) {
                return new SsoException("IdToken 为空");
            }
            return null;
        }
    }
}
