using System;
using System.Collections.Generic;
using System.Text;

namespace eagle {
    public sealed class DynamicTextTemplate {
        public string ParsedText { get; }
        public string LiteralSuffix { get; }

        private DynamicTextTemplate(string parsedText, string literalSuffix) {
            ParsedText = parsedText ?? throw new ArgumentNullException(nameof(parsedText));
            LiteralSuffix = literalSuffix ?? "";
        }

        public static DynamicTextTemplate Parsed(string text) {
            return new DynamicTextTemplate(text, "");
        }

        public static string Placeholder(DynamicTextPlaceholderName name) {
            return $"{{{name.Value}}}";
        }

        public static bool TryParsePlaceholderToken(
                string token,
                out DynamicTextPlaceholderName name) {
            name = default;
            if (string.IsNullOrEmpty(token) || token[0] != '{' || token[^1] != '}') {
                return false;
            }

            var placeholderName = token.Substring(1, token.Length - 2);
            if (!DynamicTextPlaceholderName.IsValidName(placeholderName)) { return false; }

            name = new DynamicTextPlaceholderName(placeholderName);
            return true;
        }

        public DynamicTextTemplate WithLiteralSuffix(string suffix) {
            return new DynamicTextTemplate(ParsedText, suffix ?? "");
        }

        public DynamicTextTemplate WithParsedSuffix(string suffix) {
            return new DynamicTextTemplate(
                    ParsedText + (suffix ?? throw new ArgumentNullException(nameof(suffix))),
                    LiteralSuffix);
        }

        public string Render(
                IReadOnlyDictionary<DynamicTextPlaceholderName, string> placeholderValues) {
            if (placeholderValues == null) {
                throw new ArgumentNullException(nameof(placeholderValues));
            }

            var result = new StringBuilder();
            for (int i = 0; i < ParsedText.Length; i++) {
                if (ParsedText[i] != '{') {
                    result.Append(ParsedText[i]);
                    continue;
                }

                var closeBraceIndex = ParsedText.IndexOf('}', i + 1);
                if (closeBraceIndex < 0) {
                    result.Append(ParsedText, i, ParsedText.Length - i);
                    break;
                }

                var token = ParsedText.Substring(i, closeBraceIndex - i + 1);
                if (TryParsePlaceholderToken(token, out var name) &&
                    placeholderValues.TryGetValue(name, out var value)) {
                    if (value == null) {
                        throw new ArgumentException(
                                $"Dynamic text placeholder value cannot be null: {name}",
                                nameof(placeholderValues));
                    }
                    result.Append(value);
                } else {
                    result.Append(token);
                }

                i = closeBraceIndex;
            }

            result.Append(LiteralSuffix);
            return result.ToString();
        }

        public IReadOnlyCollection<DynamicTextPlaceholderName> PlaceholderNames() {
            var names = new List<DynamicTextPlaceholderName>();
            var seenNames = new HashSet<DynamicTextPlaceholderName>();
            for (int i = 0; i < ParsedText.Length; i++) {
                if (ParsedText[i] != '{') { continue; }

                var closeBraceIndex = ParsedText.IndexOf('}', i + 1);
                if (closeBraceIndex < 0) { break; }

                var token = ParsedText.Substring(i, closeBraceIndex - i + 1);
                if (TryParsePlaceholderToken(token, out var name) && seenNames.Add(name)) {
                    names.Add(name);
                }
                i = closeBraceIndex;
            }
            return names;
        }
    }
}
