using System.Net;
using System.Text;
using FluentAssertions;
using WebClientTpos.Client.Services;
using Xunit;

namespace WebClientTpos.ComponentTests.Services;

/// <summary>
/// EN: Unit tests for PosDataService — smart 4-format JSON deserialization and HTTP helpers.
/// VI: Unit tests cho PosDataService — deserialize JSON thông minh 4 định dạng và các HTTP helper.
/// </summary>
public class PosDataServiceTests
{
    // EN: Simple item DTO used across tests.
    // VI: DTO item đơn giản dùng trong các test.
    private record ItemDto(Guid Id, string Name);

    // -----------------------------------------------------------------------
    // Helper — build PosDataService with a mocked HttpMessageHandler
    // -----------------------------------------------------------------------

    private static (PosDataService sut, AuthStateService authState) BuildSut(
        string responseBody,
        HttpStatusCode statusCode = HttpStatusCode.OK)
    {
        // EN: Auth is via BFF httpOnly cookie — AuthStateService only tracks email/role/expiry.
        // VI: Auth qua BFF httpOnly cookie — AuthStateService chỉ theo dõi email/role/expiry.
        var authState = new AuthStateService();
        authState.Login("test@goodgo.vn", "owner");

        var handler = new FakeHttpMessageHandler(responseBody, statusCode);
        var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost/") };

        var sut = new PosDataService(httpClient);
        return (sut, authState);
    }

    // -----------------------------------------------------------------------
    // GetListFromApiAsync — 4-format deserialization
    // -----------------------------------------------------------------------

    [Fact]
    public async Task GetListFromApiAsync_PlainArray_ShouldDeserializeCorrectly()
    {
        // Arrange — Case 1: plain JSON array
        var json = """
            [
                {"id":"00000000-0000-0000-0000-000000000001","name":"Espresso"},
                {"id":"00000000-0000-0000-0000-000000000002","name":"Cappuccino"}
            ]
            """;
        var (sut, _) = BuildSut(json);

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().HaveCount(2);
        result[0].Name.Should().Be("Espresso");
    }

    [Fact]
    public async Task GetListFromApiAsync_PagedResultWrapper_ShouldDeserializeCorrectly()
    {
        // Arrange — Case 2: { "items": [...] }
        var json = """
            {
                "items": [
                    {"id":"00000000-0000-0000-0000-000000000003","name":"Latte"},
                    {"id":"00000000-0000-0000-0000-000000000004","name":"Mocha"}
                ],
                "totalCount": 2,
                "pageNumber": 1,
                "pageSize": 10
            }
            """;
        var (sut, _) = BuildSut(json);

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().HaveCount(2);
        result[1].Name.Should().Be("Mocha");
    }

    [Fact]
    public async Task GetListFromApiAsync_ApiResponseWithPagedData_ShouldDeserializeCorrectly()
    {
        // Arrange — Case 3: { "data": { "items": [...] } }
        var json = """
            {
                "success": true,
                "data": {
                    "items": [
                        {"id":"00000000-0000-0000-0000-000000000005","name":"Flat White"}
                    ],
                    "totalCount": 1
                }
            }
            """;
        var (sut, _) = BuildSut(json);

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().HaveCount(1);
        result[0].Name.Should().Be("Flat White");
    }

    [Fact]
    public async Task GetListFromApiAsync_ApiResponseWithDirectArray_ShouldDeserializeCorrectly()
    {
        // Arrange — Case 4: { "data": [...] }
        var json = """
            {
                "success": true,
                "data": [
                    {"id":"00000000-0000-0000-0000-000000000006","name":"Americano"}
                ]
            }
            """;
        var (sut, _) = BuildSut(json);

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().HaveCount(1);
        result[0].Name.Should().Be("Americano");
    }

    [Fact]
    public async Task GetListFromApiAsync_GenericWrapper_ShouldDeserializeFirstArrayProperty()
    {
        // Arrange — Case 5: { "products": [...] } (generic single-array-property)
        var json = """
            {
                "products": [
                    {"id":"00000000-0000-0000-0000-000000000007","name":"Cold Brew"}
                ]
            }
            """;
        var (sut, _) = BuildSut(json);

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().HaveCount(1);
        result[0].Name.Should().Be("Cold Brew");
    }

    [Fact]
    public async Task GetListFromApiAsync_EmptyJson_ShouldReturnEmptyList()
    {
        // Arrange
        var (sut, _) = BuildSut("   ");

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().BeEmpty();
    }

    [Fact]
    public async Task GetListFromApiAsync_NonSuccessStatus_ShouldReturnEmptyList()
    {
        // Arrange
        var (sut, _) = BuildSut("{}", HttpStatusCode.InternalServerError);

        // Act
        var result = await sut.GetListFromApiAsync<ItemDto>("api/bff/products");

        // Assert
        result.Should().BeEmpty();
    }

    // -----------------------------------------------------------------------
    // PostAsync
    // -----------------------------------------------------------------------

    [Fact]
    public async Task PostAsync_OnSuccess_ShouldReturnTrue()
    {
        // Arrange
        var (sut, _) = BuildSut("{\"success\":true}", HttpStatusCode.OK);

        // Act
        var result = await sut.PostAsync("api/bff/orders", new { quantity = 1 });

        // Assert
        result.Should().BeTrue();
    }

    [Fact]
    public async Task PostAsync_OnBadRequest_ShouldReturnFalse()
    {
        // Arrange
        var (sut, _) = BuildSut("{\"success\":false}", HttpStatusCode.BadRequest);

        // Act
        var result = await sut.PostAsync("api/bff/orders", new { quantity = -1 });

        // Assert
        result.Should().BeFalse();
    }

    // -----------------------------------------------------------------------
    // PutAsync
    // -----------------------------------------------------------------------

    [Fact]
    public async Task PutAsync_OnSuccess_ShouldReturnTrue()
    {
        var (sut, _) = BuildSut("{}", HttpStatusCode.OK);
        var result = await sut.PutAsync("api/bff/products/1", new { price = 40000 });
        result.Should().BeTrue();
    }

    [Fact]
    public async Task PutAsync_OnNotFound_ShouldReturnFalse()
    {
        var (sut, _) = BuildSut("{}", HttpStatusCode.NotFound);
        var result = await sut.PutAsync("api/bff/products/nonexistent", new { price = 0 });
        result.Should().BeFalse();
    }
}

// ---------------------------------------------------------------------------
// Test helper — FakeHttpMessageHandler
// ---------------------------------------------------------------------------

/// <summary>
/// EN: In-memory HTTP handler for unit testing HttpClient-based services.
/// VI: HTTP handler in-memory để unit test các service dùng HttpClient.
/// </summary>
internal class FakeHttpMessageHandler : HttpMessageHandler
{
    private readonly string _responseBody;
    private readonly HttpStatusCode _statusCode;

    public FakeHttpMessageHandler(string responseBody, HttpStatusCode statusCode = HttpStatusCode.OK)
    {
        _responseBody = responseBody;
        _statusCode = statusCode;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(_statusCode)
        {
            Content = new StringContent(_responseBody, Encoding.UTF8, "application/json"),
        };
        return Task.FromResult(response);
    }
}
