using System.Diagnostics;
using LiquidCode.Tester.Worker.Services.Isolate;

namespace LiquidCode.Tester.Worker.Services;

/// <summary>
/// C++ program execution service using Isolate sandbox
/// </summary>
public class CppExecutionServiceIsolate : IExecutionService
{
    private readonly ILogger<CppExecutionServiceIsolate> _logger;
    private readonly IsolateService _isolateService;
    private readonly IsolateBoxPool _boxPool;

    public CppExecutionServiceIsolate(
        ILogger<CppExecutionServiceIsolate> logger,
        IsolateService isolateService,
        IsolateBoxPool boxPool)
    {
        _logger = logger;
        _isolateService = isolateService;
        _boxPool = boxPool;
    }

    public async Task<ExecutionResult> ExecuteAsync(
        string executablePath,
        string inputFilePath,
        int timeLimitMs,
        int memoryLimitMb)
    {
        _logger.LogInformation(
            "Executing {Executable} with Isolate: time={TimeLimit}ms, memory={MemoryLimit}MB",
            executablePath, timeLimitMs, memoryLimitMb);

        var result = new ExecutionResult();
        var stopwatch = Stopwatch.StartNew();
        int boxId = -1;

        try
        {
            // Acquire a box from pool
            boxId = await _boxPool.AcquireBoxAsync();
            _logger.LogDebug("Acquired isolate box {BoxId}", boxId);

            // Initialize the box
            await _isolateService.InitBoxAsync(boxId);

            // Copy executable to box
            var boxDir = $"/var/local/lib/isolate/{boxId}/box";
            var executableName = Path.GetFileName(executablePath);
            var boxExecutablePath = Path.Combine(boxDir, executableName);

            File.Copy(executablePath, boxExecutablePath, overwrite: true);

            // Make executable
            var chmodProcess = Process.Start(new ProcessStartInfo
            {
                FileName = "chmod",
                Arguments = $"+x {boxExecutablePath}",
                UseShellExecute = false
            });
            chmodProcess?.WaitForExit();

            // Prepare input/output files inside the sandbox
            var outputFilePath = Path.Combine(boxDir, "output.txt");
            string? sandboxInputPath = null;

            if (!string.IsNullOrEmpty(inputFilePath) && File.Exists(inputFilePath))
            {
                sandboxInputPath = Path.Combine(boxDir, "input.txt");
                File.Copy(inputFilePath, sandboxInputPath, overwrite: true);
            }

            // Run in Isolate
            var isolateResult = await _isolateService.RunAsync(new IsolateRunOptions
            {
                BoxId = boxId,
                Executable = $"/box/{executableName}",
                TimeLimitSeconds = timeLimitMs / 1000.0,
                WallTimeLimitSeconds = (timeLimitMs / 1000.0) * 2,
                MemoryLimitKb = memoryLimitMb * 1024,
                StackLimitKb = 256 * 1024, // 256 MB stack
                ProcessLimit = 1, // Single process only
                EnableNetwork = false, // No network access
                StdinFile = sandboxInputPath,
                StdoutFile = outputFilePath,
                WorkingDirectory = "/box"
            });

            stopwatch.Stop();

            // Read output
            if (File.Exists(outputFilePath))
            {
                result.Output = await File.ReadAllTextAsync(outputFilePath);
            }

            // Map Isolate result to ExecutionResult
            result.ExecutionTimeMs = (long)(isolateResult.CpuTimeSeconds * 1000);
            result.MemoryUsedMb = isolateResult.MemoryUsedKb / 1024;
            result.ErrorOutput = isolateResult.ErrorOutput;
            result.ExitCode = isolateResult.ExitCode;

            if (isolateResult.TimeLimitExceeded)
            {
                result.TimeLimitExceeded = true;
                result.ErrorMessage = $"Time limit exceeded: {isolateResult.CpuTimeSeconds:F3}s";
                _logger.LogWarning("Time limit exceeded for box {BoxId}", boxId);
            }
            else if (isolateResult.MemoryLimitExceeded)
            {
                result.MemoryLimitExceeded = true;
                result.ErrorMessage = $"Memory limit exceeded: {isolateResult.MemoryUsedKb / 1024}MB";
                if (isolateResult.CgroupOomKilled)
                {
                    result.ErrorMessage += " (OOM killed by cgroup)";
                }
                _logger.LogWarning("Memory limit exceeded for box {BoxId}", boxId);
            }
            else if (isolateResult.RuntimeError)
            {
                result.RuntimeError = true;
                result.ErrorMessage = $"Runtime error: {isolateResult.Message}";
                if (isolateResult.ExitSignal.HasValue)
                {
                    result.ErrorMessage += $" (signal {isolateResult.ExitSignal})";
                }
                _logger.LogWarning("Runtime error for box {BoxId}: {Message}", boxId, isolateResult.Message);
            }
            else if (isolateResult.ExitCode == 0)
            {
                result.Success = true;
                _logger.LogInformation(
                    "Execution successful: time={Time}ms, memory={Memory}MB, box={BoxId}",
                    result.ExecutionTimeMs, result.MemoryUsedMb, boxId);
            }
            else
            {
                result.RuntimeError = true;
                result.ErrorMessage = $"Non-zero exit code: {isolateResult.ExitCode}";
                _logger.LogWarning("Non-zero exit code {ExitCode} for box {BoxId}",
                    isolateResult.ExitCode, boxId);
            }

            // Log detailed statistics
            _logger.LogDebug(
                "Isolate stats: CPU={Cpu}s, Wall={Wall}s, Memory={Mem}KB, " +
                "VoluntaryContextSwitches={Vol}, ForcedContextSwitches={Forced}",
                isolateResult.CpuTimeSeconds,
                isolateResult.WallTimeSeconds,
                isolateResult.MemoryUsedKb,
                isolateResult.VoluntaryContextSwitches,
                isolateResult.ForcedContextSwitches);
        }
        catch (Exception ex)
        {
            stopwatch.Stop();
            result.ExecutionTimeMs = stopwatch.ElapsedMilliseconds;
            result.RuntimeError = true;
            result.ErrorMessage = $"Execution error: {ex.Message}";
            _logger.LogError(ex, "Error during Isolate execution");
        }
        finally
        {
            // Cleanup box
            if (boxId >= 0)
            {
                try
                {
                    await _isolateService.CleanupBoxAsync(boxId);
                    _logger.LogDebug("Cleaned up isolate box {BoxId}", boxId);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Failed to cleanup box {BoxId}", boxId);
                }

                // Release box back to pool
                _boxPool.ReleaseBox(boxId);
                _logger.LogDebug("Released box {BoxId} back to pool", boxId);
            }
        }

        return result;
    }
}
