namespace Hims.Infrastructure.Repositories.Dapper
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Threading.Tasks;
    using global::Dapper;
    using Domain.Repositories.Dapper;
    using Domain.Repositories.SqlGenerator;
    using Shared.Dapper.SqlGenerator;
    using SqlGenerator;

    /// <inheritdoc />
    public partial class DapperRepository<TEntity> : IDapperRepository<TEntity> where TEntity : class
    {
        /// <summary>
        /// The _date time format.
        /// </summary>
        private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss";

        /// <summary>
        /// Initializes a new instance of the <see cref="DapperRepository{TEntity}"/> class.
        /// </summary>
        /// <param name="connection">
        /// The connection.
        /// </param>
        public DapperRepository(IDbConnection connection)
        {
            this.Connection = connection;
            this.SqlGenerator = new SqlGenerator<TEntity>(SqlProvider.PostgreSQL);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DapperRepository{TEntity}"/> class.
        /// </summary>
        /// <param name="connection">
        /// The connection.
        /// </param>
        /// <param name="sqlProvider">
        /// The SQL provider.
        /// </param>
        public DapperRepository(IDbConnection connection, SqlProvider sqlProvider)
        {
            this.Connection = connection;
            this.SqlGenerator = new SqlGenerator<TEntity>(sqlProvider);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DapperRepository{TEntity}"/> class.
        /// </summary>
        /// <param name="connection">
        /// The connection.
        /// </param>
        /// <param name="sqlGenerator">
        /// The SQL generator.
        /// </param>
        public DapperRepository(IDbConnection connection, ISqlGenerator<TEntity> sqlGenerator)
        {
            this.Connection = connection;
            this.SqlGenerator = sqlGenerator;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DapperRepository{TEntity}"/> class.
        /// </summary>
        /// <param name="connection">
        /// The connection.
        /// </param>
        /// <param name="config">
        /// The config.
        /// </param>
        public DapperRepository(IDbConnection connection, SqlGeneratorConfig config)
        {
            this.Connection = connection;
            this.SqlGenerator = new SqlGenerator<TEntity>(config);
        }

        /// <inheritdoc />
        public IDbConnection Connection { get; }

        /// <inheritdoc />
        public ISqlGenerator<TEntity> SqlGenerator { get; }

        /// <inheritdoc />
        public virtual bool Update(TEntity instance) => this.Update(instance, null);

        /// <inheritdoc />
        public virtual bool Update(TEntity instance, IDbTransaction transaction)
        {
            var sqlQuery = this.SqlGenerator.GetUpdate(instance);
            var updated = this.Connection.Execute(sqlQuery.GetSql(), instance, transaction) > 0;
            return updated;
        }

        /// <inheritdoc />
        public virtual Task<int> UpdateAsync(TEntity instance) => this.UpdateAsync(instance, null);

        /// <inheritdoc />
        public virtual async Task<int> UpdateAsync(TEntity instance, IDbTransaction transaction)
        {
            var sqlQuery = this.SqlGenerator.GetUpdate(instance);
            return await this.Connection.ExecuteAsync(sqlQuery.GetSql(), instance, transaction);
        }

        /// <inheritdoc />
        public virtual bool Update(Expression<Func<TEntity, bool>> predicate, TEntity instance) => this.Update(predicate, instance, null);

        /// <inheritdoc />
        public virtual bool Update(Expression<Func<TEntity, bool>> predicate, TEntity instance, IDbTransaction transaction)
        {
            var sqlQuery = this.SqlGenerator.GetUpdate(predicate, instance);
            var updated = this.Connection.Execute(sqlQuery.GetSql(), instance, transaction) > 0;
            return updated;
        }

        /// <inheritdoc />
        public virtual Task<bool> UpdateAsync(Expression<Func<TEntity, bool>> predicate, TEntity instance) => this.UpdateAsync(predicate, instance, null);

        /// <inheritdoc />
        public virtual async Task<bool> UpdateAsync(Expression<Func<TEntity, bool>> predicate, TEntity instance, IDbTransaction transaction)
        {
            var sqlQuery = this.SqlGenerator.GetUpdate(predicate, instance);
            var updated = await this.Connection.ExecuteAsync(sqlQuery.GetSql(), instance, transaction) > 0;
            return updated;
        }

        /// <inheritdoc />
        public virtual bool Insert(TEntity instance) => this.Insert(instance, null);

        /// <inheritdoc />
        public virtual bool Insert(TEntity instance, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetInsert(instance);
            if (this.SqlGenerator.IsIdentity)
            {
                var newId = this.Connection.Query<long>(queryResult.GetSql(), queryResult.Param, transaction).FirstOrDefault();
                return this.SetValue(newId, instance);
            }

            return this.Connection.Execute(queryResult.GetSql(), instance, transaction) > 0;
        }

        /// <inheritdoc />
        public virtual Task<int> InsertAsync(TEntity instance) => this.InsertAsync(instance, null);

        /// <inheritdoc />
        public virtual async Task<int> InsertAsync(TEntity instance, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetInsert(instance);
            if (this.SqlGenerator.IsIdentity)
            {
                var newId = (await this.Connection.QueryAsync<int>(queryResult.GetSql(), queryResult.Param, transaction)).FirstOrDefault();

                // return this.SetValue(newId, instance);
                return newId;
            }

            return await this.Connection.ExecuteAsync(queryResult.GetSql(), instance, transaction);
        }

        /// <inheritdoc />
        public virtual TEntity FindById(object id) => this.FindById(id, null);

        /// <inheritdoc />
        public virtual TEntity FindById(object id, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id);
            return this.Connection.QuerySingleOrDefault<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual Task<TEntity> FindByIdAsync(object id) => this.FindByIdAsync(id, null);

        /// <inheritdoc />
        public virtual Task<TEntity> FindByIdAsync(object id, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id);
            return this.Connection.QuerySingleOrDefaultAsync<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public IEnumerable<TEntity> FindAllBetween(object from, object to, Expression<Func<TEntity, object>> btwField, IDbTransaction transaction = null) => this.FindAllBetween(from, to, btwField, null, transaction);

        /// <inheritdoc />
        public IEnumerable<TEntity> FindAllBetween(
            object from,
            object to,
            Expression<Func<TEntity, object>> btwField,
            Expression<Func<TEntity, bool>> predicate = null,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectBetween(from, to, btwField, predicate);
            return this.Connection.Query<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public IEnumerable<TEntity> FindAllBetween(DateTime from, DateTime to, Expression<Func<TEntity, object>> btwField, IDbTransaction transaction = null) => this.FindAllBetween(from, to, btwField, null, transaction);

        /// <inheritdoc />
        public IEnumerable<TEntity> FindAllBetween(
            DateTime from,
            DateTime to,
            Expression<Func<TEntity, object>> btwField,
            Expression<Func<TEntity, bool>> predicate,
            IDbTransaction transaction = null)
        {
            var fromString = from.ToString(DateTimeFormat);
            var toString = to.ToString(DateTimeFormat);
            return this.FindAllBetween(fromString, toString, btwField, predicate);
        }

        /// <inheritdoc />
        public Task<IEnumerable<TEntity>> FindAllBetweenAsync(object from, object to, Expression<Func<TEntity, object>> btwField, IDbTransaction transaction = null) => this.FindAllBetweenAsync(from, to, btwField, null, transaction);

        /// <inheritdoc />
        public Task<IEnumerable<TEntity>> FindAllBetweenAsync(
            object from,
            object to,
            Expression<Func<TEntity, object>> btwField,
            Expression<Func<TEntity, bool>> predicate,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectBetween(from, to, btwField, predicate);
            return this.Connection.QueryAsync<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public Task<IEnumerable<TEntity>> FindAllBetweenAsync(DateTime from, DateTime to, Expression<Func<TEntity, object>> btwField, IDbTransaction transaction = null) => this.FindAllBetweenAsync(from, to, btwField, null, transaction);

        /// <inheritdoc />
        public Task<IEnumerable<TEntity>> FindAllBetweenAsync(
            DateTime from,
            DateTime to,
            Expression<Func<TEntity, object>> btwField,
            Expression<Func<TEntity, bool>> predicate,
            IDbTransaction transaction = null) =>
            this.FindAllBetweenAsync(from.ToString(DateTimeFormat), to.ToString(DateTimeFormat), btwField, predicate, transaction);

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll(IDbTransaction transaction = null) => this.FindAll(null, transaction);

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectAll(predicate);
            return this.Connection.Query<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync(IDbTransaction transaction = null) => this.FindAllAsync(null, transaction);

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectAll(predicate);
            return this.Connection.QueryAsync<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual TEntity Find() => this.Find(null, null);

        /// <inheritdoc />
        public virtual TEntity Find(IDbTransaction transaction) => this.Find(null, transaction);

        /// <inheritdoc />
        public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate) => this.Find(predicate, null);

        /// <inheritdoc />
        public virtual TEntity Find(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate);
            return this.Connection.QueryFirstOrDefault<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual Task<TEntity> FindAsync() => this.FindAsync(null, null);

        /// <inheritdoc />
        public virtual Task<TEntity> FindAsync(IDbTransaction transaction) => this.FindAsync(null, transaction);

        /// <inheritdoc />
        public virtual Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate) => this.FindAsync(predicate, null);

        /// <inheritdoc />
        public virtual Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate);
            return this.Connection.QueryFirstOrDefaultAsync<TEntity>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual bool Delete(TEntity instance, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetDelete(instance);
            var deleted = this.Connection.Execute(queryResult.GetSql(), queryResult.Param, transaction) > 0;
            return deleted;
        }

        /// <inheritdoc />
        public virtual async Task<bool> DeleteAsync(TEntity instance, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetDelete(instance);
            var deleted = await this.Connection.ExecuteAsync(queryResult.GetSql(), queryResult.Param, transaction) > 0;
            return deleted;
        }

        /// <inheritdoc />
        public virtual bool Delete(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetDelete(predicate);
            var deleted = this.Connection.Execute(queryResult.GetSql(), queryResult.Param, transaction) > 0;
            return deleted;
        }

        /// <inheritdoc />
        public virtual async Task<bool> DeleteAsync(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetDelete(predicate);
            var deleted = await this.Connection.ExecuteAsync(queryResult.GetSql(), queryResult.Param, transaction) > 0;
            return deleted;
        }

        /// <inheritdoc />
        public virtual int Count() => this.Count(transaction: null);

        /// <inheritdoc />
        public virtual int Count(IDbTransaction transaction) => this.Count(null, transaction);

        /// <inheritdoc />
        public virtual int Count(Expression<Func<TEntity, bool>> predicate) => this.Count(predicate, transaction: null);

        /// <inheritdoc />
        public virtual int Count(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetCount(predicate);
            return this.Connection.QueryFirstOrDefault<int>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual int Count(Expression<Func<TEntity, object>> distinctField) => this.Count(distinctField, null);

        /// <inheritdoc />
        public virtual int Count(Expression<Func<TEntity, object>> distinctField, IDbTransaction transaction) => this.Count(null, distinctField, transaction);

        /// <inheritdoc />
        public virtual int Count(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> distinctField) => this.Count(predicate, distinctField, null);

        /// <inheritdoc />
        public virtual int Count(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> distinctField, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetCount(predicate, distinctField);
            return this.Connection.QueryFirstOrDefault<int>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual Task<int> CountAsync() => this.CountAsync(transaction: null);

        /// <inheritdoc />
        public virtual Task<int> CountAsync(IDbTransaction transaction) => this.CountAsync(null, transaction);

        /// <inheritdoc />
        public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate) => this.CountAsync(predicate, transaction: null);

        /// <inheritdoc />
        public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetCount(predicate);
            return this.Connection.QueryFirstOrDefaultAsync<int>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public virtual Task<int> CountAsync(Expression<Func<TEntity, object>> distinctField) => this.CountAsync(distinctField, null);

        /// <inheritdoc />
        public virtual Task<int> CountAsync(Expression<Func<TEntity, object>> distinctField, IDbTransaction transaction) => this.CountAsync(null, distinctField, transaction);

        /// <inheritdoc />
        public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> distinctField) => this.CountAsync(predicate, distinctField, null);

        /// <inheritdoc />
        public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> distinctField, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetCount(predicate, distinctField);
            return this.Connection.QueryFirstOrDefaultAsync<int>(queryResult.GetSql(), queryResult.Param, transaction);
        }

        /// <inheritdoc />
        public bool BulkUpdate(IEnumerable<TEntity> instances) => this.BulkUpdate(instances, null);

        /// <inheritdoc />
        public bool BulkUpdate(IEnumerable<TEntity> instances, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetBulkUpdate(instances);
            var result = this.Connection.Execute(queryResult.GetSql(), queryResult.Param, transaction) > 0;
            return result;
        }

        /// <inheritdoc />
        public Task<bool> BulkUpdateAsync(IEnumerable<TEntity> instances) => this.BulkUpdateAsync(instances, null);

        /// <inheritdoc />
        public async Task<bool> BulkUpdateAsync(IEnumerable<TEntity> instances, IDbTransaction transaction)
        {
            var queryResult = this.SqlGenerator.GetBulkUpdate(instances);
            var result = await this.Connection.ExecuteAsync(queryResult.GetSql(), queryResult.Param, transaction) > 0;
            return result;
        }

        /// <inheritdoc />
        public virtual int BulkInsert(IEnumerable<TEntity> instances, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetBulkInsert(instances);
            var count = this.Connection.Execute(queryResult.GetSql(), queryResult.Param, transaction);
            return count;
        }

        /// <inheritdoc />
        public virtual async Task<int> BulkInsertAsync(IEnumerable<TEntity> instances, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetBulkInsert(instances);
            var count = await this.Connection.ExecuteAsync(queryResult.GetSql(), queryResult.Param, transaction);
            return count;
        }

        /// <inheritdoc />
        public virtual TEntity FindById<TChild1>(object id, Expression<Func<TEntity, object>> child1, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1);
            return this.ExecuteJoinQuery<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity FindById<TChild1, TChild2>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2);
            return this.ExecuteJoinQuery<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity FindById<TChild1, TChild2, TChild3>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2, child3).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity FindById<TChild1, TChild2, TChild3, TChild4>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3, child4);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, child1, child2, child3, child4).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity FindById<TChild1, TChild2, TChild3, TChild4, TChild5>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3, child4, child5);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, child1, child2, child3, child4, child5).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity FindById<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            Expression<Func<TEntity, object>> child6,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3, child4, child5, child6);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(queryResult, transaction, child1, child2, child3, child4, child5, child6).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindByIdAsync<TChild1>(
            object id,
            Expression<Func<TEntity, object>> child1,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1);
            return (await this.ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindByIdAsync<TChild1, TChild2>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindByIdAsync<TChild1, TChild2, TChild3>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2, child3)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindByIdAsync<TChild1, TChild2, TChild3, TChild4>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3, child4);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, child1, child2, child3, child4)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindByIdAsync<TChild1, TChild2, TChild3, TChild4, TChild5>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3, child4, child5);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, child1, child2, child3, child4, child5)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindByIdAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(
            object id,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            Expression<Func<TEntity, object>> child6,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectById(id, child1, child2, child3, child4, child5, child6);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(queryResult, transaction, child1, child2, child3, child4, child5, child6)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll<TChild1>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> child1, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectAll(predicate, child1);
            return this.ExecuteJoinQuery<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1);
        }

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll<TChild1, TChild2>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2);
            return this.ExecuteJoinQuery<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(sqlQuery, transaction, child1, child2);
        }

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll<TChild1, TChild2, TChild3>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(sqlQuery, transaction, child1, child2, child3);
        }

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll<TChild1, TChild2, TChild3, TChild4>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3, child4);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(sqlQuery, transaction, child1, child2, child3, child4);
        }

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll<TChild1, TChild2, TChild3, TChild4, TChild5>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3, child4, child5);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(sqlQuery, transaction, child1, child2, child3, child4, child5);
        }

        /// <inheritdoc />
        public virtual IEnumerable<TEntity> FindAll<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            Expression<Func<TEntity, object>> child6,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3, child4, child5, child6);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(sqlQuery, transaction, child1, child2, child3, child4, child5, child6);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync<TChild1>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> child1, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectAll(predicate, child1);
            return this.ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync<TChild1, TChild2>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2);
            return this.ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(sqlQuery, transaction, child1, child2);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync<TChild1, TChild2, TChild3>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3);
            return this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(sqlQuery, transaction, child1, child2, child3);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync<TChild1, TChild2, TChild3, TChild4>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3, child4);
            return this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(sqlQuery, transaction, child1, child2, child3, child4);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync<TChild1, TChild2, TChild3, TChild4, TChild5>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3, child4, child5);
            return this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(sqlQuery, transaction, child1, child2, child3, child4, child5);
        }

        /// <inheritdoc />
        public virtual Task<IEnumerable<TEntity>> FindAllAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            Expression<Func<TEntity, object>> child6,
            IDbTransaction transaction = null)
        {
            var sqlQuery = this.SqlGenerator.GetSelectAll(predicate, child1, child2, child3, child4, child5, child6);
            return this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(sqlQuery, transaction, child1, child2, child3, child4, child5, child6);
        }

        /// <inheritdoc />
        public virtual TEntity Find<TChild1>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> child1, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1);
            return this.ExecuteJoinQuery<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity Find<TChild1, TChild2>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2);
            return this.ExecuteJoinQuery<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity Find<TChild1, TChild2, TChild3>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2, child3).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity Find<TChild1, TChild2, TChild3, TChild4>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3, child4);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, child1, child2, child3, child4).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity Find<TChild1, TChild2, TChild3, TChild4, TChild5>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3, child4, child5);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, child1, child2, child3, child4, child5).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual TEntity Find<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            Expression<Func<TEntity, object>> child6,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3, child4, child5, child6);
            return this.ExecuteJoinQuery<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(queryResult, transaction, child1, child2, child3, child4, child5, child6).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindAsync<TChild1>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> child1, IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1);
            return (await this.ExecuteJoinQueryAsync<TChild1, DontMap, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindAsync<TChild1, TChild2>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, DontMap, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindAsync<TChild1, TChild2, TChild3>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, DontMap, DontMap, DontMap>(queryResult, transaction, child1, child2, child3)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindAsync<TChild1, TChild2, TChild3, TChild4>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3, child4);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, DontMap, DontMap>(queryResult, transaction, child1, child2, child3, child4)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindAsync<TChild1, TChild2, TChild3, TChild4, TChild5>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3, child4, child5);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, DontMap>(queryResult, transaction, child1, child2, child3, child4, child5)).FirstOrDefault();
        }

        /// <inheritdoc />
        public virtual async Task<TEntity> FindAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(
            Expression<Func<TEntity, bool>> predicate,
            Expression<Func<TEntity, object>> child1,
            Expression<Func<TEntity, object>> child2,
            Expression<Func<TEntity, object>> child3,
            Expression<Func<TEntity, object>> child4,
            Expression<Func<TEntity, object>> child5,
            Expression<Func<TEntity, object>> child6,
            IDbTransaction transaction = null)
        {
            var queryResult = this.SqlGenerator.GetSelectFirst(predicate, child1, child2, child3, child4, child5, child6);
            return (await this.ExecuteJoinQueryAsync<TChild1, TChild2, TChild3, TChild4, TChild5, TChild6>(queryResult, transaction, child1, child2, child3, child4, child5, child6)).FirstOrDefault();
        }

        /// <summary>
        /// The set value.
        /// </summary>
        /// <param name="newId">
        /// The new id.
        /// </param>
        /// <param name="instance">
        /// The instance.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool SetValue(long newId, TEntity instance)
        {
            if (newId <= 0)
            {
                return false;
            }

            var newParsedId = Convert.ChangeType(newId, this.SqlGenerator.IdentitySqlProperty.PropertyInfo.PropertyType);
            this.SqlGenerator.IdentitySqlProperty.PropertyInfo.SetValue(instance, newParsedId);
            return true;
        }
    }
}