using System.Text.RegularExpressions;
using Model.Glossary;

namespace Services.Website;

public class GlossaryService : IGlossaryService
{
    private List<string>? _sortedTerms;
    private Dictionary<string, GlossaryTermModel>? _terms;

    public GlossaryTermModel? GetTerm(string id)
    {
        var terms = GetAllTermsDict();
        return terms.TryGetValue(id, out var term) ? term : null;
    }

    public List<GlossaryTermModel> SearchTerms(string query)
    {
        var q = query.ToLowerInvariant();
        return GetAllTerms().Where(t =>
            t.Term.Contains(q, StringComparison.OrdinalIgnoreCase) ||
            t.ShortDefinition.Contains(q, StringComparison.OrdinalIgnoreCase) ||
            t.Category.Contains(q, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    public List<GlossaryTermModel> GetTermsByCategory(string category)
    {
        return GetAllTerms().Where(t =>
            t.Category.Equals(category, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    public List<GlossaryTermModel> GetAllTerms()
    {
        return GetAllTermsDict().Values.ToList();
    }

    public List<string> GetCategories()
    {
        return GetAllTerms().Select(t => t.Category).Distinct().ToList();
    }

    public string LinkifyText(string text)
    {
        if (string.IsNullOrEmpty(text)) return text;

        var sorted = GetSortedTerms();

        foreach (var term in sorted)
        {
            var pattern = $@"\b{Regex.Escape(term)}\b";
            text = Regex.Replace(text, pattern, match =>
            {
                var termData = GetTermByName(term);
                if (termData == null) return match.Value;
                return
                    $"<span class=\"glossary-link\" data-glossary-id=\"{termData.Id}\">{match.Value}</span>";
            });
        }

        return text;
    }

    public void Subscribe(Action action)
    {
        OnChange += action;
    }

    public void Unsubscribe(Action action)
    {
        OnChange += action;
    }

    private event Action OnChange = null!;

    private void NotifyDataChanged()
    {
        OnChange?.Invoke();
    }

    private Dictionary<string, GlossaryTermModel> GetAllTermsDict()
    {
        _terms ??= GlossaryData.GetTerms();
        return _terms;
    }

    private List<string> GetSortedTerms()
    {
        if (_sortedTerms != null) return _sortedTerms;

        _sortedTerms = GetAllTerms()
            .Select(t => t.Term)
            .OrderByDescending(t => t.Length)
            .ToList();

        return _sortedTerms;
    }

    private GlossaryTermModel? GetTermByName(string name)
    {
        return GetAllTerms().FirstOrDefault(t =>
            t.Term.Equals(name, StringComparison.OrdinalIgnoreCase));
    }
}