using Beam.Abstractions;
using HtmlAgilityPack;

namespace Beam.Dynamic;


public class ManyComposeDataProviders<T> : IManySelectionComposableDataProvider<T> {
    public required IManySelectionComposableDataProvider<object>[] SelectWith { get; init; }
    public required IManySelectionComposableDataProvider<T> GetWith { get; init; }
    
    private ManyComposeDataProviders() {}

    public static ManyComposeDataProviders<T> Create(IManySelectionComposableDataProvider<object> selectWith, IManySelectionComposableDataProvider<T> getWith) {
        return new ManyComposeDataProviders<T>() {
            GetWith = getWith,
            SelectWith = [selectWith]
        };
    }

    public static ManyComposeDataProviders<T> Create(IManySelectionComposableDataProvider<object>[] selectWiths, IManySelectionComposableDataProvider<T> getWith) {
        return new ManyComposeDataProviders<T>() {
            GetWith = getWith,
            SelectWith = selectWiths
        };
    }

    /// <summary>
    /// Composes the data providers, first selecting a node with <see cref="SelectWith"/>, then getting the data with <see cref="GetWith"/>.
    /// </summary>
    /// <exception cref="Exception">Throws when <see cref="SelectWith"/> returns a null value.</exception>
    /// <param name="document"></param>
    /// <returns></returns>
    public T Get(HtmlDocument document) {
        var selected = SelectMany(document);
        if (selected is null)
            throw new Exception("Selection operation failed.");
        return GetWith.ManyGet(selected);
    }
    
    /// <summary>
    /// Uses the <see cref="GetWith"/> data provider to get the data from the supplied node.
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    public T ManyGet(HtmlNode[] node) {
        return GetWith.ManyGet(node);
    }
    
    /// <summary>
    /// Uses the <see cref="SelectWith"/> data provider to select a node from the supplied document.
    /// </summary>
    /// <param name="doc"></param>
    /// <returns></returns>
    public HtmlNode[]? SelectMany(HtmlDocument doc) {
        var selected = SelectWith[0].SelectMany(doc);
        foreach(var provider in SelectWith.Skip(1)) {
            if (selected is null)
                return null;
            selected = provider.SelectMany(selected);
        }

        return selected;
    }

    /// <summary>
    /// Uses the <see cref="SelectWith"/> data provider to select a node from the supplied document.
    /// </summary>
    /// <param name="doc"></param>
    /// <returns></returns>
    public HtmlNode[]? SelectMany(HtmlNode[] node) {
        var selected = SelectWith[0].SelectMany(node);
        foreach(var provider in SelectWith.Skip(1)) {
            if (selected is null)
                return null;
            selected = provider.SelectMany(selected);
        }

        return selected;
    }
}