using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MktZaloService.Infrastructure.Zalo.Models;

namespace MktZaloService.Infrastructure.Zalo;

/// <summary>
/// EN: Interface for Zalo Official Account API client.
/// VI: Interface cho Zalo Official Account API client.
/// </summary>
public interface IZaloOfficialAccountClient
{
    /// <summary>
    /// EN: Send a text message to a Zalo user.
    /// VI: Gửi tin nhắn văn bản đến người dùng Zalo.
    /// </summary>
    Task<ZaloSendMessageResponse> SendTextMessageAsync(
        string zaloUserId,
        string text,
        CancellationToken ct = default);

    /// <summary>
    /// EN: Send a ZNS template message.
    /// VI: Gửi tin nhắn mẫu ZNS.
    /// </summary>
    Task<ZaloSendMessageResponse> SendTemplateMessageAsync(
        string phoneNumber,
        string templateId,
        Dictionary<string, string> parameters,
        CancellationToken ct = default);

    /// <summary>
    /// EN: Get user profile by Zalo user ID.
    /// VI: Lấy thông tin người dùng theo Zalo user ID.
    /// </summary>
    Task<ZaloUserProfileData?> GetUserProfileAsync(
        string zaloUserId,
        CancellationToken ct = default);
}

/// <summary>
/// EN: HTTP client for Zalo Official Account API with Polly retry policies.
/// VI: HTTP client cho Zalo Official Account API với Polly retry policies.
/// </summary>
public class ZaloOfficialAccountClient : IZaloOfficialAccountClient
{
    private readonly HttpClient _httpClient;
    private readonly ZaloOAOptions _options;
    private readonly ILogger<ZaloOfficialAccountClient> _logger;

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

    public ZaloOfficialAccountClient(
        HttpClient httpClient,
        IOptions<ZaloOAOptions> options,
        ILogger<ZaloOfficialAccountClient> logger)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _options = options.Value ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        // EN: Set default authorization header
        // VI: Thiết lập header authorization mặc định
        _httpClient.DefaultRequestHeaders.Clear();
        _httpClient.DefaultRequestHeaders.Add("access_token", _options.AccessToken);
    }

    /// <summary>
    /// EN: Send a text message to a Zalo user.
    /// VI: Gửi tin nhắn văn bản đến người dùng Zalo.
    /// </summary>
    public async Task<ZaloSendMessageResponse> SendTextMessageAsync(
        string zaloUserId,
        string text,
        CancellationToken ct = default)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(zaloUserId);
        ArgumentException.ThrowIfNullOrWhiteSpace(text);

        var request = new ZaloSendTextRequest
        {
            Recipient = new ZaloMessageRecipient { UserId = zaloUserId },
            Message = new ZaloTextMessage { Text = text }
        };

        _logger.LogInformation(
            "EN: Sending text message to Zalo user {ZaloUserId}. VI: Đang gửi tin nhắn đến người dùng Zalo {ZaloUserId}",
            zaloUserId);

        var response = await _httpClient.PostAsJsonAsync(
            "/v3.0/oa/message/cs",
            request,
            JsonOptions,
            ct);

        response.EnsureSuccessStatusCode();

        var result = await response.Content.ReadFromJsonAsync<ZaloSendMessageResponse>(JsonOptions, ct);

        if (result?.Error != 0)
        {
            _logger.LogWarning(
                "EN: Zalo API error {Error}: {Message}. VI: Lỗi Zalo API {Error}: {Message}",
                result?.Error, result?.Message);
        }

        return result ?? new ZaloSendMessageResponse { Error = -1, Message = "Failed to parse response" };
    }

    /// <summary>
    /// EN: Send a ZNS template message.
    /// VI: Gửi tin nhắn mẫu ZNS.
    /// </summary>
    public async Task<ZaloSendMessageResponse> SendTemplateMessageAsync(
        string phoneNumber,
        string templateId,
        Dictionary<string, string> parameters,
        CancellationToken ct = default)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(phoneNumber);
        ArgumentException.ThrowIfNullOrWhiteSpace(templateId);

        var request = new ZaloSendTemplateRequest
        {
            Phone = phoneNumber,
            TemplateId = templateId,
            TemplateData = parameters ?? new Dictionary<string, string>()
        };

        _logger.LogInformation(
            "EN: Sending ZNS template {TemplateId} to {Phone}. VI: Đang gửi mẫu ZNS {TemplateId} đến {Phone}",
            templateId, phoneNumber);

        var response = await _httpClient.PostAsJsonAsync(
            "/v3.0/oa/message/template",
            request,
            JsonOptions,
            ct);

        response.EnsureSuccessStatusCode();

        var result = await response.Content.ReadFromJsonAsync<ZaloSendMessageResponse>(JsonOptions, ct);

        if (result?.Error != 0)
        {
            _logger.LogWarning(
                "EN: Zalo ZNS error {Error}: {Message}. VI: Lỗi Zalo ZNS {Error}: {Message}",
                result?.Error, result?.Message);
        }

        return result ?? new ZaloSendMessageResponse { Error = -1, Message = "Failed to parse response" };
    }

    /// <summary>
    /// EN: Get user profile by Zalo user ID.
    /// VI: Lấy thông tin người dùng theo Zalo user ID.
    /// </summary>
    public async Task<ZaloUserProfileData?> GetUserProfileAsync(
        string zaloUserId,
        CancellationToken ct = default)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(zaloUserId);

        var dataParam = JsonSerializer.Serialize(new { user_id = zaloUserId });
        var url = $"/v3.0/oa/user/detail?data={Uri.EscapeDataString(dataParam)}";

        _logger.LogDebug(
            "EN: Getting profile for Zalo user {ZaloUserId}. VI: Đang lấy thông tin người dùng Zalo {ZaloUserId}",
            zaloUserId);

        var response = await _httpClient.GetAsync(url, ct);
        response.EnsureSuccessStatusCode();

        var result = await response.Content.ReadFromJsonAsync<ZaloUserProfileResponse>(JsonOptions, ct);

        if (result?.Error != 0)
        {
            _logger.LogWarning(
                "EN: Failed to get Zalo user profile: {Error} - {Message}. VI: Không thể lấy thông tin: {Error} - {Message}",
                result?.Error, result?.Message);
            return null;
        }

        return result.Data;
    }
}
