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

namespace FacebookService.Infrastructure.ExternalServices;

/// <summary>
/// EN: Facebook Messenger API client implementation.
/// VI: Triển khai client cho Facebook Messenger API.
/// </summary>
public class FacebookMessengerClient : IFacebookMessengerClient
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<FacebookMessengerClient> _logger;
    private readonly string _pageAccessToken;
    private readonly string _apiVersion;

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    };

    public FacebookMessengerClient(
        HttpClient httpClient,
        IConfiguration configuration,
        ILogger<FacebookMessengerClient> logger)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        _pageAccessToken = configuration["Facebook:PageAccessToken"] 
            ?? configuration["FACEBOOK_PAGE_ACCESS_TOKEN"]
            ?? throw new InvalidOperationException("Facebook Page Access Token not configured");

        _apiVersion = configuration["Facebook:ApiVersion"] 
            ?? configuration["FACEBOOK_API_VERSION"]
            ?? "v18.0";

        _httpClient.BaseAddress = new Uri($"https://graph.facebook.com/{_apiVersion}/");
    }

    /// <inheritdoc/>
    public async Task<SendMessageResult> SendTextMessageAsync(
        string recipientId,
        string message,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            recipient = new { id = recipientId },
            message = new { text = message }
        };

        return await SendRequestAsync(payload, cancellationToken);
    }

    /// <inheritdoc/>
    public async Task<SendMessageResult> SendQuickRepliesAsync(
        string recipientId,
        string message,
        IEnumerable<QuickReply> quickReplies,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            recipient = new { id = recipientId },
            message = new
            {
                text = message,
                quick_replies = quickReplies.Select(qr => new
                {
                    content_type = qr.ContentType,
                    title = qr.Title,
                    payload = qr.Payload,
                    image_url = qr.ImageUrl
                })
            }
        };

        return await SendRequestAsync(payload, cancellationToken);
    }

    /// <inheritdoc/>
    public async Task<SendMessageResult> SendGenericTemplateAsync(
        string recipientId,
        IEnumerable<TemplateElement> elements,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            recipient = new { id = recipientId },
            message = new
            {
                attachment = new
                {
                    type = "template",
                    payload = new
                    {
                        template_type = "generic",
                        elements = elements.Select(MapElement)
                    }
                }
            }
        };

        return await SendRequestAsync(payload, cancellationToken);
    }

    /// <inheritdoc/>
    public async Task<SendMessageResult> SendButtonTemplateAsync(
        string recipientId,
        string text,
        IEnumerable<TemplateButton> buttons,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            recipient = new { id = recipientId },
            message = new
            {
                attachment = new
                {
                    type = "template",
                    payload = new
                    {
                        template_type = "button",
                        text = text,
                        buttons = buttons.Select(MapButton)
                    }
                }
            }
        };

        return await SendRequestAsync(payload, cancellationToken);
    }

    /// <inheritdoc/>
    public async Task<FacebookUserProfile?> GetUserProfileAsync(
        string userId,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var url = $"{userId}?fields=first_name,last_name,profile_pic,locale,timezone&access_token={_pageAccessToken}";
            var response = await _httpClient.GetAsync(url, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogWarning("Failed to get user profile for: {UserId}", userId);
                return null;
            }

            var json = await response.Content.ReadAsStringAsync(cancellationToken);
            var data = JsonSerializer.Deserialize<JsonElement>(json);

            return new FacebookUserProfile(
                Id: userId,
                FirstName: data.TryGetProperty("first_name", out var fn) ? fn.GetString() : null,
                LastName: data.TryGetProperty("last_name", out var ln) ? ln.GetString() : null,
                ProfilePic: data.TryGetProperty("profile_pic", out var pp) ? pp.GetString() : null,
                Locale: data.TryGetProperty("locale", out var loc) ? loc.GetString() : null,
                Timezone: data.TryGetProperty("timezone", out var tz) ? (float?)tz.GetSingle() : null
            );
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting user profile for: {UserId}", userId);
            return null;
        }
    }

    /// <inheritdoc/>
    public async Task<bool> MarkAsSeenAsync(
        string recipientId,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            recipient = new { id = recipientId },
            sender_action = "mark_seen"
        };

        var result = await SendRequestAsync(payload, cancellationToken);
        return result.Success;
    }

    /// <inheritdoc/>
    public async Task<bool> ShowTypingAsync(
        string recipientId,
        bool isTyping = true,
        CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            recipient = new { id = recipientId },
            sender_action = isTyping ? "typing_on" : "typing_off"
        };

        var result = await SendRequestAsync(payload, cancellationToken);
        return result.Success;
    }

    private async Task<SendMessageResult> SendRequestAsync(
        object payload,
        CancellationToken cancellationToken)
    {
        try
        {
            var url = $"me/messages?access_token={_pageAccessToken}";
            var response = await _httpClient.PostAsJsonAsync(url, payload, JsonOptions, cancellationToken);
            var content = await response.Content.ReadAsStringAsync(cancellationToken);

            if (response.IsSuccessStatusCode)
            {
                var data = JsonSerializer.Deserialize<JsonElement>(content);
                return new SendMessageResult(
                    Success: true,
                    MessageId: data.TryGetProperty("message_id", out var mid) ? mid.GetString() : null,
                    RecipientId: data.TryGetProperty("recipient_id", out var rid) ? rid.GetString() : null
                );
            }

            _logger.LogWarning("Facebook API error: {Response}", content);
            return new SendMessageResult(Success: false, Error: content);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending message to Facebook");
            return new SendMessageResult(Success: false, Error: ex.Message);
        }
    }

    private static object MapElement(TemplateElement element)
    {
        return new
        {
            title = element.Title,
            subtitle = element.Subtitle,
            image_url = element.ImageUrl,
            default_action = element.DefaultAction != null ? MapButton(element.DefaultAction) : null,
            buttons = element.Buttons?.Select(MapButton)
        };
    }

    private static object MapButton(TemplateButton button)
    {
        return button.Type switch
        {
            "web_url" => new { type = "web_url", title = button.Title, url = button.Url },
            "postback" => new { type = "postback", title = button.Title, payload = button.Payload },
            "phone_number" => new { type = "phone_number", title = button.Title, payload = button.Payload },
            _ => new { type = button.Type, title = button.Title }
        };
    }

    /// <summary>
    /// EN: Create retry policy for HTTP requests.
    /// VI: Tạo retry policy cho HTTP requests.
    /// </summary>
    public static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .WaitAndRetryAsync(3, retryAttempt => 
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
    }
}
