using Hotline.Enterprise;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using Hotline.Configurations;
using Hotline.Share.Dtos.Enterprise;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using XF.Domain.Cache;
using XF.Domain.Exceptions;

namespace Hotline.YbEnterprise.Sdk
{
    public class EnterpriseService : IEnterpriseService
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
        private readonly ITypedCache<YbEnterpriseToken> _tokenCache;
        private readonly ILogger<EnterpriseService> _logger;
        public EnterpriseService(
            IHttpClientFactory httpClientFactory,
            IOptionsSnapshot<AppConfiguration> appOptions,
            ITypedCache<YbEnterpriseToken> tokenCache,
            ILogger<EnterpriseService> logger)
        {
            _httpClientFactory = httpClientFactory;
            _appOptions = appOptions;
            _tokenCache = tokenCache;
            _logger = logger;
        }

        /// <summary>
        /// 获取企业列表
        /// </summary>
        /// <param name="name"></param>
        /// <param name="current"></param>
        /// <param name="size"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<YbEnterprisePaged> QueryYbEnterprisesAsync(
            string name, int current, int size, CancellationToken cancellationToken)
        {
            var request = new YbEnterprisesRequest()
            {
                EnterpriseName = name,
                Current = current,
                Size = size
            };
            
            var token = await GetTokenAsync(YbEnterpriseDefaults.KeyOfToken, cancellationToken);
            var result = await ExecuteAsync<YbEnterprisesRequest, YbEnterpriseResponse<YbEnterprisePaged>>(
                YbEnterpriseDefaults.PathEnterprises,
                request,
                d => d.DefaultRequestHeaders.Add("Blade-Auth", $"{token.TokenType} {token.AccessToken}"),
                cancellationToken);
            if (result is null || !result.Success)
                throw new UserFriendlyException("未获取到企业专班数据");
            return result.Data;
        }

        private async Task<YbEnterpriseToken> GetTokenAsync(string key, CancellationToken cancellationToken)
        {
            var token = await _tokenCache.GetAsync(key, cancellationToken);
            if (token is not null) return token;
            token = await GetTokenAsync(cancellationToken);
            await _tokenCache.SetAsync(key, token, TimeSpan.FromSeconds(token.ExpiresIn - 5), cancellationToken);
            return token;
        }

        private async Task<YbEnterpriseToken> GetTokenAsync(CancellationToken cancellationToken)
        {
            var config = _appOptions.Value.YiBin.Enterprise;
            string authorization = config.ClientId + ":" + config.ClientSecret;
            authorization = Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization));
            var result = await ExecuteAsync<object, YbEnterpriseResponse<YbEnterpriseToken>>(
                YbEnterpriseDefaults.PathToken,
                new(),
                d =>
                {
                    d.DefaultRequestHeaders.Authorization = AuthenticationHeaderValue.Parse($"Basic {authorization}");
                    d.DefaultRequestHeaders.Add("Tenant-Id", config.TenantId);
                }, cancellationToken);

            if (result is null || !result.Success)
                throw new UserFriendlyException("获取enterprise token失败");
            return result.Data;
        }

        private async Task<TResponse?> ExecuteAsync<TRequest, TResponse>(string path, TRequest request,
            Action<HttpClient>? clientInitialize = null, CancellationToken cancellationToken = default)
        {
            var client = _httpClientFactory.CreateClient(YbEnterpriseDefaults.HttpName);
            clientInitialize?.Invoke(client);

            using var responseMessage = await client.PostAsJsonAsync(path, request, cancellationToken);
            responseMessage.EnsureSuccessStatusCode();
            var result = await responseMessage.Content.ReadFromJsonAsync<TResponse>(cancellationToken: cancellationToken);
            return result;
        }
    }
}