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 CompilationServiceFactoryTests
{
    private readonly IServiceProvider _serviceProvider;
    private readonly CompilationServiceFactory _factory;

    public CompilationServiceFactoryTests()
    {
        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 compilation services
        services.AddSingleton<CppCompilationService>();
        services.AddSingleton<JavaCompilationService>();
        services.AddSingleton<KotlinCompilationService>();
        services.AddSingleton<CSharpCompilationService>();
        services.AddSingleton<PythonCompilationService>();

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

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

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

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

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

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

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

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

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

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

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

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

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