namespace Hims.Domain.Repositories.SqlGenerator
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;
    using Shared.Dapper.SqlGenerator;

    /// <summary>
    /// Universal SqlGenerator for Tables
    /// </summary>
    /// <typeparam name="TEntity">
    /// The entity.
    /// </typeparam>
    public interface ISqlGenerator<TEntity> where TEntity : class
    {
        /// <summary>
        /// Gets the all properties.
        /// </summary>
        PropertyInfo[] AllProperties { get; }

        /// <summary>
        /// Gets a value indicating whether has updated at.
        /// </summary>
        bool HasUpdatedAt { get; }

        /// <summary>
        /// Gets the updated at property.
        /// </summary>
        PropertyInfo UpdatedAtProperty { get; }

        /// <summary>
        /// Gets the updated at property metadata.
        /// </summary>
        SqlPropertyMetadata UpdatedAtPropertyMetadata { get; }

        /// <summary>
        /// Gets a value indicating whether is identity.
        /// </summary>
        bool IsIdentity { get; }

        /// <summary>
        /// Gets the table name.
        /// </summary>
        string TableName { get; }

        /// <summary>
        /// Gets the table schema.
        /// </summary>
        string TableSchema { get; }

        /// <summary>
        /// Gets the identity sql property.
        /// </summary>
        SqlPropertyMetadata IdentitySqlProperty { get; }

        /// <summary>
        /// Gets the key sql properties.
        /// </summary>
        SqlPropertyMetadata[] KeySqlProperties { get; }

        /// <summary>
        /// Gets the sql properties.
        /// </summary>
        SqlPropertyMetadata[] SqlProperties { get; }

        /// <summary>
        /// Gets the sql join properties.
        /// </summary>
        SqlJoinPropertyMetadata[] SqlJoinProperties { get; }

        /// <summary>
        /// Gets the config.
        /// </summary>
        SqlGeneratorConfig Config { get; }

        /// <summary>
        /// Gets a value indicating whether logical delete.
        /// </summary>
        bool LogicalDelete { get; }

        /// <summary>
        /// Gets the status property name.
        /// </summary>
        string StatusPropertyName { get; }

        /// <summary>
        /// Gets the logical delete value.
        /// </summary>
        object LogicalDeleteValue { get; }

        /// <summary>
        /// The get count.
        /// </summary>
        /// <param name="predicate">
        /// The predicate.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetCount(Expression<Func<TEntity, bool>> predicate);

        /// <summary>
        /// The get count.
        /// </summary>
        /// <param name="predicate">
        /// The predicate.
        /// </param>
        /// <param name="distinctField">
        /// The distinct field.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetCount(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, object>> distinctField);

        /// <summary>
        /// The get insert.
        /// </summary>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetInsert(TEntity entity);

        /// <summary>
        /// The get bulk insert.
        /// </summary>
        /// <param name="entities">
        /// The entities.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetBulkInsert(IEnumerable<TEntity> entities);

        /// <summary>
        /// The get update.
        /// </summary>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetUpdate(TEntity entity);

        /// <summary>
        /// The get update.
        /// </summary>
        /// <param name="predicate">
        /// The predicate.
        /// </param>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetUpdate(Expression<Func<TEntity, bool>> predicate, TEntity entity);

        /// <summary>
        /// The get bulk update.
        /// </summary>
        /// <param name="entities">
        /// The entities.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetBulkUpdate(IEnumerable<TEntity> entities);

        /// <summary>
        /// The get select by id.
        /// </summary>
        /// <param name="id">
        /// The id.
        /// </param>
        /// <param name="includes">
        /// The includes.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetSelectById(object id, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// The get select first.
        /// </summary>
        /// <param name="predicate">
        /// The predicate.
        /// </param>
        /// <param name="includes">
        /// The includes.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetSelectFirst(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// The get select all.
        /// </summary>
        /// <param name="predicate">
        /// The predicate.
        /// </param>
        /// <param name="includes">
        /// The includes.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetSelectAll(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includes);

        /// <summary>
        /// The get select between.
        /// </summary>
        /// <param name="from">
        /// The from.
        /// </param>
        /// <param name="to">
        /// The to.
        /// </param>
        /// <param name="btwField">
        /// The btw field.
        /// </param>
        /// <param name="expression">
        /// The expression.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetSelectBetween(object from, object to, Expression<Func<TEntity, object>> btwField, Expression<Func<TEntity, bool>> expression = null);

        /// <summary>
        /// The get delete.
        /// </summary>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetDelete(TEntity entity);

        /// <summary>
        /// The get delete.
        /// </summary>
        /// <param name="predicate">
        /// The predicate.
        /// </param>
        /// <returns>
        /// The <see cref="SqlQuery"/>.
        /// </returns>
        SqlQuery GetDelete(Expression<Func<TEntity, bool>> predicate);
    }
}