namespace LiquidCode.Tester.Worker.Services;

/// <summary>
/// Service for generating answer files by running the main solution from Polygon package
/// </summary>
public class AnswerGenerationService
{
    private readonly ICompilationServiceFactory _compilationFactory;
    private readonly IExecutionServiceFactory _executionFactory;
    private readonly ILogger<AnswerGenerationService> _logger;

    public AnswerGenerationService(
        ICompilationServiceFactory compilationFactory,
        IExecutionServiceFactory executionFactory,
        ILogger<AnswerGenerationService> logger)
    {
        _compilationFactory = compilationFactory;
        _executionFactory = executionFactory;
        _logger = logger;
    }

    public async Task<bool> GenerateAnswersAsync(
        PolygonProblemDescriptor descriptor,
        string workingDirectory,
        List<string> inputFilePaths,
        List<string> answerFilePaths)
    {
        if (string.IsNullOrEmpty(descriptor.MainSolutionPath))
        {
            _logger.LogWarning("No main solution specified, cannot generate answers");
            return false;
        }

        var solutionPath = Path.Combine(workingDirectory, descriptor.MainSolutionPath);
        if (!File.Exists(solutionPath))
        {
            _logger.LogWarning("Main solution file not found: {Path}", solutionPath);
            return false;
        }

        // Determine language and version from solution type
        var (language, version) = ParseSolutionType(descriptor.MainSolutionType ?? "");
        if (language == null)
        {
            _logger.LogWarning("Unsupported solution type: {Type}", descriptor.MainSolutionType);
            return false;
        }

        _logger.LogInformation("Generating answers using {Language} {Version} solution: {Path}",
            language, version, descriptor.MainSolutionPath);

        try
        {
            // Read solution source code
            var sourceCode = await File.ReadAllTextAsync(solutionPath);

            // Get compilation service
            var compilationService = _compilationFactory.GetCompilationService(language);
            var executionService = _executionFactory.GetExecutionService(language);

            // Compile solution
            _logger.LogInformation("Compiling main solution...");
            var compilationResult = await compilationService.CompileAsync(
                sourceCode,
                Path.GetDirectoryName(solutionPath)!,
                version);

            if (!compilationResult.Success)
            {
                _logger.LogError("Failed to compile main solution: {Error}", compilationResult.CompilerOutput);
                return false;
            }

            _logger.LogInformation("Main solution compiled successfully");

            // Generate answers for each test
            int generatedCount = 0;
            for (int i = 0; i < inputFilePaths.Count; i++)
            {
                var inputPath = inputFilePaths[i];
                var answerPath = answerFilePaths[i];

                if (!File.Exists(inputPath))
                {
                    _logger.LogWarning("Input file not found: {Path}", inputPath);
                    continue;
                }

                _logger.LogDebug("Generating answer {Index}/{Total}: {AnswerPath}",
                    i + 1, inputFilePaths.Count, answerPath);

                // Execute solution with input
                var executionResult = await executionService.ExecuteAsync(
                    compilationResult.ExecutablePath!,
                    inputPath,
                    descriptor.TimeLimitMs * 2, // Give extra time for answer generation
                    descriptor.MemoryLimitMb * 2);

                if (!executionResult.Success || executionResult.RuntimeError)
                {
                    _logger.LogWarning("Failed to generate answer for {InputPath}: {Error}",
                        inputPath, executionResult.ErrorMessage);
                    continue;
                }

                // Save output as answer file
                var answerDir = Path.GetDirectoryName(answerPath);
                if (!string.IsNullOrEmpty(answerDir) && !Directory.Exists(answerDir))
                {
                    Directory.CreateDirectory(answerDir);
                }

                await File.WriteAllTextAsync(answerPath, executionResult.Output);
                generatedCount++;

                _logger.LogDebug("Generated answer {Index}/{Total}", i + 1, inputFilePaths.Count);
            }

            _logger.LogInformation("Generated {Count} answer files out of {Total} tests",
                generatedCount, inputFilePaths.Count);

            return generatedCount > 0;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error generating answers");
            return false;
        }
    }

    private (string? language, string version) ParseSolutionType(string solutionType)
    {
        // Polygon solution types: python.2, python.3, cpp.g++17, cpp.g++20, java7, java8, etc.
        if (string.IsNullOrEmpty(solutionType))
        {
            return (null, "");
        }

        if (solutionType.StartsWith("python"))
        {
            var versionPart = solutionType.Replace("python", string.Empty, StringComparison.OrdinalIgnoreCase)
                .Trim('.', ' ');

            if (string.IsNullOrWhiteSpace(versionPart))
            {
                return ("python", "3");
            }

            // Normalize python version; Polygon often uses python.3 or python3.10
            versionPart = versionPart.TrimStart('.');

            if (!versionPart.Contains('.'))
            {
                // Assume major version provided, default to CPython minor 10
                versionPart = versionPart switch
                {
                    "2" => "2.7",
                    "3" => "3.10",
                    _ => $"3.{versionPart}"
                };
            }

            return ("python", versionPart);
        }

        if (solutionType.StartsWith("cpp."))
        {
            var standard = ExtractCppStandard(solutionType);
            return ("cpp", standard);
        }

        if (solutionType.StartsWith("java"))
        {
            // java7, java8, java11
            if (solutionType.Contains("11"))
                return ("java", "11");
            if (solutionType.Contains("8"))
                return ("java", "8");
            return ("java", "11"); // Default to Java 11
        }

        if (solutionType.StartsWith("csharp"))
        {
            return ("csharp", "9"); // Default to C# 9
        }

        if (solutionType.StartsWith("kotlin"))
        {
            return ("kotlin", "1.9"); // Default to Kotlin 1.9
        }

        _logger.LogWarning("Unknown solution type: {Type}", solutionType);
        return (null, "");
    }

    private static string ExtractCppStandard(string solutionType)
    {
        var knownStandards = new[] { "26", "23", "20", "17", "14", "11", "03", "98" };

        foreach (var standard in knownStandards)
        {
            if (solutionType.Contains($"++{standard}", StringComparison.OrdinalIgnoreCase) ||
                solutionType.Contains($"c++{standard}", StringComparison.OrdinalIgnoreCase))
            {
                // Normalize 03 to 03, 98 stays 98
                return standard.TrimStart('0');
            }
        }

        // Default to modern standard if not specified
        return "17";
    }
}
