using System.Diagnostics;

namespace LiquidCode.Tester.Worker.Services;

/// <summary>
/// Service for running custom checkers (testlib-based)
/// </summary>
public class CheckerService
{
    private readonly ILogger<CheckerService> _logger;

    public CheckerService(ILogger<CheckerService> logger)
    {
        _logger = logger;
    }

    /// <summary>
    /// Check user output using custom checker
    /// </summary>
    /// <param name="checkerPath">Path to checker executable</param>
    /// <param name="inputPath">Path to input file</param>
    /// <param name="userOutput">User program output</param>
    /// <param name="answerPath">Path to answer file</param>
    /// <returns>Checker result</returns>
    public async Task<CheckerResult> CheckAsync(
        string checkerPath,
        string inputPath,
        string userOutput,
        string answerPath)
    {
        if (!File.Exists(checkerPath))
        {
            _logger.LogError("Checker not found: {CheckerPath}", checkerPath);
            return new CheckerResult
            {
                Accepted = false,
                ExitCode = -1,
                Message = "Checker executable not found"
            };
        }

        // Save user output to temporary file
        var tempOutputPath = Path.Combine(Path.GetTempPath(), $"user_output_{Guid.NewGuid()}.txt");

        try
        {
            await File.WriteAllTextAsync(tempOutputPath, userOutput);

            _logger.LogDebug("Running checker: {Checker} {Input} {Output} {Answer}",
                checkerPath, inputPath, tempOutputPath, answerPath);

            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = checkerPath,
                    Arguments = $"\"{inputPath}\" \"{tempOutputPath}\" \"{answerPath}\"",
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                }
            };

            process.Start();

            var stdout = await process.StandardOutput.ReadToEndAsync();
            var stderr = await process.StandardError.ReadToEndAsync();

            // Wait with timeout (checkers should be fast)
            var completed = await Task.Run(() => process.WaitForExit(5000));

            if (!completed)
            {
                _logger.LogWarning("Checker timeout, killing process");
                try
                {
                    process.Kill(entireProcessTree: true);
                }
                catch { }

                return new CheckerResult
                {
                    Accepted = false,
                    ExitCode = -1,
                    Message = "Checker timeout"
                };
            }

            var exitCode = process.ExitCode;

            _logger.LogDebug("Checker exit code: {ExitCode}, stderr: {Stderr}", exitCode, stderr);

            return new CheckerResult
            {
                Accepted = exitCode == 0,
                ExitCode = exitCode,
                Message = string.IsNullOrWhiteSpace(stderr) ? stdout : stderr,
                Verdict = GetVerdictFromExitCode(exitCode)
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error running checker");
            return new CheckerResult
            {
                Accepted = false,
                ExitCode = -1,
                Message = $"Checker error: {ex.Message}"
            };
        }
        finally
        {
            // Cleanup temporary file
            try
            {
                if (File.Exists(tempOutputPath))
                {
                    File.Delete(tempOutputPath);
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Failed to delete temporary output file: {Path}", tempOutputPath);
            }
        }
    }

    private CheckerVerdict GetVerdictFromExitCode(int exitCode)
    {
        return exitCode switch
        {
            0 => CheckerVerdict.OK,
            1 => CheckerVerdict.WrongAnswer,
            2 => CheckerVerdict.PresentationError,
            3 => CheckerVerdict.CheckerFailed,
            7 => CheckerVerdict.PartialScore,
            _ => CheckerVerdict.Unknown
        };
    }
}

/// <summary>
/// Result of custom checker execution
/// </summary>
public class CheckerResult
{
    public bool Accepted { get; set; }
    public int ExitCode { get; set; }
    public string Message { get; set; } = string.Empty;
    public CheckerVerdict Verdict { get; set; }
}

/// <summary>
/// Checker verdict codes (testlib standard)
/// </summary>
public enum CheckerVerdict
{
    OK = 0,              // Accepted
    WrongAnswer = 1,     // Wrong Answer
    PresentationError = 2, // Presentation Error (format issue)
    CheckerFailed = 3,   // Internal checker error
    PartialScore = 7,    // Partial score (for IOI-style)
    Unknown = -1         // Unknown exit code
}
