using System.Globalization;
using NodePipeline.Abstractions;
using NodePipeline.Abstractions.Exceptions;
using NodePipeline.Abstractions.Interfaces;
using NodePipeline.Abstractions.Models.Validation;
using NodePipeline.Configuration.Yaml.Loader;
using NodePipeline.Engine.Abstractions;
using NodePipeline.Engine.Execution;

namespace NodePipeline.ConsoleApp;

public class PipelineRegistrator
{
    private readonly CultureInfo _cultureInfo;
    private readonly INodeFactory _nodeFactory;
    private readonly IPipelineNodeValidator _nodeValidator;
    private readonly PipelineRegistry _registry;
    private readonly IPipelineLocalizationProvider _validationLocalizationProvider;

    public PipelineRegistrator(PipelineRegistry registry, INodeFactory nodeFactory,
        IPipelineNodeValidator nodeValidator,
        IPipelineLocalizationProvider? pipelineLocalizationProvider = null)
    {
        _registry = registry;
        _nodeFactory = nodeFactory;
        _nodeValidator = nodeValidator;
        // _cultureInfo = cultureInfo ?? CultureInfo.CurrentCulture;
        _cultureInfo = CultureInfo.CurrentCulture;
        _validationLocalizationProvider = pipelineLocalizationProvider ?? new PipelineLocalizationProvider();
    }

    public string RegisterPipeline(string yamlConfigFilePath)
    {
        using var yaml = File.OpenRead(yamlConfigFilePath);
        var graphLoader = new YamlGraphLoader();
        var config = graphLoader.Load(yaml);

        var pipelineId = config.Name;
        try
        {
            // var registrationResult = _registry.Register(pipelineId, config, treatWarningsAsErrors: false, cultureInfo: _cultureInfo);
            var registrationResult =
                _registry.Register(_nodeFactory, _nodeValidator, pipelineId, config, false, _cultureInfo);
            PrintRegistrationResult(registrationResult, pipelineId);

            return pipelineId;
        }
        catch (PipelineValidationException ex)
        {
            PrintExceptions(ex);
            Environment.Exit(-1);
        }

        return null;
    }

    private void PrintRegistrationResult(PipelineRegistrationResult registrationResult, string pipelineId)
    {
        var color = Console.ForegroundColor;
        Console.ForegroundColor = ConsoleColor.DarkGray;
        var warnings = registrationResult.NodeWarnings;
        Console.ForegroundColor =
            registrationResult.WarningsCount == 0 ? ConsoleColor.DarkGreen : ConsoleColor.DarkYellow;
        Console.WriteLine(registrationResult.WarningsCount == 0
            ? _validationLocalizationProvider.GetLocalizedString("PipelineRegistered", CultureInfo.CurrentCulture,
                pipelineId)
            : _validationLocalizationProvider.GetLocalizedString("PipelineRegisteredWithWarnings",
                CultureInfo.CurrentCulture, pipelineId, registrationResult.WarningsCount));
        //TODO: add messages
        //TODO: add method GetMessages to exceptions

        if (registrationResult.WarningsCount > 0)
        {
            Console.WriteLine("Nodes:");

            foreach (var nodeKvp in warnings.OrderBy(q => q.Key))
            {
                Console.ForegroundColor = ConsoleColor.DarkYellow;
                Console.WriteLine($"\t[{nodeKvp.Key}]");
                Console.ForegroundColor = color;

                var warningParameters = nodeKvp.Value.ParameterResults
                    .Where(q => q.Value.Any(x => x.Result == ValidationResult.HasWarnings))
                    .ToDictionary();
                var warningPorts = nodeKvp.Value.PortResults
                    .Where(q => q.Value.Any(x => x.Result == ValidationResult.HasWarnings))
                    .ToDictionary();

                if (warningParameters.Count > 0)
                {
                    Console.WriteLine("\t\tParameters:");
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    foreach (var parameter in warningParameters.OrderBy(q => q.Key))
                        // foreach (var warning in parameter.Value.OrderBy(q => q.ErrorMessage))
                    foreach (var warning in parameter.Value)
                        Console.WriteLine(string.Concat("\t\t[WARN] ", parameter.Key, ": ", warning.ErrorMessage));
                }

                if (warningPorts.Count > 0)
                {
                    Console.WriteLine("\t\tPorts:");
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    foreach (var parameter in warningPorts.OrderBy(q => q.Key))
                    foreach (var warning in parameter.Value)
                        Console.WriteLine(string.Concat("\t\t[WARN] ", parameter.Key, ": ", warning.ErrorMessage));
                }
            }
        }

        Console.ForegroundColor = color;
    }

    private void PrintExceptions(PipelineValidationException ex)
    {
        var color = Console.ForegroundColor;
        Console.ForegroundColor = ConsoleColor.DarkGray;
        Console.WriteLine(_validationLocalizationProvider.GetLocalizedString(ex.Message, CultureInfo.CurrentCulture,
            ex.PipelineId, ex.RegistrationResult.ErrorsCount, ex.RegistrationResult.WarningsCount));
        Console.ForegroundColor = ConsoleColor.DarkYellow;
        Console.Write($"Warnings: [{ex.RegistrationResult.WarningsCount}] | ");
        Console.ForegroundColor = ConsoleColor.DarkRed;
        Console.WriteLine($"Errors: [{ex.RegistrationResult.ErrorsCount}]");
        Console.ForegroundColor = color;
        Console.WriteLine(new string('-', 30));
        Console.WriteLine("Nodes:");

        foreach (var nodeKvp in ex.RegistrationResult.GetNotValidNodes().OrderBy(q => q.Key))
        {
            Console.ForegroundColor = nodeKvp.Value.Result == ValidationResult.HasWarnings
                ? ConsoleColor.DarkYellow
                : ConsoleColor.DarkRed;
            Console.WriteLine($"\t[{nodeKvp.Key}]");
            Console.ForegroundColor = color;

            // var warningParameters = ex.WarningParameters.TryGetValue(nodeKvp.Key, out var value);
            // var errorParameters = ex.ErrorParameters.TryGetValue(nodeKvp.Key);
            // var warningPorts = ex.WarningPorts[nodeKvp.Key];
            // var errorPorts = ex.ErrorPorts[nodeKvp.Key];

            if ((ex.RegistrationResult.WarningParameters.TryGetValue(nodeKvp.Key, out var warningParameters) &&
                 warningParameters.Count > 0)
                | (ex.RegistrationResult.ErrorParameters.TryGetValue(nodeKvp.Key, out var errorParameters) &&
                   errorParameters.Count > 0))
            {
                Console.WriteLine("\t\tParameters:");
                if (warningParameters?.Count > 0)
                {
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    foreach (var parameter in warningParameters.OrderBy(q => q.Key))
                        // foreach (var warning in parameter.Value.OrderBy(q => q.ErrorMessage))
                    foreach (var warning in parameter.Value)
                        Console.WriteLine(string.Concat("\t\t[WARN] ", parameter.Key, ": ", warning.ErrorMessage));
                }

                if (errorParameters?.Count > 0)
                {
                    Console.ForegroundColor = ConsoleColor.DarkRed;
                    foreach (var parameter in errorParameters.OrderBy(q => q.Key))
                        // foreach (var error in parameter.Value.OrderBy(q => q.ErrorMessage))
                    foreach (var error in parameter.Value)
                        Console.WriteLine(string.Concat("\t\t[ERRO] ", parameter.Key, ": ", error.ErrorMessage));
                }
            }

            if ((ex.RegistrationResult.WarningPorts.TryGetValue(nodeKvp.Key, out var warningPorts) &&
                 warningPorts.Count > 0)
                | (ex.RegistrationResult.ErrorPorts.TryGetValue(nodeKvp.Key, out var errorPorts) &&
                   errorPorts.Count > 0))
            {
                Console.WriteLine("\t\tPorts:");
                if (warningPorts?.Count > 0)
                {
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    foreach (var port in warningPorts.OrderBy(q => q.Key))
                        // foreach (var warning in parameter.Value.OrderBy(q => q.ErrorMessage))
                    foreach (var warning in port.Value)
                        Console.WriteLine(string.Concat("\t\t[WARN] ", port.Key, ": ", warning.ErrorMessage));
                }

                if (errorPorts?.Count > 0)
                {
                    Console.ForegroundColor = ConsoleColor.DarkRed;
                    foreach (var port in errorPorts.OrderBy(q => q.Key))
                        // foreach (var error in parameter.Value.OrderBy(q => q.ErrorMessage))
                    foreach (var error in port.Value)
                        Console.WriteLine(string.Concat("\t\t[ERRO] ", port.Key, ": ", error.ErrorMessage));
                }
            }
        }

        Console.ForegroundColor = color;
    }
}