using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq.Expressions;
using iiie.Logs.DataAccess;
using iiie.Logs.DBO;

namespace iiie.WebApiUtils.BusinessManager
{
    /// <summary>
    /// Helper for SQL Server data access
    /// </summary>
    public abstract class SqlServerBasicManager<TDbObject, TEntities, TThis>
        where TDbObject : class
        where TEntities : DbContext, new()
        where TThis : SqlServerBasicManager<TDbObject, TEntities, TThis>, new()
    {
        /// <summary>
        /// Return the table to be used
        /// </summary>
        /// <param name="db">The database</param>
        /// <returns>The table instance</returns>
        public abstract DbSet<TDbObject> GetTable(TEntities db);

        /// <summary>
        /// Return the database table to use
        /// </summary>
        /// <param name="db">The database instance</param>
        /// <returns>The table</returns>
        public static DbSet<TDbObject> GetTableStatic(TEntities db)
        {
            var type = new TThis();
            return type.GetTable(db);
        }

        /// <summary>
        /// Get a predicate form the given keys and values
        /// </summary>
        /// <param name="keys">The keys and values to select data</param>
        /// <returns>The predicate</returns>
        public static Expression<Func<TDbObject, bool>> GetExpression(params KeyValuePair<string, object>[] keys)
        {
            var param = Expression.Parameter(typeof(TDbObject), "x");
            Expression totalExp = null;
            foreach (var pair in keys)
            {
                var equalExp = Expression.Equal(Expression.Property(param, pair.Key), Expression.Constant(pair.Value));
                totalExp = totalExp == null ? equalExp : Expression.And(equalExp, totalExp);
            }
            return Expression.Lambda<Func<TDbObject, bool>>(totalExp, param);
        }

        /// <summary>
        /// Execute SQL quries in try catch
        /// </summary>
        /// <param name="func">The queries to be performed</param>
        /// <returns>The result or an error</returns>
        public static OpResult<U> Execute<U>(Func<TEntities, DbSet<TDbObject>, OpResult<U>> func)
        {
            try
            {
                using (var db = new TEntities())
                {
                    var table = GetTableStatic(db);
                    return func(db, table);
                }
            }
            catch (Exception e)
            {
                return Logger.Error<U>(ResultStatus.DBError, e);
            }
        }
    }
}
