using System.Net.Http.Headers;
using LiquidCode.Tester.Common.Models;

namespace LiquidCode.Tester.Gateway.Services;

public class WorkerClientService : IWorkerClientService
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly ILogger<WorkerClientService> _logger;
    private readonly IConfiguration _configuration;

    public WorkerClientService(
        IHttpClientFactory httpClientFactory,
        ILogger<WorkerClientService> logger,
        IConfiguration configuration)
    {
        _httpClientFactory = httpClientFactory;
        _logger = logger;
        _configuration = configuration;
    }

    public async Task SendToWorkerAsync(SubmitForTesterModel submit, string packagePath, bool deletePackageAfterSend)
    {
        var workerUrl = GetWorkerUrlForLanguage(submit.Language);
        _logger.LogInformation("Sending submit {SubmitId} to worker at {WorkerUrl}", submit.Id, workerUrl);

        try
        {
            var httpClient = _httpClientFactory.CreateClient();

            using var form = new MultipartFormDataContent();

            // Add submit metadata
            form.Add(new StringContent(submit.Id.ToString()), "Id");
            form.Add(new StringContent(submit.MissionId.ToString()), "MissionId");
            form.Add(new StringContent(submit.Language), "Language");
            form.Add(new StringContent(submit.LanguageVersion), "LanguageVersion");
            form.Add(new StringContent(submit.SourceCode, System.Text.Encoding.UTF8, "text/plain"), "SourceCode");
            form.Add(new StringContent(submit.CallbackUrl), "CallbackUrl");

            // Add package file
            var fileStream = File.OpenRead(packagePath);
            var fileContent = new StreamContent(fileStream);
            fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
            form.Add(fileContent, "Package", Path.GetFileName(packagePath));

            var response = await httpClient.PostAsync($"{workerUrl}/api/test", form);
            response.EnsureSuccessStatusCode();

            _logger.LogInformation("Submit {SubmitId} sent successfully to worker", submit.Id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send submit {SubmitId} to worker", submit.Id);
            throw;
        }
        finally
        {
            if (deletePackageAfterSend)
            {
                // Clean up package file when it is not needed anymore
                try
                {
                    if (File.Exists(packagePath))
                    {
                        File.Delete(packagePath);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Failed to delete package file {Path}", packagePath);
                }
            }
        }
    }

    private string GetWorkerUrlForLanguage(string language)
    {
        var workerUrl = language.ToLowerInvariant() switch
        {
            "c++" or "cpp" => _configuration["Workers:Cpp"],
            "java" => _configuration["Workers:Java"],
            "kotlin" => _configuration["Workers:Kotlin"],
            "c#" or "csharp" => _configuration["Workers:CSharp"],
            "python" => _configuration["Workers:Python"],
            _ => throw new NotSupportedException($"Language {language} is not supported")
        };

        if (string.IsNullOrEmpty(workerUrl))
        {
            throw new InvalidOperationException($"Worker URL for language {language} is not configured");
        }

        return workerUrl;
    }
}
