using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace InABox.Core
{
    
    public interface IAutoEntityUnionGenerator : IAutoEntityGenerator
    {
        IAutoEntityUnionTable[] Tables { get; }
    }

    public interface IAutoEntityUnionConstant
    {
        object Value { get; }
        IColumn Mapping { get; }
    }

    public class AutoEntityUnionConstant : IAutoEntityUnionConstant
    {
        public object Value { get; private set; }
        public IColumn Mapping { get; private set; }

        public AutoEntityUnionConstant(object value, IColumn mapping)
        {
            Value = value;
            Mapping = mapping;
        }
    }
    
    public interface IAutoEntityUnionMapping
    {
        IComplexColumn Source { get; }
        IColumn Target { get; }
    }
    
    public class AutoEntityUnionMapping : IAutoEntityUnionMapping
    {
        public IComplexColumn Source { get; private set; }
        public IColumn Target { get; private set; }

        public AutoEntityUnionMapping(IComplexColumn source, IColumn target)
        {
            Source = source;
            Target = target;
        }
    }
    
    public interface IAutoEntityUnionTable
    {
        Type Entity { get; }
        IFilter? Filter { get; }
        AutoEntityUnionConstant[] Constants { get; }
        AutoEntityUnionMapping[] Mappings { get; }
    }
    
    public class AutoEntityUnionTable<TInterface,TEntity> : IAutoEntityUnionTable
    {
        public Type Entity => typeof(TEntity);
        public IFilter? Filter { get; private set; }
        
        
        private List<AutoEntityUnionConstant> _constants = new List<AutoEntityUnionConstant>();
        public AutoEntityUnionConstant[] Constants => _constants.ToArray();
        
        private List<AutoEntityUnionMapping> _mappings = new List<AutoEntityUnionMapping>();
        public AutoEntityUnionMapping[] Mappings => _mappings.ToArray();

        public AutoEntityUnionTable(Filter<TEntity>? filter)
        {
            Filter = filter;
        }
        
        public AutoEntityUnionTable<TInterface,TEntity> WithFilter(Filter<TEntity>? filter)
        {
            Filter = filter;
            return this;
        }
        
        public AutoEntityUnionTable<TInterface, TEntity> AddConstant<TType>(Expression<Func<TInterface, object?>> mapping, TType constant)
        {
            _constants.Add(new AutoEntityUnionConstant(constant, new Column<TInterface>(mapping)));
            return this;
        }
        
        public AutoEntityUnionTable<TInterface, TEntity> AliasField<TType>(Expression<Func<TInterface, object?>> target, Expression<Func<TEntity, TType>> source)
        {
            var _tgt = new Column<TInterface>(target);
            var _node = new ComplexFormulaFieldNode<TEntity, TType>(source);
            var _src = new ComplexColumn<TEntity, TType>(_tgt.Property, _node); 
            
            _mappings.Add(new AutoEntityUnionMapping(_src, _tgt));
            return this;
        }
        
        public AutoEntityUnionTable<TInterface, TEntity> AliasField<TType>(Expression<Func<TInterface, object?>> target, IComplexFormulaNode<TEntity, TType> source)
        {
            var _tgt = new Column<TInterface>(target);
            var _src = new ComplexColumn<TEntity, TType>(_tgt.Property, source); 
            
            _mappings.Add(new AutoEntityUnionMapping(_src, _tgt));
            return this;
        }
        
        public AutoEntityUnionTable<TInterface, TEntity> AliasField(Column<TInterface> target, Column<TEntity> source)
        {
            var _node = new ComplexFormulaFieldNode<TEntity, object?>(source.Property);
            var _src = new ComplexColumn<TEntity, object?>(target.Property, _node); 
            
            _mappings.Add(new AutoEntityUnionMapping(_src, target));
            return this;
        }
        
        public AutoEntityUnionTable<TInterface, TEntity> AliasField(Expression<Func<TInterface, object?>> target, string formatString, params Expression<Func<TEntity, object?>>[] fields)
        {
            var _tgt = new Column<TInterface>(target);
            var _o = fields.Select(x => new ComplexFormulaFieldNode<TEntity, object?>(x));
            var _o2 = _o.OfType<IComplexFormulaNode<TEntity, object?>>().ToList()
                .Prepend(new ComplexFormulaConstantNode<TEntity, object>(formatString))
                .ToArray();
            var _n = new ComplexFormulaFormulaNode<TEntity, object?>(_o2, FormulaOperator.Format);
            //var _node = new ComplexFormulaFieldNode<TEntity, TType>(source);
            var _src = new ComplexColumn<TEntity, object?>(_tgt.Property, _n); 
            
            _mappings.Add(new AutoEntityUnionMapping(_src, _tgt));
            return this;
        }
        
    }
    
    public abstract class AutoEntityUnionGenerator<TInterface> : IAutoEntityUnionGenerator
    {
        public AutoEntityUnionGenerator()
        {
            Configure();
        }

        private List<IAutoEntityUnionTable> _tables = new List<IAutoEntityUnionTable>();
                
        public IAutoEntityUnionTable[] Tables => _tables.ToArray();
        
        public AutoEntityUnionTable<TInterface, TType> AddTable<TType>(Filter<TType>? filter = null)
        {
            var table = new AutoEntityUnionTable<TInterface, TType>(filter);
            _tables.Add(table);
            return table;
        }
        
        protected abstract void Configure(); 
        
        public Type Definition => typeof(TInterface);
        
        public abstract bool Distinct { get; }

        public abstract Column<TInterface>[] IDColumns { get; }

        IColumn[] IAutoEntityGenerator.IDColumns => IDColumns;
    }
}