using ChatService.Domain.Contracts;
using ChatService.Infrastructure.Services;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

namespace ChatService.UnitTests.Infrastructure.Services;

/// <summary>
/// EN: Unit tests for NullAIService (used when OpenAI API key is not configured).
/// VI: Unit tests cho NullAIService (dùng khi chưa cấu hình OpenAI API key).
/// </summary>
public class NullAIServiceTests
{
    private readonly NullAIService _sut;

    public NullAIServiceTests()
    {
        _sut = new NullAIService();
    }

    [Fact]
    public async Task IsAvailableAsync_ShouldReturnFalse()
    {
        // Act
        var result = await _sut.IsAvailableAsync();

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

    [Fact]
    public async Task GetResponseAsync_ShouldReturnNotConfiguredMessage()
    {
        // Arrange
        var prompt = "Hello AI";
        var history = Enumerable.Empty<ChatMessage>();

        // Act
        var result = await _sut.GetResponseAsync(prompt, history);

        // Assert
        result.Should().Contain("not configured");
    }

    [Fact]
    public async Task StreamResponseAsync_ShouldReturnNotConfiguredMessage()
    {
        // Arrange
        var prompt = "Hello AI";
        var history = Enumerable.Empty<ChatMessage>();
        var chunks = new List<string>();

        // Act
        await foreach (var chunk in _sut.StreamResponseAsync(prompt, history))
        {
            chunks.Add(chunk);
        }

        // Assert
        chunks.Should().HaveCount(1);
        chunks[0].Should().Contain("not configured");
    }

    [Fact]
    public void NullAIService_ShouldImplementIAIService()
    {
        // Assert
        _sut.Should().BeAssignableTo<IAIService>();
    }
}

/// <summary>
/// EN: Unit tests for AIService (requires mocking HttpClient).
/// VI: Unit tests cho AIService (cần mock HttpClient).
/// </summary>
public class AIServiceTests
{
    [Fact]
    public void AIService_ShouldImplementIAIService()
    {
        // Arrange
        var httpClient = new HttpClient();
        var options = Options.Create(new AIServiceOptions());
        var logger = new Mock<ILogger<AIService>>().Object;

        // Act
        var service = new AIService(httpClient, options, logger);

        // Assert
        service.Should().BeAssignableTo<IAIService>();
    }

    [Fact]
    public void AIService_ShouldUseProvidedOptions()
    {
        // Arrange
        var httpClient = new HttpClient();
        var options = Options.Create(new AIServiceOptions
        {
            Model = "gpt-3.5-turbo",
            MaxHistoryMessages = 10
        });
        var logger = new Mock<ILogger<AIService>>().Object;

        // Act
        var service = new AIService(httpClient, options, logger);

        // Assert
        service.Should().NotBeNull();
    }

    [Fact]
    public async Task IsAvailableAsync_ShouldReturnFalse_WhenApiReturnsError()
    {
        // Arrange
        var mockHandler = new MockHttpMessageHandler(new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized));
        var httpClient = new HttpClient(mockHandler);
        var options = Options.Create(new AIServiceOptions());
        var logger = new Mock<ILogger<AIService>>().Object;
        var service = new AIService(httpClient, options, logger);

        // Act
        var result = await service.IsAvailableAsync();

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

/// <summary>
/// EN: Mock HTTP message handler for testing.
/// VI: Mock HTTP message handler cho testing.
/// </summary>
internal class MockHttpMessageHandler : HttpMessageHandler
{
    private readonly HttpResponseMessage _response;

    public MockHttpMessageHandler(HttpResponseMessage response)
    {
        _response = response;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        return Task.FromResult(_response);
    }
}
