// EN: Functional tests for OrdersController covering the full order lifecycle.
// VI: Functional tests cho OrdersController bao phủ toàn bộ vòng đời order.

using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using OrderService.Domain.AggregatesModel.OrderAggregate;
using OrderService.Infrastructure;
using Xunit;

namespace OrderService.FunctionalTests.Controllers;

/// <summary>
/// EN: Functional tests for the Orders API endpoints.
///     Tests the full order lifecycle: create -> pay -> complete/cancel.
///     Uses InMemory database and mocked external services.
/// VI: Functional tests cho Orders API endpoints.
///     Test toàn bộ vòng đời order: tạo -> thanh toán -> hoàn thành/hủy.
///     Sử dụng InMemory database và mock external services.
/// </summary>
[Collection("OrderService")]
public class OrdersControllerTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly HttpClient _client;
    private readonly CustomWebApplicationFactory _factory;

    /// <summary>
    /// EN: Default shop ID used in tests (matches TestAuthHandler.TestShopId).
    /// VI: Shop ID mặc định sử dụng trong tests (khớp với TestAuthHandler.TestShopId).
    /// </summary>
    private static readonly Guid TestShopId = TestAuthHandler.TestShopId;

    /// <summary>
    /// EN: JSON serializer options matching the API's camelCase convention.
    /// VI: Tùy chọn JSON serializer khớp với quy ước camelCase của API.
    /// </summary>
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

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

    // ============================================================
    // EN: Helper methods
    // VI: Các phương thức helper
    // ============================================================

    /// <summary>
    /// EN: Create a valid order request payload.
    /// VI: Tạo payload request order hợp lệ.
    /// </summary>
    private static object CreateValidOrderRequest(Guid? shopId = null, decimal unitPrice = 50_000m)
    {
        return new
        {
            shopId = shopId ?? TestShopId,
            items = new[]
            {
                new
                {
                    productId = Guid.NewGuid(),
                    productName = "Test Product",
                    productType = "Physical",
                    quantity = 2,
                    unitPrice = unitPrice,
                    trackInventory = true
                }
            }
        };
    }

    /// <summary>
    /// EN: Create an order via API and return the order ID.
    /// VI: Tạo order qua API và trả về order ID.
    /// </summary>
    private async Task<Guid> CreateOrderAndGetIdAsync(Guid? shopId = null, decimal unitPrice = 50_000m)
    {
        var request = CreateValidOrderRequest(shopId, unitPrice);
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);
        response.EnsureSuccessStatusCode();

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        return body.GetProperty("orderId").GetGuid();
    }

    /// <summary>
    /// EN: Pay for an order via API (cash payment).
    /// VI: Thanh toán order qua API (tiền mặt).
    /// </summary>
    private async Task PayOrderCashAsync(Guid orderId, decimal amountTendered = 200_000m)
    {
        var payRequest = new { paymentMethod = "cash", amountTendered = amountTendered };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);
        response.EnsureSuccessStatusCode();
    }

    // ============================================================
    // EN: CREATE ORDER tests
    // VI: Tests TẠO ORDER
    // ============================================================

    [Fact]
    public async Task CreateOrder_WithValidData_Returns201()
    {
        // Arrange
        var request = CreateValidOrderRequest();

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("orderId").GetGuid().Should().NotBeEmpty();
        body.GetProperty("totalAmount").GetDecimal().Should().Be(100_000m);
        body.GetProperty("status").GetString().Should().Be("Validated");
    }

    [Fact]
    public async Task CreateOrder_WithMultipleItems_Returns201WithCorrectTotal()
    {
        // Arrange
        var request = new
        {
            shopId = TestShopId,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Item A", productType = "Physical", quantity = 1, unitPrice = 30_000m, trackInventory = true },
                new { productId = Guid.NewGuid(), productName = "Item B", productType = "Service", quantity = 3, unitPrice = 15_000m, trackInventory = false },
            }
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        // 30_000 * 1 + 15_000 * 3 = 75_000
        body.GetProperty("totalAmount").GetDecimal().Should().Be(75_000m);
    }

    [Fact]
    public async Task CreateOrder_WithEmptyShopId_ReturnsError()
    {
        // Arrange
        var request = new
        {
            shopId = Guid.Empty,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Test", productType = "Physical", quantity = 1, unitPrice = 10_000m, trackInventory = true }
            }
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert — FluentValidation throws ValidationException via MediatR pipeline;
        //          Hellang ProblemDetails maps this to 500 (no explicit mapping for FluentValidation.ValidationException).
        //          400 would be preferred, but requires ProblemDetails.Map<ValidationException> config.
        response.IsSuccessStatusCode.Should().BeFalse();
        response.StatusCode.Should().BeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.InternalServerError);
    }

    [Fact]
    public async Task CreateOrder_WithNoItems_ReturnsError()
    {
        // Arrange
        var request = new
        {
            shopId = TestShopId,
            items = Array.Empty<object>()
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.IsSuccessStatusCode.Should().BeFalse();
        response.StatusCode.Should().BeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.InternalServerError);
    }

    [Fact]
    public async Task CreateOrder_WithInvalidProductType_ReturnsError()
    {
        // Arrange
        var request = new
        {
            shopId = TestShopId,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Test", productType = "InvalidType", quantity = 1, unitPrice = 10_000m, trackInventory = true }
            }
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.IsSuccessStatusCode.Should().BeFalse();
        response.StatusCode.Should().BeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.InternalServerError);
    }

    [Fact]
    public async Task CreateOrder_WithZeroQuantity_ReturnsError()
    {
        // Arrange
        var request = new
        {
            shopId = TestShopId,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Test", productType = "Physical", quantity = 0, unitPrice = 10_000m, trackInventory = true }
            }
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.IsSuccessStatusCode.Should().BeFalse();
        response.StatusCode.Should().BeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.InternalServerError);
    }

    [Fact]
    public async Task CreateOrder_WithDiscount_Returns201WithDiscountedTotal()
    {
        // Arrange
        var request = new
        {
            shopId = TestShopId,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Test Product", productType = "Physical", quantity = 2, unitPrice = 50_000m, trackInventory = true }
            },
            discountAmount = 20_000m,
            discountType = "promotion",
            discountReference = "PROMO-001"
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        // 50_000 * 2 - 20_000 = 80_000
        body.GetProperty("totalAmount").GetDecimal().Should().Be(80_000m);
    }

    // ============================================================
    // EN: PAY ORDER tests
    // VI: Tests THANH TOAN ORDER
    // ============================================================

    [Fact]
    public async Task PayOrder_CashPayment_Returns200WithChange()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync(unitPrice: 50_000m);
        var payRequest = new { paymentMethod = "cash", amountTendered = 200_000m };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("data").GetProperty("changeAmount").GetDecimal().Should().Be(100_000m);
        body.GetProperty("data").GetProperty("transactionId").GetString().Should().StartWith("CASH-");
    }

    [Fact]
    public async Task PayOrder_CardPayment_Returns200()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync();
        var payRequest = new { paymentMethod = "card" };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("data").GetProperty("transactionId").GetString().Should().StartWith("CARD-");
    }

    [Fact]
    public async Task PayOrder_OnlinePayment_Returns200WithPaymentUrl()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync();
        var payRequest = new
        {
            paymentMethod = "vnpay",
            returnUrl = "https://myshop.test/return"
        };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("data").GetProperty("paymentUrl").GetString().Should().StartWith("https://mock-gateway.test/pay");
        body.GetProperty("data").GetProperty("status").GetString().Should().Be("PaymentPending");
    }

    [Fact]
    public async Task PayOrder_CashWithInsufficientAmount_Returns400()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync(unitPrice: 50_000m);
        // 2 items * 50_000 = 100_000, but tendering only 50_000
        var payRequest = new { paymentMethod = "cash", amountTendered = 50_000m };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeFalse();
    }

    [Fact]
    public async Task PayOrder_AlreadyPaid_Returns500()
    {
        // Arrange — create and pay an order
        var orderId = await CreateOrderAndGetIdAsync();
        await PayOrderCashAsync(orderId);

        // Act — try to pay again
        var payRequest = new { paymentMethod = "cash", amountTendered = 200_000m };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

        // Assert — should fail (order is already Processing, not Validated)
        response.StatusCode.Should().BeOneOf(
            HttpStatusCode.BadRequest,
            HttpStatusCode.InternalServerError);
    }

    // ============================================================
    // EN: COMPLETE ORDER tests
    // VI: Tests HOAN THANH ORDER
    // ============================================================

    [Fact]
    public async Task CompleteOrder_AfterPayment_Returns200()
    {
        // Arrange — create, pay (transitions to Processing)
        var orderId = await CreateOrderAndGetIdAsync();
        await PayOrderCashAsync(orderId);

        // Act — complete the order
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/complete?shopId={TestShopId}",
            new { });

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("status").GetString().Should().Be("Completed");
    }

    [Fact]
    public async Task CompleteOrder_WithoutPayment_Returns500()
    {
        // Arrange — create order but don't pay (status = Validated)
        var orderId = await CreateOrderAndGetIdAsync();

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/complete?shopId={TestShopId}",
            new { });

        // Assert — should fail because order is not in Processing state
        response.StatusCode.Should().BeOneOf(
            HttpStatusCode.BadRequest,
            HttpStatusCode.InternalServerError);
    }

    // ============================================================
    // EN: CANCEL ORDER tests
    // VI: Tests HUY ORDER
    // ============================================================

    [Fact]
    public async Task CancelOrder_PendingOrder_Returns200()
    {
        // Arrange — create an order (status = Validated)
        var orderId = await CreateOrderAndGetIdAsync();

        // Act
        var cancelRequest = new { reason = "Customer changed their mind" };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/cancel?shopId={TestShopId}", cancelRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("status").GetString().Should().Be("Cancelled");
    }

    [Fact]
    public async Task CancelOrder_AlreadyCompleted_Returns500()
    {
        // Arrange — create, pay, and complete the order
        var orderId = await CreateOrderAndGetIdAsync();
        await PayOrderCashAsync(orderId);

        var completeResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/complete?shopId={TestShopId}",
            new { });
        completeResponse.EnsureSuccessStatusCode();

        // Act — try to cancel a completed order
        var cancelRequest = new { reason = "Too late" };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/cancel?shopId={TestShopId}", cancelRequest);

        // Assert — should fail (cannot cancel completed order)
        response.StatusCode.Should().BeOneOf(
            HttpStatusCode.BadRequest,
            HttpStatusCode.InternalServerError);
    }

    [Fact]
    public async Task CancelOrder_AlreadyCancelled_Returns500()
    {
        // Arrange — create and cancel an order
        var orderId = await CreateOrderAndGetIdAsync();

        var cancelRequest = new { reason = "First cancellation" };
        var firstResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/cancel?shopId={TestShopId}", cancelRequest);
        firstResponse.EnsureSuccessStatusCode();

        // Act — try to cancel again
        var secondCancelRequest = new { reason = "Second cancellation attempt" };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/cancel?shopId={TestShopId}", secondCancelRequest);

        // Assert — should fail (already cancelled)
        response.StatusCode.Should().BeOneOf(
            HttpStatusCode.BadRequest,
            HttpStatusCode.InternalServerError);
    }

    [Fact]
    public async Task CancelOrder_WithEmptyReason_ReturnsError()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync();

        // Act
        var cancelRequest = new { reason = "" };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/cancel?shopId={TestShopId}", cancelRequest);

        // Assert — FluentValidation rejects empty reason;
        //          mapped as 500 via ProblemDetails (no explicit ValidationException mapping).
        response.IsSuccessStatusCode.Should().BeFalse();
        response.StatusCode.Should().BeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.InternalServerError);
    }

    // ============================================================
    // EN: PAYMENT CALLBACK tests
    // VI: Tests CALLBACK THANH TOAN
    // ============================================================

    [Fact]
    public async Task PaymentCallback_SuccessfulPayment_Returns200()
    {
        // Arrange — create order and initiate online payment (status -> PaymentPending)
        var orderId = await CreateOrderAndGetIdAsync();

        var payRequest = new
        {
            paymentMethod = "vnpay",
            returnUrl = "https://myshop.test/return"
        };
        var payResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);
        payResponse.EnsureSuccessStatusCode();

        // Act — simulate successful gateway callback
        var callbackRequest = new
        {
            gatewayTransactionId = "VNP-TXN-12345",
            isSuccess = true,
            gatewayResponseCode = "00"
        };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/payment-callback", callbackRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("data").GetProperty("status").GetString().Should().Be("Processing");
    }

    [Fact]
    public async Task PaymentCallback_FailedPayment_CancelsOrder()
    {
        // Arrange — create order and initiate online payment
        var orderId = await CreateOrderAndGetIdAsync();

        var payRequest = new
        {
            paymentMethod = "momo",
            returnUrl = "https://myshop.test/return"
        };
        var payResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);
        payResponse.EnsureSuccessStatusCode();

        // Act — simulate failed gateway callback
        var callbackRequest = new
        {
            gatewayTransactionId = "MOMO-TXN-FAIL",
            isSuccess = false,
            gatewayResponseCode = "99"
        };
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/payment-callback", callbackRequest);

        // Assert — payment failed but callback endpoint succeeds, order is cancelled
        response.StatusCode.Should().Be(HttpStatusCode.BadRequest);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeFalse();
    }

    // ============================================================
    // EN: FULL LIFECYCLE tests
    // VI: Tests VONG DOI DAY DU
    // ============================================================

    [Fact]
    public async Task FullLifecycle_CashPayment_CreatePayComplete()
    {
        // Step 1: Create order
        var request = new
        {
            shopId = TestShopId,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Bia Saigon", productType = "Physical", quantity = 5, unitPrice = 15_000m, trackInventory = true },
                new { productId = Guid.NewGuid(), productName = "Pho", productType = "PreparedFood", quantity = 2, unitPrice = 45_000m, trackInventory = false }
            }
        };

        var createResponse = await _client.PostAsJsonAsync("/api/v1/orders", request);
        createResponse.StatusCode.Should().Be(HttpStatusCode.Created);

        var createBody = await createResponse.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        var orderId = createBody.GetProperty("orderId").GetGuid();
        // 15_000 * 5 + 45_000 * 2 = 165_000
        createBody.GetProperty("totalAmount").GetDecimal().Should().Be(165_000m);
        createBody.GetProperty("status").GetString().Should().Be("Validated");

        // Step 2: Pay with cash
        var payRequest = new { paymentMethod = "cash", amountTendered = 200_000m };
        var payResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);
        payResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var payBody = await payResponse.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        payBody.GetProperty("success").GetBoolean().Should().BeTrue();
        payBody.GetProperty("data").GetProperty("changeAmount").GetDecimal().Should().Be(35_000m);

        // Step 3: Complete order
        var completeResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/complete?shopId={TestShopId}", new { });
        completeResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var completeBody = await completeResponse.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        completeBody.GetProperty("success").GetBoolean().Should().BeTrue();
        completeBody.GetProperty("status").GetString().Should().Be("Completed");
    }

    [Fact]
    public async Task FullLifecycle_OnlinePayment_CreatePayCallbackComplete()
    {
        // Step 1: Create order
        var orderId = await CreateOrderAndGetIdAsync(unitPrice: 100_000m);

        // Step 2: Initiate online payment
        var payRequest = new
        {
            paymentMethod = "vnpay",
            returnUrl = "https://myshop.test/return"
        };
        var payResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);
        payResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var payBody = await payResponse.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        payBody.GetProperty("success").GetBoolean().Should().BeTrue();
        payBody.GetProperty("data").GetProperty("paymentUrl").GetString().Should().Contain("mock-gateway");
        payBody.GetProperty("data").GetProperty("status").GetString().Should().Be("PaymentPending");

        // Step 3: Gateway callback (success)
        var callbackRequest = new
        {
            gatewayTransactionId = "VNP-SUCCESS-001",
            isSuccess = true,
            gatewayResponseCode = "00"
        };
        var callbackResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/payment-callback", callbackRequest);
        callbackResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var callbackBody = await callbackResponse.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        callbackBody.GetProperty("success").GetBoolean().Should().BeTrue();
        callbackBody.GetProperty("data").GetProperty("status").GetString().Should().Be("Processing");

        // Step 4: Complete order
        var completeResponse = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/complete?shopId={TestShopId}", new { });
        completeResponse.StatusCode.Should().Be(HttpStatusCode.OK);

        var completeBody = await completeResponse.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        completeBody.GetProperty("status").GetString().Should().Be("Completed");
    }

    // ============================================================
    // EN: HEALTH CHECK tests
    // VI: Tests KIEM TRA SUC KHOE
    // ============================================================

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

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

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

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

    // ============================================================
    // EN: EDGE CASE tests
    // VI: Tests TRUONG HOP CANH
    // ============================================================

    [Fact]
    public async Task PayOrder_QrPayment_TreatedAsCard()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync();
        var payRequest = new { paymentMethod = "qr" };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("data").GetProperty("transactionId").GetString().Should().StartWith("CARD-");
    }

    [Fact]
    public async Task PayOrder_TransferPayment_TreatedAsCard()
    {
        // Arrange
        var orderId = await CreateOrderAndGetIdAsync();
        var payRequest = new { paymentMethod = "transfer" };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
    }

    [Fact]
    public async Task CreateOrder_WithTableId_Returns201()
    {
        // Arrange
        var tableId = Guid.NewGuid();
        var request = new
        {
            shopId = TestShopId,
            customerId = Guid.NewGuid(),
            tableId = tableId,
            items = new[]
            {
                new { productId = Guid.NewGuid(), productName = "Pho Bo", productType = "PreparedFood", quantity = 1, unitPrice = 55_000m, trackInventory = false }
            }
        };

        // Act
        var response = await _client.PostAsJsonAsync("/api/v1/orders", request);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("orderId").GetGuid().Should().NotBeEmpty();
    }

    [Fact]
    public async Task PayOrder_CashExactAmount_ZeroChange()
    {
        // Arrange — order total = 2 * 50_000 = 100_000
        var orderId = await CreateOrderAndGetIdAsync(unitPrice: 50_000m);
        var payRequest = new { paymentMethod = "cash", amountTendered = 100_000m };

        // Act
        var response = await _client.PostAsJsonAsync(
            $"/api/v1/orders/{orderId}/pay?shopId={TestShopId}", payRequest);

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

        var body = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions);
        body.GetProperty("success").GetBoolean().Should().BeTrue();
        body.GetProperty("data").GetProperty("changeAmount").GetDecimal().Should().Be(0m);
    }
}
