// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections;
using TypeAgent.KnowPro.Query;

namespace TypeAgent.KnowPro;

public class SearchTermGroup : ISearchTerm, IEnumerable<ISearchTerm>
{
    public SearchTermGroup(SearchTermBooleanOp booleanOp, IList<ISearchTerm>? terms = null)
    {
        ArgumentVerify.ThrowIfNull(booleanOp, nameof(booleanOp));

        BooleanOp = booleanOp;
        Terms = terms ?? [];
    }

    public SearchTermGroup(SearchTermBooleanOp booleanOp, params ISearchTerm[]? terms) : this(booleanOp, (IList<ISearchTerm>)terms)
    {
    }

    public SearchTermBooleanOp BooleanOp { get; }

    public IList<ISearchTerm> Terms { get; set; }

    public bool IsEmpty => Terms.IsNullOrEmpty();

    public void Add(ISearchTerm searchTerm)
    {
        ArgumentVerify.ThrowIfNull(searchTerm, nameof(searchTerm));
        Terms.Add(searchTerm);
    }

    public void Add(string term, bool exactMatch = false)
    {
        Add(new SearchTerm(term, exactMatch));
    }

    public void Add(IEnumerable<string> terms, bool exactMatch = false)
    {
        ArgumentVerify.ThrowIfNull(terms, nameof(terms));

        foreach (var term in terms)
        {
            Add(term, exactMatch);
        }
    }

    public void Add(KnowledgePropertyName propertyName, string value, bool exactMatch = false)
    {
        Add(new PropertySearchTerm(propertyName, new SearchTerm(value, exactMatch)));
    }

    public void Add(KnowledgePropertyName propertyName, IEnumerable<string> values, bool exactMatch = false)
    {
        ArgumentVerify.ThrowIfNull(values, nameof(values));

        foreach (string value in values)
        {
            Add(new PropertySearchTerm(propertyName, new SearchTerm(value, exactMatch)));
        }
    }

    public void Add(string propertyName, string propertyValue)
    {
        Add(new PropertySearchTerm(propertyName, propertyValue));
    }

    public ISearchTerm Optimize()
    {
        return Terms.Count == 1 ? Terms[0] : this;
    }

    public IEnumerator<ISearchTerm> GetEnumerator()
    {
        return this.Terms.GetEnumerator();
    }

    public override string ToString()
    {
        // TODO: pretty printer
        string sep = $" {BooleanOp.ToString().ToUpper()} ";

        StringBuilder sb = new StringBuilder();
        sb.Append(" ( ");
        for (int i = 0; i < Terms.Count; ++i)
        {
            if (i > 0)
            {
                sb.Append(sep);
            }
            sb.Append('(');
            sb.Append(Terms[i].ToString());
            sb.Append(')');
        }
        sb.Append(" )");
        return sb.ToString();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)this.Terms).GetEnumerator();
    }
}

public enum SearchTermBooleanOp
{
    [JsonPropertyName("or")]
    Or,
    [JsonPropertyName("orMax")]
    OrMax,
    [JsonPropertyName("and")]
    And
}
