using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StellaOps.Feedser.Core.Jobs;

namespace StellaOps.Feedser.Exporter.TrivyDb;

public sealed class TrivyDbExportJob : IJob
{
    public const string JobKind = "export:trivy-db";
    public static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(20);
    public static readonly TimeSpan DefaultLeaseDuration = TimeSpan.FromMinutes(10);

    private readonly TrivyDbFeedExporter _exporter;
    private readonly ILogger<TrivyDbExportJob> _logger;

    public TrivyDbExportJob(TrivyDbFeedExporter exporter, ILogger<TrivyDbExportJob> logger)
    {
        _exporter = exporter ?? throw new ArgumentNullException(nameof(exporter));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task ExecuteAsync(JobExecutionContext context, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Executing Trivy DB export job {RunId}", context.RunId);
        var overrides = CreateOverrides(context.Parameters);
        if (overrides?.HasOverrides == true)
        {
            using var scope = TrivyDbExportOverrideScope.Begin(overrides);
            await _exporter.ExportAsync(context.Services, cancellationToken).ConfigureAwait(false);
        }
        else
        {
            await _exporter.ExportAsync(context.Services, cancellationToken).ConfigureAwait(false);
        }

        _logger.LogInformation("Completed Trivy DB export job {RunId}", context.RunId);
    }

    private static TrivyDbExportOverrides? CreateOverrides(IReadOnlyDictionary<string, object?> parameters)
    {
        if (parameters is null || parameters.Count == 0)
        {
            return null;
        }

        var publishFull = TryReadBoolean(parameters, "publishFull");
        var publishDelta = TryReadBoolean(parameters, "publishDelta");
        var includeFull = TryReadBoolean(parameters, "includeFull");
        var includeDelta = TryReadBoolean(parameters, "includeDelta");

        var overrides = new TrivyDbExportOverrides(publishFull, publishDelta, includeFull, includeDelta);
        return overrides.HasOverrides ? overrides : null;
    }

    private static bool? TryReadBoolean(IReadOnlyDictionary<string, object?> parameters, string key)
    {
        if (!parameters.TryGetValue(key, out var value) || value is null)
        {
            return null;
        }

        switch (value)
        {
            case bool b:
                return b;
            case string s when bool.TryParse(s, out var result):
                return result;
            case JsonElement element:
                return element.ValueKind switch
                {
                    JsonValueKind.True => true,
                    JsonValueKind.False => false,
                    JsonValueKind.String when bool.TryParse(element.GetString(), out var parsed) => parsed,
                    _ => null,
                };
            case IConvertible convertible:
                try
                {
                    return convertible.ToBoolean(CultureInfo.InvariantCulture);
                }
                catch
                {
                    return null;
                }
        }

        return null;
    }
}
