using IdokladSdk.Enums;
using Idoklad.Configuration;

namespace Idoklad.Credentials;

/// <summary>
/// Resolves the iDoklad credentials for the current request.
///
/// Mirrors <c>get_request_settings</c> from the sibling microsoft-365-service: each credential
/// value is taken from its request header when present and otherwise falls back to the
/// environment-configured default. Secrets are only ever read from headers (never query/body).
/// If, after applying the fallbacks, any required value is still missing, the request is rejected.
/// </summary>
public sealed class RequestCredentialsProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IdokladSettings _settings;

    public RequestCredentialsProvider(IHttpContextAccessor httpContextAccessor, IdokladSettings settings)
    {
        _httpContextAccessor = httpContextAccessor;
        _settings = settings;
    }

    public IdokladCredentials Resolve()
    {
        var headers = _httpContextAccessor.HttpContext?.Request.Headers;

        var clientId = HeaderOrDefault(headers, CredentialConstants.ClientIdHeader, _settings.ClientId);
        var clientSecret = HeaderOrDefault(headers, CredentialConstants.ClientSecretHeader, _settings.ClientSecret);
        var applicationId = HeaderOrDefault(headers, CredentialConstants.ApplicationIdHeader, _settings.ApplicationId);

        var missing = new List<string>();
        if (string.IsNullOrWhiteSpace(clientId)) missing.Add(CredentialConstants.ClientIdHeader);
        if (string.IsNullOrWhiteSpace(clientSecret)) missing.Add(CredentialConstants.ClientSecretHeader);
        if (string.IsNullOrWhiteSpace(applicationId)) missing.Add(CredentialConstants.ApplicationIdHeader);
        if (missing.Count > 0)
        {
            throw new MissingCredentialsException(missing);
        }

        return new IdokladCredentials
        {
            ClientId = clientId!,
            ClientSecret = clientSecret!,
            ApplicationId = applicationId!,
            Language = ResolveLanguage(headers),
        };
    }

    private Language ResolveLanguage(IHeaderDictionary? headers)
    {
        var raw = headers is not null && headers.TryGetValue(CredentialConstants.LanguageHeader, out var value)
            ? value.ToString()
            : null;

        return Enum.TryParse<Language>(raw, ignoreCase: true, out var parsed) ? parsed : _settings.Language;
    }

    private static string? HeaderOrDefault(IHeaderDictionary? headers, string name, string fallback)
    {
        if (headers is not null && headers.TryGetValue(name, out var value))
        {
            var headerValue = value.ToString();
            if (!string.IsNullOrWhiteSpace(headerValue))
            {
                return headerValue;
            }
        }

        return string.IsNullOrWhiteSpace(fallback) ? null : fallback;
    }
}
