using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace AdsBillingService.FunctionalTests.Controllers;

/// <summary>
/// EN: Functional tests for billing account, credit line, and invoice admin flows.
/// VI: Functional tests cho luồng tài khoản billing, hạn mức tín dụng và quản trị hóa đơn.
/// </summary>
public class AdsBillingControllerTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client;

    public AdsBillingControllerTests(CustomWebApplicationFactory factory)
    {
        _client = factory.CreateClient(new WebApplicationFactoryClientOptions
        {
            AllowAutoRedirect = false,
        });
    }

    [Fact]
    public async Task BillingAccountFlow_ShouldCreateAccount_AddFunds_AndReturnBalance()
    {
        // Arrange
        var advertiserId = Guid.NewGuid();
        var createRequest = new
        {
            advertiserId,
            walletId = Guid.NewGuid(),
            paymentMethod = "prepaid",
        };

        // Act
        var createResponse = await _client.PostAsJsonAsync("/api/v1/ads-billing/accounts", createRequest);
        var accountId = await createResponse.Content.ReadFromJsonAsync<Guid>();

        var addFundsResponse = await _client.PostAsJsonAsync(
            $"/api/v1/ads-billing/accounts/{accountId}/add-funds",
            new { amount = 500m });

        var balanceResponse = await _client.GetAsync($"/api/v1/ads-billing/accounts/{accountId}/balance");

        // Assert
        createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
        accountId.Should().NotBe(Guid.Empty);
        addFundsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
        balanceResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var balancePayload = await balanceResponse.Content.ReadFromJsonAsync<JsonElement>();
        balancePayload.GetProperty("balance").GetDecimal().Should().Be(500m);
    }

    [Fact]
    public async Task CreditLineRequest_ShouldIncreaseCreditLimit()
    {
        // Arrange
        var advertiserId = Guid.NewGuid();
        var createRequest = new
        {
            advertiserId,
            walletId = Guid.NewGuid(),
            paymentMethod = "postpaid",
        };

        var createResponse = await _client.PostAsJsonAsync("/api/v1/ads-billing/accounts", createRequest);
        createResponse.StatusCode.Should().Be(HttpStatusCode.Created);

        // Act
        var increaseResponse = await _client.PostAsJsonAsync(
            "/api/v1/ads-billing/credit-lines/request",
            new
            {
                advertiserId,
                requestedAmount = 1500m,
                reason = "Scale campaign budget",
            });

        var creditLineResponse = await _client.GetAsync($"/api/v1/ads-billing/credit-lines/{advertiserId}");

        // Assert
        increaseResponse.StatusCode.Should().Be(HttpStatusCode.Accepted);
        creditLineResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var creditPayload = await creditLineResponse.Content.ReadFromJsonAsync<JsonElement>();
        creditPayload.GetProperty("creditLimit").GetDecimal().Should().Be(1500m);
    }

    [Fact]
    public async Task RegenerateInvoice_WithoutCharges_ShouldReturnBadRequest()
    {
        // Arrange
        var advertiserId = Guid.NewGuid();
        var createResponse = await _client.PostAsJsonAsync(
            "/api/v1/ads-billing/accounts",
            new
            {
                advertiserId,
                walletId = Guid.NewGuid(),
                paymentMethod = "postpaid",
            });

        createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
        var accountId = await createResponse.Content.ReadFromJsonAsync<Guid>();

        // Act
        var regenerateResponse = await _client.PostAsJsonAsync(
            "/api/v1/admin/ads-billing/invoices/regenerate",
            new
            {
                billingAccountId = accountId,
                startDate = DateTime.UtcNow.AddDays(-7),
                endDate = DateTime.UtcNow,
            });

        // Assert
        regenerateResponse.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }

    [Fact]
    public async Task HealthCheck_ShouldReturnHealthy()
    {
        // Act
        var response = await _client.GetAsync("/health/live");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
    }
}
