using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace NPin.Framework.AspNetCore.Authentication.OAuth;

public abstract class OAuthAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>
    where TOptions : AuthenticationSchemeOptions, new()
{
    public abstract string AuthenticationSchemeName { get; }
    // private AuthenticationScheme _scheme;

    protected IHttpClientFactory HttpClientFactory { get; }
    protected HttpClient HttpClient { get; }

    public OAuthAuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder,
        IHttpClientFactory httpClientFactory) :
        base(options, logger, encoder)
    {
        HttpClientFactory = httpClientFactory;
        HttpClient = httpClientFactory.CreateClient();
    }

    /// <summary>
    /// Claims 转换为 票据
    /// </summary>
    /// <param name="claims"></param>
    /// <returns></returns>
    private AuthenticationTicket ConvertTicket(List<Claim> claims)
    {
        var claimsIdentity = new ClaimsIdentity(claims.ToArray(), AuthenticationSchemeName);
        var principal = new ClaimsPrincipal(claimsIdentity);
        return new AuthenticationTicket(principal, AuthenticationSchemeName);
    }

    protected async Task<THttpModel> SendHttpRequestAsync<THttpModel>(string url,
        IEnumerable<KeyValuePair<string, string?>> query,
        HttpMethod? httpMethod = null)
    {
        httpMethod ??= HttpMethod.Get;

        var queryUrl = QueryHelpers.AddQueryString(url, query);
        HttpResponseMessage response = null;
        if (httpMethod == HttpMethod.Get)
        {
            response = await HttpClient.GetAsync(queryUrl);
        }
        else if (httpMethod == HttpMethod.Post)
        {
            response = await HttpClient.PostAsync(queryUrl, null);
        }

        if (response == null)
        {
            throw new NotSupportedException($"不支持的请求方式: {httpMethod}");
        }

        var content = await response.Content.ReadAsStringAsync();
        if (!response.IsSuccessStatusCode)
        {
            throw new Exception($"授权服务器请求错误,请求地址[{queryUrl}] 错误信息[{content}]");
        }

        VerifyErrResponse(content);

        var model = JsonConvert.DeserializeObject<THttpModel>(content);
        return model!;
    }

    protected virtual void VerifyErrResponse(string content)
    {
        AuthenticationErrCodeModel.VerifyErrResponse(content);
    }
    
    /// <summary>
    /// 获取登录票据
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    protected abstract Task<List<Claim>> GetAuthTicketAsync(string code);

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (!Context.Request.Query.ContainsKey("code"))
        {
            return AuthenticateResult.Fail("回调中未包含code参数");
        }

        var code = Context.Request.Query["code"].ToString();

        List<Claim> authTicket;
        try
        {
            authTicket = await GetAuthTicketAsync(code);
        }
        catch (Exception ex)
        {
            return AuthenticateResult.Fail(ex.Message ?? "未知错误");
        }
        
        // 获取登录票据成功
        return AuthenticateResult.Success(ConvertTicket(authTicket));
    }
}