using System.Net;
using System.Security.Authentication;
using FitConnect.Services.Interfaces;
using FitConnect.Services.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace FitConnect.Services;

internal class OAuthService : RestCallService, IOAuthService {
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly ILogger? _logger;
    private readonly string _tokenUrl;
    private OAuthAccessToken? _token;

    public OAuthService(string tokenUrl, string version, string clientId, string clientSecret,
        ILogger? logger) : base($"{tokenUrl}/{version}", logger) {
        _tokenUrl = tokenUrl;
        _clientId = clientId;
        _clientSecret = clientSecret;
        _logger = logger;
    }

    public OAuthAccessToken? Token {
        get => _token;
        private set {
            _token = value;
            AccessToken = value?.AccessToken;
        }
    }

    /// <summary>
    ///     Requesting an OAuth token from the FitConnect API.
    ///     <para>
    ///         You can get the Client ID and Client Secret from the FitConnect Self Service portal
    ///         under <br />
    ///         https://portal.auth-testing.fit-connect.fitko.dev
    ///     </para>
    /// </summary>
    /// <param name="scope">Scope if needed</param>
    /// <returns>The received token or null</returns>
    public async Task AuthenticateAsync(
        string? scope = null) {
        var client = CreateClient();

        var requestContent = new Dictionary<string, string> {
            { "grant_type", "client_credentials" },
            { "client_id", _clientId },
            { "client_secret", _clientSecret }
        };

        if (scope != null)
            requestContent["scope"] = scope;

        var content = new FormUrlEncodedContent(requestContent);

        var request = new HttpRequestMessage(HttpMethod.Post, _tokenUrl) {
            Content = content,
            Method = HttpMethod.Post
        };

        var response = await client.SendAsync(request);
        if (response.IsSuccessStatusCode) {
            var result =
                JsonConvert.DeserializeObject<OAuthAccessToken>(
                    await response.Content.ReadAsStringAsync());
            if (result == null)
                throw new AuthenticationException("Failed to authenticate");
            Token = result;
        }

        if (response.StatusCode == HttpStatusCode.Unauthorized)
            throw new InvalidCredentialException(await response.Content.ReadAsStringAsync());
    }

    public bool IsAuthenticated => Token != null;

    public void EnsureAuthenticated() {
        if (!IsAuthenticated) AuthenticateAsync().Wait();
    }
}