using System.Collections.Concurrent;
using System.Security.Cryptography.X509Certificates;
using Csob.Configuration;

namespace Csob.Client;

/// <summary>
/// Provides <see cref="HttpClient"/> instances configured for mutual TLS with a per-request eIDAS
/// client certificate. ČSOB requires the client certificate at the transport layer, so a single
/// shared client cannot be used across tenants.
///
/// Clients are cached by certificate thumbprint and reused for connection pooling. In practice the
/// certificate identifies the TPP application (not the PSU), so the number of distinct certificates
/// is small and bounded by the set of calling tenants. Certificates and clients live in memory only
/// and never touch disk.
/// </summary>
public sealed class CsobHttpClientProvider : IDisposable
{
    private readonly CsobSettings _settings;
    private readonly ConcurrentDictionary<string, HttpClient> _clients = new();
    private readonly object _buildLock = new();

    public CsobHttpClientProvider(CsobSettings settings) => _settings = settings;

    /// <summary>
    /// Returns a (cached) mutual-TLS <see cref="HttpClient"/> presenting <paramref name="certificate"/>.
    /// A fresh certificate instance is built per request; if an equivalent one (same thumbprint) is
    /// already cached, the redundant instance is disposed so it does not leak.
    /// </summary>
    public HttpClient GetClient(X509Certificate2 certificate)
    {
        var thumbprint = certificate.Thumbprint;

        if (_clients.TryGetValue(thumbprint, out var existing))
        {
            certificate.Dispose();
            return existing;
        }

        lock (_buildLock)
        {
            if (_clients.TryGetValue(thumbprint, out existing))
            {
                certificate.Dispose();
                return existing;
            }

            var client = BuildClient(certificate);
            _clients[thumbprint] = client;
            return client;
        }
    }

    private HttpClient BuildClient(X509Certificate2 certificate)
    {
        var handler = new SocketsHttpHandler
        {
            PooledConnectionLifetime = TimeSpan.FromMinutes(10),
        };
        handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate };

        return new HttpClient(handler, disposeHandler: true)
        {
            Timeout = TimeSpan.FromSeconds(_settings.RequestTimeoutSeconds),
        };
    }

    public void Dispose()
    {
        foreach (var client in _clients.Values)
        {
            client.Dispose();
        }

        _clients.Clear();
    }
}
