using LiquidCode.Tester.Worker.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;

namespace LiquidCode.Tester.Worker.Tests;

public class ExecutionServiceFactoryTests
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ExecutionServiceFactory _factory;

    public ExecutionServiceFactoryTests()
    {
        var services = new ServiceCollection();

        // Mock configuration
        var configuration = new Mock<IConfiguration>();
        services.AddSingleton(configuration.Object);

        // Mock logger
        var loggerFactory = new Mock<ILoggerFactory>();
        loggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>()))
            .Returns(new Mock<ILogger>().Object);
        services.AddSingleton(loggerFactory.Object);
        services.AddLogging();

        // Register execution services
        services.AddSingleton<CppExecutionService>();
        services.AddSingleton<JavaExecutionService>();
        services.AddSingleton<KotlinExecutionService>();
        services.AddSingleton<CSharpExecutionService>();
        services.AddSingleton<PythonExecutionService>();

        _serviceProvider = services.BuildServiceProvider();
        _factory = new ExecutionServiceFactory(
            _serviceProvider,
            _serviceProvider.GetRequiredService<ILogger<ExecutionServiceFactory>>());
    }

    [Theory]
    [InlineData("C++", typeof(CppExecutionService))]
    [InlineData("c++", typeof(CppExecutionService))]
    [InlineData("cpp", typeof(CppExecutionService))]
    [InlineData("CPP", typeof(CppExecutionService))]
    public void GetExecutionService_CppLanguage_ReturnsCppExecutionService(string language, Type expectedType)
    {
        // Act
        var service = _factory.GetExecutionService(language);

        // Assert
        Assert.IsType(expectedType, service);
    }

    [Theory]
    [InlineData("Java", typeof(JavaExecutionService))]
    [InlineData("java", typeof(JavaExecutionService))]
    [InlineData("JAVA", typeof(JavaExecutionService))]
    public void GetExecutionService_JavaLanguage_ReturnsJavaExecutionService(string language, Type expectedType)
    {
        // Act
        var service = _factory.GetExecutionService(language);

        // Assert
        Assert.IsType(expectedType, service);
    }

    [Theory]
    [InlineData("Kotlin", typeof(KotlinExecutionService))]
    [InlineData("kotlin", typeof(KotlinExecutionService))]
    [InlineData("KOTLIN", typeof(KotlinExecutionService))]
    public void GetExecutionService_KotlinLanguage_ReturnsKotlinExecutionService(string language, Type expectedType)
    {
        // Act
        var service = _factory.GetExecutionService(language);

        // Assert
        Assert.IsType(expectedType, service);
    }

    [Theory]
    [InlineData("C#", typeof(CSharpExecutionService))]
    [InlineData("c#", typeof(CSharpExecutionService))]
    [InlineData("csharp", typeof(CSharpExecutionService))]
    [InlineData("CSharp", typeof(CSharpExecutionService))]
    [InlineData("CSHARP", typeof(CSharpExecutionService))]
    public void GetExecutionService_CSharpLanguage_ReturnsCSharpExecutionService(string language, Type expectedType)
    {
        // Act
        var service = _factory.GetExecutionService(language);

        // Assert
        Assert.IsType(expectedType, service);
    }

    [Theory]
    [InlineData("Python", typeof(PythonExecutionService))]
    [InlineData("python", typeof(PythonExecutionService))]
    [InlineData("PYTHON", typeof(PythonExecutionService))]
    public void GetExecutionService_PythonLanguage_ReturnsPythonExecutionService(string language, Type expectedType)
    {
        // Act
        var service = _factory.GetExecutionService(language);

        // Assert
        Assert.IsType(expectedType, service);
    }

    [Theory]
    [InlineData("Go")]
    [InlineData("Rust")]
    [InlineData("JavaScript")]
    [InlineData("")]
    [InlineData("   ")]
    public void GetExecutionService_UnsupportedLanguage_ThrowsNotSupportedException(string language)
    {
        // Act & Assert
        Assert.Throws<NotSupportedException>(() => _factory.GetExecutionService(language));
    }

    [Fact]
    public void GetExecutionService_NullLanguage_ThrowsException()
    {
        // Act & Assert
        Assert.Throws<NullReferenceException>(() => _factory.GetExecutionService(null!));
    }
}
