using System;
using System.Collections.Generic;
using System.Globalization;
using NodePipeline.Abstractions.Interfaces;
using NodePipeline.Constants.Exceptions;
using NodePipeline.Constants.ValidationResults;

namespace NodePipeline.Abstractions;

public class PipelineLocalizationProvider : IPipelineLocalizationProvider
{
    private readonly Dictionary<string, Dictionary<string, string>> _messages = new(StringComparer.OrdinalIgnoreCase);

    private readonly Dictionary<string, string> _overrides = new();
    private readonly bool _returnParams = true;

    public PipelineLocalizationProvider(bool? returnParams = null)
    {
        if (returnParams.HasValue) _returnParams = returnParams.Value;
        InitDefaultDictionaries();
    }

    public string GetLocalizedString(string key, CultureInfo culture, params object[] args)
    {
        var locale = culture.TwoLetterISOLanguageName;

        if (_overrides.TryGetValue($"{locale}:{key}", out var msg)) return string.Format(culture, msg, args);

        if (_messages.TryGetValue(locale, out var dict) && dict.TryGetValue(key, out var def))
            return TryGetFormattedString(key, culture, def, _returnParams, args);

        return GetKey(key, _returnParams, args);
    }

    private void InitDefaultDictionaries()
    {
        _messages["en"] = Merge(NodeFactory.MessagesEn,
            NodeValidation.MessagesEn,
            PipelineRegistry.MessagesEn,
            PipelineValidation.MessagesEn,
            ErrorMessages.MessagesEn);
        _messages["ru"] = Merge(NodeFactory.MessagesRu,
            NodeValidation.MessagesRu,
            PipelineRegistry.MessagesRu,
            PipelineValidation.MessagesRu,
            ErrorMessages.MessagesRu);
    }

    public void RegisterOverride(string key, string message, string locale)
    {
        _overrides[$"{locale}:{key}"] = message;
    }

    public void RegisterLocalization(Dictionary<string, string> localization, string locale)
    {
        foreach (var kv in localization)
            _overrides[$"{locale}:{kv.Key}"] = kv.Value;
    }

    private static string TryGetFormattedString(string key, CultureInfo culture, string template, bool returnParams,
        params object[] args)
    {
        try
        {
            return string.Format(culture, template, args);
        }
        catch (FormatException ex)
        {
            return string.Concat("Invalid template format: ", template, "\r\n", ex.Message, "\r\n",
                GetKey(key, returnParams, args));
        }
    }

    private static string GetKey(string key, bool returnParams, params object[] args)
    {
        return !returnParams ? key : string.Concat(key, "; [", string.Join("], [", args), "]");
    }

    private static Dictionary<string, string> Merge(params Dictionary<string, string>[] dictionaries)
    {
        var result = new Dictionary<string, string>();
        foreach (var d in dictionaries)
        foreach (var kv in d)
            result[kv.Key] = kv.Value;
        return result;
    }
}