using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;

namespace WhatsAppService.Infrastructure.ExternalServices;

/// <summary>
/// EN: WhatsApp Cloud API client implementation with resilience.
/// VI: Triển khai WhatsApp Cloud API client với khả năng phục hồi.
/// </summary>
public class WhatsAppCloudApiClient : IWhatsAppCloudApiClient
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<WhatsAppCloudApiClient> _logger;
    private readonly JsonSerializerOptions _jsonOptions;

    private const string BaseUrl = "https://graph.facebook.com";
    private const string ApiVersion = "v18.0";

    public WhatsAppCloudApiClient(HttpClient httpClient, ILogger<WhatsAppCloudApiClient> logger)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
        };
    }

    public async Task<SendMessageResult> SendTextMessageAsync(
        string phoneNumberId,
        string accessToken,
        string recipientWaId,
        string text,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            messaging_product = "whatsapp",
            recipient_type = "individual",
            to = recipientWaId,
            type = "text",
            text = new { body = text }
        };

        return await SendMessageAsync(phoneNumberId, accessToken, payload, cancellationToken);
    }

    public async Task<SendMessageResult> SendTemplateMessageAsync(
        string phoneNumberId,
        string accessToken,
        string recipientWaId,
        string templateName,
        string languageCode,
        object[]? components = null,
        CancellationToken cancellationToken = default)
    {
        var template = new Dictionary<string, object>
        {
            ["name"] = templateName,
            ["language"] = new { code = languageCode }
        };

        if (components != null)
        {
            template["components"] = components;
        }

        var payload = new
        {
            messaging_product = "whatsapp",
            recipient_type = "individual",
            to = recipientWaId,
            type = "template",
            template
        };

        return await SendMessageAsync(phoneNumberId, accessToken, payload, cancellationToken);
    }

    public async Task<SendMessageResult> SendMediaMessageAsync(
        string phoneNumberId,
        string accessToken,
        string recipientWaId,
        string mediaType,
        string mediaUrl,
        string? caption = null,
        CancellationToken cancellationToken = default)
    {
        var mediaContent = new Dictionary<string, object> { ["link"] = mediaUrl };
        if (!string.IsNullOrEmpty(caption))
        {
            mediaContent["caption"] = caption;
        }

        var payload = new Dictionary<string, object>
        {
            ["messaging_product"] = "whatsapp",
            ["recipient_type"] = "individual",
            ["to"] = recipientWaId,
            ["type"] = mediaType,
            [mediaType] = mediaContent
        };

        return await SendMessageAsync(phoneNumberId, accessToken, payload, cancellationToken);
    }

    public async Task<SendMessageResult> SendInteractiveMessageAsync(
        string phoneNumberId,
        string accessToken,
        string recipientWaId,
        object interactiveContent,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            messaging_product = "whatsapp",
            recipient_type = "individual",
            to = recipientWaId,
            type = "interactive",
            interactive = interactiveContent
        };

        return await SendMessageAsync(phoneNumberId, accessToken, payload, cancellationToken);
    }

    public async Task<bool> MarkMessageAsReadAsync(
        string phoneNumberId,
        string accessToken,
        string messageId,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var url = $"{BaseUrl}/{ApiVersion}/{phoneNumberId}/messages";
            var payload = new
            {
                messaging_product = "whatsapp",
                status = "read",
                message_id = messageId
            };

            using var request = new HttpRequestMessage(HttpMethod.Post, url);
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
            request.Content = JsonContent.Create(payload, options: _jsonOptions);

            var response = await _httpClient.SendAsync(request, cancellationToken);
            return response.IsSuccessStatusCode;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to mark message {MessageId} as read", messageId);
            return false;
        }
    }

    public async Task<string?> GetMediaUrlAsync(
        string accessToken,
        string mediaId,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var url = $"{BaseUrl}/{ApiVersion}/{mediaId}";
            using var request = new HttpRequestMessage(HttpMethod.Get, url);
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

            var response = await _httpClient.SendAsync(request, cancellationToken);
            if (!response.IsSuccessStatusCode) return null;

            var result = await response.Content.ReadFromJsonAsync<MediaResponse>(cancellationToken: cancellationToken);
            return result?.Url;
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to get media URL for {MediaId}", mediaId);
            return null;
        }
    }

    private async Task<SendMessageResult> SendMessageAsync(
        string phoneNumberId,
        string accessToken,
        object payload,
        CancellationToken cancellationToken)
    {
        try
        {
            var url = $"{BaseUrl}/{ApiVersion}/{phoneNumberId}/messages";

            using var request = new HttpRequestMessage(HttpMethod.Post, url);
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
            request.Content = JsonContent.Create(payload, options: _jsonOptions);

            _logger.LogDebug("Sending message to WhatsApp API: {Url}", url);

            var response = await _httpClient.SendAsync(request, cancellationToken);
            var content = await response.Content.ReadAsStringAsync(cancellationToken);

            if (response.IsSuccessStatusCode)
            {
                var result = JsonSerializer.Deserialize<WhatsAppSendResponse>(content, _jsonOptions);
                var messageId = result?.Messages?.FirstOrDefault()?.Id;
                
                _logger.LogInformation("Message sent successfully. MessageId: {MessageId}", messageId);
                return new SendMessageResult(true, messageId, null, null);
            }

            var error = JsonSerializer.Deserialize<WhatsAppErrorResponse>(content, _jsonOptions);
            _logger.LogWarning("WhatsApp API error: {Code} - {Message}", error?.Error?.Code, error?.Error?.Message);
            
            return new SendMessageResult(false, null, error?.Error?.Code?.ToString(), error?.Error?.Message);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send message via WhatsApp API");
            return new SendMessageResult(false, null, "EXCEPTION", ex.Message);
        }
    }

    private record WhatsAppSendResponse(
        [property: JsonPropertyName("messages")] List<MessageInfo>? Messages);
    
    private record MessageInfo(
        [property: JsonPropertyName("id")] string? Id);
    
    private record WhatsAppErrorResponse(
        [property: JsonPropertyName("error")] ErrorInfo? Error);
    
    private record ErrorInfo(
        [property: JsonPropertyName("code")] int? Code,
        [property: JsonPropertyName("message")] string? Message);
    
    private record MediaResponse(
        [property: JsonPropertyName("url")] string? Url);
}
