using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Polly;
using Polly.Retry;

namespace PromotionService.API.Application.Services;

/// <summary>
/// EN: HTTP client implementation for Wallet Service.
/// VI: Triển khai HTTP client cho Wallet Service.
/// </summary>
public class WalletServiceClient : IWalletServiceClient
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<WalletServiceClient> _logger;
    private readonly AsyncRetryPolicy<HttpResponseMessage> _retryPolicy;

    public WalletServiceClient(HttpClient httpClient, ILogger<WalletServiceClient> logger)
    {
        _httpClient = httpClient;
        _logger = logger;

        // Retry policy with exponential backoff
        _retryPolicy = Policy<HttpResponseMessage>
            .Handle<HttpRequestException>()
            .OrResult(r => !r.IsSuccessStatusCode && r.StatusCode != System.Net.HttpStatusCode.BadRequest)
            .WaitAndRetryAsync(3, retryAttempt =>
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                (result, timeSpan, retryCount, context) =>
                {
                    _logger.LogWarning("Retry {RetryCount} for Wallet Service call after {Delay}s",
                        retryCount, timeSpan.TotalSeconds);
                });
    }

    public async Task<HoldResult> CreateHoldAsync(
        Guid walletId,
        decimal amount,
        string currencyType,
        string referenceType,
        Guid referenceId,
        string description,
        CancellationToken cancellationToken = default)
    {
        var request = new
        {
            Amount = amount,
            CurrencyType = currencyType,
            ReferenceType = referenceType,
            ReferenceId = referenceId,
            Description = description
        };

        var response = await _retryPolicy.ExecuteAsync(async () =>
            await _httpClient.PostAsJsonAsync($"/api/v1/wallets/{walletId}/holds", request, cancellationToken));

        response.EnsureSuccessStatusCode();

        var result = await response.Content.ReadFromJsonAsync<HoldResult>(cancellationToken: cancellationToken)
            ?? throw new InvalidOperationException("Failed to deserialize hold result");

        _logger.LogInformation("Created hold {HoldId} for wallet {WalletId}", result.HoldId, walletId);
        return result;
    }

    public async Task<bool> ExecuteHoldAsync(
        Guid walletId,
        Guid holdId,
        decimal amount,
        string? executionRef = null,
        CancellationToken cancellationToken = default)
    {
        var request = new { Amount = amount, ExecutionReference = executionRef };

        var response = await _retryPolicy.ExecuteAsync(async () =>
            await _httpClient.PostAsJsonAsync($"/api/v1/wallets/{walletId}/holds/{holdId}/execute", request, cancellationToken));

        if (!response.IsSuccessStatusCode)
        {
            _logger.LogError("Failed to execute hold {HoldId}: {StatusCode}", holdId, response.StatusCode);
            return false;
        }

        _logger.LogInformation("Executed hold {HoldId} for {Amount}", holdId, amount);
        return true;
    }

    public async Task<bool> ReleaseHoldAsync(
        Guid walletId,
        Guid holdId,
        decimal? amount = null,
        CancellationToken cancellationToken = default)
    {
        var request = new { Amount = amount };

        var response = await _retryPolicy.ExecuteAsync(async () =>
            await _httpClient.PostAsJsonAsync($"/api/v1/wallets/{walletId}/holds/{holdId}/release", request, cancellationToken));

        if (!response.IsSuccessStatusCode)
        {
            _logger.LogError("Failed to release hold {HoldId}: {StatusCode}", holdId, response.StatusCode);
            return false;
        }

        _logger.LogInformation("Released hold {HoldId}", holdId);
        return true;
    }

    public async Task<bool> CancelHoldAsync(
        Guid walletId,
        Guid holdId,
        CancellationToken cancellationToken = default)
    {
        var response = await _retryPolicy.ExecuteAsync(async () =>
            await _httpClient.PostAsync($"/api/v1/wallets/{walletId}/holds/{holdId}/cancel", null, cancellationToken));

        if (!response.IsSuccessStatusCode)
        {
            _logger.LogError("Failed to cancel hold {HoldId}: {StatusCode}", holdId, response.StatusCode);
            return false;
        }

        _logger.LogInformation("Cancelled hold {HoldId}", holdId);
        return true;
    }

    public async Task<WalletInfo?> GetWalletByUserIdAsync(
        Guid userId,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _httpClient.GetAsync($"/api/v1/wallets/user/{userId}", cancellationToken);

            if (!response.IsSuccessStatusCode)
                return null;

            return await response.Content.ReadFromJsonAsync<WalletInfo>(cancellationToken: cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to get wallet for user {UserId}", userId);
            return null;
        }
    }
}
