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

namespace InABox.Core
{
    
    public interface IAutoEntityCartesianMapping
    {
        IColumn Column { get;}
        IColumn Mapping { get; }        
    }
    
    public struct AutoEntityCartesianMapping : IAutoEntityCartesianMapping
    {
        public IColumn Column { get; private set; }
        public IColumn Mapping { get; private set; }

        public AutoEntityCartesianMapping(IColumn column, IColumn mapping)
        {
            Column = column;
            Mapping = mapping;
        }
        
    }
    
    public interface IAutoEntityCartesianConstant
    {
        object Constant { get;}
        IColumn Mapping { get; }        
    }
    
    public struct AutoEntityCartesianConstant: IAutoEntityCartesianConstant
    {
        public object Constant { get; private set; }
        public IColumn Mapping { get; private set; }

        public AutoEntityCartesianConstant(object constant, IColumn mapping)
        {
            Constant = constant;
            Mapping = mapping;
        }
        
    }

    public interface IAutoEntityCartesianTable
    {
        Type Type { get; }

        IFilter? Filter { get; }
        
        IColumns? Columns { get; }

        IAutoEntityCartesianMapping[] Mappings { get; }
    }

    public class AutoEntityCartesianTable<T, TInterface> : IAutoEntityCartesianTable
    {
        public Type Type => typeof(T);
        
        public IFilter? Filter { get; private set; }
        
        private List<IAutoEntityCartesianMapping> _mappings = new List<IAutoEntityCartesianMapping>();
        public IAutoEntityCartesianMapping[] Mappings => _mappings.ToArray();

        public AutoEntityCartesianTable(Filter<T> filter)
        {
            Filter = filter;
        }

        public IColumns Columns
        {
            get
            {
                var columns = Core.Columns.None<T>();
                foreach (var mapping in _mappings)
                {
                    columns.Add((mapping.Column as IColumn).Property);
                }
                return columns;
            }
        }

        public AutoEntityCartesianTable<T, TInterface> AddMapping(Expression<Func<T, object?>> source, Expression<Func<TInterface, object?>> mapping)
        {
            _mappings.Add(new AutoEntityCartesianMapping(new Column<T>(source), new Column<TInterface>(mapping)));
            return this;
        }
        
    }
    
    public interface IAutoEntityCartesianGenerator : IAutoEntityGenerator
    {

        IAutoEntityCartesianTable[] Tables { get; }
        IAutoEntityCartesianConstant[] Constants { get; }
        
        void Configure();
    }
    
    public abstract class AutoEntityCartesianGenerator<TInterface>: IAutoEntityCartesianGenerator
    {
        
        public AutoEntityCartesianGenerator()
        {
            Configure();
        }
        
        private List<IAutoEntityCartesianTable> _tables = new List<IAutoEntityCartesianTable>();
        public IAutoEntityCartesianTable[] Tables => _tables.ToArray();

        private List<IAutoEntityCartesianConstant> _constants = new List<IAutoEntityCartesianConstant>();
        public IAutoEntityCartesianConstant[] Constants => _constants.ToArray();

        public abstract void Configure();
        
        protected AutoEntityCartesianTable<T,TInterface> AddTable<T>(Filter<T> filter)
        {
            var table = new AutoEntityCartesianTable<T,TInterface>(filter);
            _tables.Add(table);
            return table;
        }
        
        public void AddConstant<TType>(TType constant, Expression<Func<TInterface, object?>> mapping)
        {
            _constants.Add(new AutoEntityCartesianConstant(constant, new Column<TInterface>(mapping)));
        }
        
        public Type Definition => typeof(TInterface);

        public abstract bool Distinct { get; }

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

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