using System.IO.Compression;
using LiquidCode.Tester.Common.Models;
using LiquidCode.Tester.Worker.Controllers;
using LiquidCode.Tester.Worker.Services;
using Microsoft.Extensions.Logging;
using Moq;

namespace LiquidCode.Tester.Worker.Tests;

public class TestingServiceTests : IDisposable
{
    private readonly Mock<IPackageParserService> _packageParserMock;
    private readonly Mock<ICompilationServiceFactory> _compilationFactoryMock;
    private readonly Mock<IExecutionServiceFactory> _executionFactoryMock;
    private readonly Mock<IOutputCheckerService> _outputCheckerMock;
    private readonly Mock<ICallbackService> _callbackServiceMock;
    private readonly Mock<ILogger<TestingService>> _loggerMock;
    private readonly TestingService _service;
    private readonly string _testDirectory;

    public TestingServiceTests()
    {
        _packageParserMock = new Mock<IPackageParserService>();
        _compilationFactoryMock = new Mock<ICompilationServiceFactory>();
        _executionFactoryMock = new Mock<IExecutionServiceFactory>();
        _outputCheckerMock = new Mock<IOutputCheckerService>();
        _callbackServiceMock = new Mock<ICallbackService>();
        _loggerMock = new Mock<ILogger<TestingService>>();

        _service = new TestingService(
            _packageParserMock.Object,
            _compilationFactoryMock.Object,
            _executionFactoryMock.Object,
            _outputCheckerMock.Object,
            _callbackServiceMock.Object,
            _loggerMock.Object
        );

        _testDirectory = Path.Combine(Path.GetTempPath(), "TestingServiceTests", Guid.NewGuid().ToString());
        Directory.CreateDirectory(_testDirectory);
    }

    [Fact]
    public async Task ProcessSubmitAsync_EmptyPackage_ReturnsUnknownError()
    {
        // Arrange
        var packageFilePath = Path.Combine(_testDirectory, "empty_package.zip");
        await CreateEmptyPackage(packageFilePath);

        var request = new TestRequest
        {
            Id = 123,
            MissionId = 456,
            Language = "cpp",
            LanguageVersion = "17",
            SourceCode = "int main() { return 0; }",
            PackageFilePath = packageFilePath,
            CallbackUrl = "http://localhost/callback"
        };

        var emptyPackage = new ProblemPackage
        {
            WorkingDirectory = _testDirectory,
            ExtractionRoot = _testDirectory,
            TestCases = new List<TestCase>() // Empty list!
        };

        _packageParserMock
            .Setup(x => x.ParsePackageAsync(It.IsAny<Stream>()))
            .ReturnsAsync(emptyPackage);

        // Act
        await _service.ProcessSubmitAsync(request);

        // Assert - verify callback was called with error
        _callbackServiceMock.Verify(
            x => x.SendStatusAsync(
                request.CallbackUrl,
                It.Is<TesterResponseModel>(r =>
                    r.State == State.Done &&
                    r.ErrorCode == ErrorCode.UnknownError &&
                    r.Message == "No test cases found in package"
                )
            ),
            Times.Once
        );

        // Verify compilation was NOT attempted
        _compilationFactoryMock.Verify(
            x => x.GetCompilationService(It.IsAny<string>()),
            Times.Never
        );
    }

    [Fact]
    public async Task ProcessSubmitAsync_ValidPackage_RunsAllTests()
    {
        // Arrange
        var packageFilePath = Path.Combine(_testDirectory, "valid_package.zip");
        var inputFile = Path.Combine(_testDirectory, "1.in");
        var outputFile = Path.Combine(_testDirectory, "1.out");
        var executablePath = Path.Combine(_testDirectory, "solution.exe");

        await File.WriteAllTextAsync(inputFile, "test input");
        await File.WriteAllTextAsync(outputFile, "expected output");
        await File.WriteAllTextAsync(executablePath, "dummy");
    await CreateEmptyPackage(packageFilePath);

        var request = new TestRequest
        {
            Id = 123,
            MissionId = 456,
            Language = "cpp",
            LanguageVersion = "17",
            SourceCode = "int main() { return 0; }",
            PackageFilePath = packageFilePath,
            CallbackUrl = "http://localhost/callback"
        };

        var package = new ProblemPackage
        {
            WorkingDirectory = _testDirectory,
            ExtractionRoot = _testDirectory,
            TestCases = new List<TestCase>
            {
                new TestCase
                {
                    Number = 1,
                    InputFilePath = inputFile,
                    OutputFilePath = outputFile,
                    TimeLimit = 2000,
                    MemoryLimit = 256
                }
            }
        };

        var compilationService = new Mock<ICompilationService>();
        var executionService = new Mock<IExecutionService>();

        _packageParserMock
            .Setup(x => x.ParsePackageAsync(It.IsAny<Stream>()))
            .ReturnsAsync(package);

        _compilationFactoryMock
            .Setup(x => x.GetCompilationService("cpp"))
            .Returns(compilationService.Object);

        _executionFactoryMock
            .Setup(x => x.GetExecutionService("cpp"))
            .Returns(executionService.Object);

        compilationService
            .Setup(x => x.CompileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
            .ReturnsAsync(new CompilationResult
            {
                Success = true,
                ExecutablePath = executablePath
            });

        executionService
            .Setup(x => x.ExecuteAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
            .ReturnsAsync(new ExecutionResult
            {
                Success = true,
                Output = "expected output",
                ExitCode = 0,
                RuntimeError = false,
                TimeLimitExceeded = false,
                MemoryLimitExceeded = false
            });

        _outputCheckerMock
            .Setup(x => x.CheckOutputWithCheckerAsync(
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<string?>()))
            .ReturnsAsync(true);

        // Act
        await _service.ProcessSubmitAsync(request);

        // Assert - verify callback was called with success
        _callbackServiceMock.Verify(
            x => x.SendStatusAsync(
                request.CallbackUrl,
                It.Is<TesterResponseModel>(r =>
                    r.State == State.Done &&
                    r.ErrorCode == ErrorCode.None &&
                    r.Message == "All tests passed"
                )
            ),
            Times.AtLeastOnce
        );
    }

    private async Task CreateEmptyPackage(string filePath)
    {
        using var fileStream = File.Create(filePath);
        using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create);
        // Create empty archive
    }

    public void Dispose()
    {
        if (Directory.Exists(_testDirectory))
        {
            try
            {
                Directory.Delete(_testDirectory, true);
            }
            catch
            {
                // Ignore cleanup errors in tests
            }
        }
    }
}
