using CsvHelper.Configuration;
using InABox.Core;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace InABox.Poster.CSV;

public interface ICSVClassMap
{
    ClassMap ClassMap { get; }
}

public interface ICSVClassMap<T> : ICSVClassMap
{
    new ClassMap<T> ClassMap { get; }

    ClassMap ICSVClassMap.ClassMap => ClassMap;

    void Map(string name, Expression<Func<T, object>> expr);
}

public class CSVClassMap<T> : ClassMap<T>, ICSVClassMap<T>
{
    public ClassMap<T> ClassMap => this;

    public void Map(string name, Expression<Func<T, object>> expr)
    {
        Map(expr).Name(name);
    }
}

public interface ICSVExport<TPostable> : IPostResult<TPostable>
    where TPostable : IPostable
{
    Type Type { get; }

    ICSVClassMap ClassMap { get; }

    IEnumerable<object> Records { get; }
}

public interface ICSVExport<TExport, TPostable> : ICSVExport<TPostable>
    where TExport : class
    where TPostable : IPostable
{
    new Type Type => typeof(TExport);
    new CSVClassMap<TExport> ClassMap { get; }
    new IEnumerable<TExport> Records { get; }

    Type ICSVExport<TPostable>.Type => Type;
    ICSVClassMap ICSVExport<TPostable>.ClassMap => ClassMap;
    IEnumerable<object> ICSVExport<TPostable>.Records => Records;
}

public class CSVExport<TExport, TPostable> : ICSVExport<TExport, TPostable>
    where TExport : class
    where TPostable : IPostable
{
    public CSVClassMap<TExport> ClassMap { get; } = new CSVClassMap<TExport>();

    public IEnumerable<TExport> Records => items.Where(x => x.Item2.PostedStatus == PostedStatus.Posted).Select(x => x.Item1);

    private List<Tuple<TExport, TPostable>> items = new List<Tuple<TExport, TPostable>>();

    private Dictionary<Type, List<IPostableFragment<TPostable>>> fragments = new Dictionary<Type, List<IPostableFragment<TPostable>>>();

    public IEnumerable<TPostable> PostedEntities => items.Select(x => x.Item2);

    public IEnumerable<KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>> Fragments =>
        fragments.Select(x => new KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>(x.Key, x.Value));

    public void DefineMapping(List<Tuple<string, Expression<Func<TExport, object>>>> mappings)
    {
        foreach(var (name, expr) in mappings)
        {
            ClassMap.Map(name, expr);
        }
    }
    public void Map(string name,  Expression<Func<TExport, object>> expr)
    {
        ClassMap.Map(name, expr);
    }

    public void AddSuccess(TExport export, TPostable postable)
    {
        postable.Post();
        items.Add(new(export, postable));
    }
}

/// <summary>
/// Defines an interface for posters that can export to CSV.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
[Caption("CSV")]
public interface ICSVPoster<TEntity> : IPoster<TEntity, CSVPosterSettings>
    where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
{
    bool BeforePost(IDataModel<TEntity> model);

    ICSVExport<TEntity> Process(IDataModel<TEntity> model);

    void AfterPost(IDataModel<TEntity> model, IPostResult<TEntity> result);
}
