using System.Text;
using System.Text.Encodings.Web;
using FitConnect.Encryption;
using FitConnect.Models;
using FitConnect.Services;
using FitConnect.Services.Interfaces;
using IdentityModel;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Route = FitConnect.Services.Models.v1.Routes.Route;

namespace FitConnect;

public class Router : IRouter {
    private readonly FitConnectEnvironment _environment;
    private readonly ILogger? _logger;
    private readonly IRouteService _routeService;
    private readonly ISelfServicePortalService _selfServicePortalService;


    public Router(FitConnectEnvironment environment, ILogger? logger = null) {
        _environment = environment;
        _logger = logger;
        _routeService = new RouteService(environment.RoutingUrl, "v1", logger);
        _selfServicePortalService = new SelfServicePortalService(environment.SspUrl, "v1", logger);
    }

    public async Task<List<Route>> FindDestinationsAsync(string leiaKey, string? ags = null,
        string? ars = null,
        string? areaId = null, bool skipValidation = false) {
        var routes = await _routeService.GetDestinationIdAsync(leiaKey, ags, ars, areaId);
        if (skipValidation) return routes;

        var validation = await _selfServicePortalService.GetValidationJwk();
        foreach (var route in routes) {
            var token = new JsonWebToken(route.DestinationSignature);
            var key = validation.Keys.First(k => k.Kid == token.Kid);
            var verifyJwt =
                FitEncryption.VerifyJwt(route.DestinationSignature, key, out var _, _logger);

            var submissionKey =
                await GetSubmissionServiceValidationJwk(route.DestinationParameters.SubmissionUrl);

            // Get Key from SubmissionAPI
            var parametersJson =
                Base64Url.Encode(
                    Encoding.UTF8.GetBytes(
                        JsonConvert.SerializeObject(route.DestinationParameters)));

            var signature =
                route.DestinationParametersSignature.Replace("..", $".{parametersJson}.");

            // KID to check is in signature header kid
            var header = JsonConvert.DeserializeObject<dynamic>(
                Base64UrlEncoder.Decode(route.DestinationParametersSignature.Split('.')[0])
            );
            var kid = (string)header.kid;

            Console.WriteLine("Looking for key with kid: " + kid);
            verifyJwt &= FitEncryption.VerifyJwt(signature,
                submissionKey.Keys.First(k => k.Kid == kid), out var _, _logger);
            if (!verifyJwt) {
                throw new Exception("Invalid signature");
            }
        }

        return routes;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    private async Task<JsonWebKeySet> GetSubmissionServiceValidationJwk(string baseUrl) {
        var client = new HttpClient() {
            BaseAddress = new Uri(baseUrl),
        };

        var result = await client.GetAsync("/.well-known/jwks.json");
        return new JsonWebKeySet(await result.Content.ReadAsStringAsync());
    }


    /// <summary>
    ///     Finding Areas
    /// </summary>
    /// <param name="filter"></param>
    /// <param name="totalCount"></param>
    /// <param name="offset"></param>
    /// <param name="limit"></param>
    /// <returns></returns>
    public IEnumerable<Area> GetAreas(string filter, out int totalCount, int offset = 0,
        int limit = 100) {
        var dto = _routeService.GetAreas(filter, offset, limit).Result;
        totalCount = dto?.TotalCount ?? 0;
        return dto?.Areas ?? new List<Area>();
    }
}