using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using MemberCenter.Application.Abstractions;
using MemberCenter.Application.Models.Newsletter;
using MemberCenter.Infrastructure.Configuration;
using MemberCenter.Infrastructure.Persistence;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.EntityFrameworkCore;

namespace MemberCenter.Infrastructure.Services;

public sealed class SendEngineWebhookPublisher : ISendEngineWebhookPublisher
{
    private const string SendEngineWebhookClientKeyPrefix = "send_engine:webhook_client:";

    private readonly HttpClient _httpClient;
    private readonly IOptions<SendEngineWebhookOptions> _options;
    private readonly MemberCenterDbContext _dbContext;
    private readonly ILogger<SendEngineWebhookPublisher> _logger;

    public SendEngineWebhookPublisher(
        HttpClient httpClient,
        IOptions<SendEngineWebhookOptions> options,
        MemberCenterDbContext dbContext,
        ILogger<SendEngineWebhookPublisher> logger)
    {
        _httpClient = httpClient;
        _options = options;
        _dbContext = dbContext;
        _logger = logger;
    }

    public async Task PublishSubscriptionEventAsync(
        string eventType,
        Guid tenantId,
        SubscriptionDto subscription,
        CancellationToken cancellationToken = default)
    {
        var options = _options.Value;
        if (string.IsNullOrWhiteSpace(options.BaseUrl)
            || string.IsNullOrWhiteSpace(options.WebhookSecret))
        {
            return;
        }

        var clientId = await ResolveWebhookClientIdAsync(tenantId, cancellationToken);
        if (string.IsNullOrWhiteSpace(clientId))
        {
            _logger.LogWarning("Skip webhook publish: missing webhook client id for tenant {TenantId}", tenantId);
            return;
        }

        var payload = new
        {
            event_id = Guid.NewGuid(),
            event_type = eventType,
            tenant_id = tenantId,
            list_id = subscription.ListId,
            subscriber = new
            {
                id = subscription.Id,
                email = subscription.Email,
                status = subscription.Status,
                preferences = subscription.Preferences
            },
            occurred_at = DateTimeOffset.UtcNow
        };

        var json = JsonSerializer.Serialize(payload);
        var body = new StringContent(json, Encoding.UTF8, "application/json");

        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
        var nonce = Guid.NewGuid().ToString();
        var signature = ComputeHmacHex(options.WebhookSecret, json);

        var endpoint = BuildEndpoint(options.BaseUrl, options.SubscriptionEventsPath);
        using var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
        {
            Content = body
        };
        request.Headers.Add("X-Signature", signature);
        request.Headers.Add("X-Timestamp", timestamp);
        request.Headers.Add("X-Nonce", nonce);
        request.Headers.Add("X-Client-Id", clientId);

        try
        {
            using var response = await _httpClient.SendAsync(request, cancellationToken);
            if (!response.IsSuccessStatusCode)
            {
                var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
                _logger.LogWarning(
                    "Send Engine webhook failed. Status={StatusCode}, TenantId={TenantId}, EventType={EventType}, Body={Body}",
                    (int)response.StatusCode,
                    tenantId,
                    eventType,
                    responseBody);
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Send Engine webhook publish threw an exception for tenant {TenantId}, event {EventType}", tenantId, eventType);
        }
    }

    private static string BuildEndpoint(string baseUrl, string path)
    {
        var normalizedBase = baseUrl.TrimEnd('/');
        var normalizedPath = path.StartsWith('/') ? path : $"/{path}";
        return normalizedBase + normalizedPath;
    }

    private async Task<string?> ResolveWebhookClientIdAsync(Guid tenantId, CancellationToken cancellationToken)
    {
        var key = $"{SendEngineWebhookClientKeyPrefix}{tenantId:D}";
        var flag = await _dbContext.SystemFlags
            .AsNoTracking()
            .FirstOrDefaultAsync(x => x.Key == key, cancellationToken);
        if (flag is null || string.IsNullOrWhiteSpace(flag.Value))
        {
            return null;
        }

        return flag.Value.Trim();
    }

    private static string ComputeHmacHex(string secret, string payload)
    {
        var key = Encoding.UTF8.GetBytes(secret);
        using var hmac = new HMACSHA256(key);
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
        return Convert.ToHexString(hash).ToLowerInvariant();
    }
}
