using Microsoft.Extensions.Logging;
using OTSSignsOrchestrator.Core.Configuration;
using YamlDotNet.RepresentationModel;

namespace OTSSignsOrchestrator.Core.Services;

/// <summary>
/// Validates a rendered Compose YAML before deployment.
/// </summary>
public class ComposeValidationService
{
    private readonly ILogger<ComposeValidationService> _logger;

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

    public ValidationResult Validate(string composeYaml, string? customerAbbrev = null)
    {
        var errors = new List<string>();
        var warnings = new List<string>();

        YamlStream yamlStream;
        try
        {
            yamlStream = new YamlStream();
            using var reader = new StringReader(composeYaml);
            yamlStream.Load(reader);
        }
        catch (Exception ex)
        {
            errors.Add($"YAML parse error: {ex.Message}");
            return new ValidationResult { Errors = errors, Warnings = warnings };
        }

        if (yamlStream.Documents.Count == 0)
        {
            errors.Add("YAML document is empty.");
            return new ValidationResult { Errors = errors, Warnings = warnings };
        }

        var root = yamlStream.Documents[0].RootNode as YamlMappingNode;
        if (root == null)
        {
            errors.Add("YAML root is not a mapping node.");
            return new ValidationResult { Errors = errors, Warnings = warnings };
        }

        if (!HasKey(root, "services"))
            errors.Add("Missing required top-level key: 'services'.");
        if (!HasKey(root, "secrets"))
            warnings.Add("Missing top-level key: 'secrets'. Secrets may not be available.");

        if (HasKey(root, "services") && root.Children[new YamlScalarNode("services")] is YamlMappingNode services)
        {
            var presentServices = services.Children.Keys
                .OfType<YamlScalarNode>()
                .Select(k => k.Value!)
                .ToHashSet(StringComparer.OrdinalIgnoreCase);

            // Determine required service suffixes
            var requiredSuffixes = new[] { "-web", "-memcached", "-quickchart" };
            var prefix = customerAbbrev ?? string.Empty;

            if (!string.IsNullOrEmpty(prefix))
            {
                foreach (var suffix in requiredSuffixes)
                {
                    if (!presentServices.Contains($"{prefix}{suffix}"))
                        errors.Add($"Missing required service: '{prefix}{suffix}'.");
                }
            }
            else
            {
                // Fallback: at least check that there are web/memcached/quickchart services
                if (!presentServices.Any(s => s.EndsWith("-web", StringComparison.OrdinalIgnoreCase)))
                    errors.Add("Missing a '-web' service.");
                if (!presentServices.Any(s => s.EndsWith("-memcached", StringComparison.OrdinalIgnoreCase)))
                    errors.Add("Missing a '-memcached' service.");
            }

            foreach (var (key, value) in services.Children)
            {
                if (key is YamlScalarNode keyNode && value is YamlMappingNode svcNode)
                {
                    if (!HasKey(svcNode, "image"))
                        errors.Add($"Service '{keyNode.Value}' is missing 'image'.");
                }
            }
        }

        if (HasKey(root, "secrets") && root.Children[new YamlScalarNode("secrets")] is YamlMappingNode secretsNode)
        {
            var presentSecrets = secretsNode.Children.Keys
                .OfType<YamlScalarNode>()
                .Select(k => k.Value!)
                .ToHashSet(StringComparer.OrdinalIgnoreCase);

            foreach (var (key, value) in secretsNode.Children)
            {
                if (value is YamlMappingNode secretNode)
                {
                    if (!HasKey(secretNode, "external"))
                        warnings.Add($"Secret '{(key as YamlScalarNode)?.Value}' is not external.");
                }
            }

            // Validate that all required MySQL secrets are declared
            if (!string.IsNullOrEmpty(customerAbbrev))
            {
                var requiredSecrets = new[]
                {
                    AppConstants.CustomerMysqlUserSecretName(customerAbbrev),
                    AppConstants.GlobalMysqlHostSecretName,
                    AppConstants.GlobalMysqlPortSecretName,
                };
                foreach (var required in requiredSecrets)
                {
                    if (!presentSecrets.Contains(required))
                        errors.Add($"Missing required secret: '{required}'.");
                }
            }
        }

        return new ValidationResult { Errors = errors, Warnings = warnings };
    }

    private static bool HasKey(YamlMappingNode node, string key)
        => node.Children.ContainsKey(new YamlScalarNode(key));
}

public class ValidationResult
{
    public List<string> Errors { get; set; } = new();
    public List<string> Warnings { get; set; } = new();
    public bool IsValid => Errors.Count == 0;
}
