using SqlSugar;
using System.Linq.Expressions;

namespace SqlSugarORM;
public class SqlSugarCustom
{
    public SqlSugarClient? Client { get; private set; }
    #region Internal

    private bool disposedValue;

    private void Config(SqlSugarClient client)
    {
        client.Aop.OnLogExecuting = SqlLog;
    }
    private void SqlLog(string sql, SugarParameter[] paras)
    {
        string log = UtilMethods.GetNativeSql(sql, paras);
    }
    #endregion

    public bool Initialize()
    {

        return true;
    }

    public bool Open(string connectionString, SqlSugar.DbType dbType, bool isAutoConnection)
    {
        if (this.Client is not null)
            return false;

        ConnectionConfig config = new()
        {
            ConnectionString = connectionString,
            DbType = dbType,
            IsAutoCloseConnection = isAutoConnection
        };
        try
        {
            SqlSugarClient Db = new(config, Config);
            Db.DbMaintenance.CreateDatabase();
            this.Client = Db;
        }
        catch
        {
            return false;
        }
        return true;
    }

    public bool CreateDataBase(string dbName)
    {
        if (this.Client is null)
            return false;

        if (string.IsNullOrEmpty(dbName))
            return false;

        try
        {
            this.Client.DbMaintenance.CreateDatabase(dbName);
        }
        catch
        {
            return false;
        }

        return true;
    }

    public bool CreateTable<T>(string? tableName)
    {
        if (this.Client is null)
            return false;

        try
        {
            if (string.IsNullOrEmpty(tableName))
                this.Client.CodeFirst.InitTables<T>();
            else
                this.Client.CodeFirst.As(typeof(T), tableName).InitTables<T>();
        }
        catch
        {
            return false;
        }


        return true;
    }


    public bool Insert<T>(T data) where T : class, new()
    {
        if (this.Client is null)
            return false;
        try
        {
            this.Client.Insertable(data).ExecuteCommand();
        }
        catch
        {
            return false;
        }


        return true;
    }

    public bool Insert<T>(string tablename, T data) where T : class, new()
    {
        if (this.Client is null)
            return false;
        try
        {
            if (string.IsNullOrEmpty(tablename))
                this.Client.Insertable(data).ExecuteCommand();
            else
                this.Client.Insertable(data).AS(tablename).ExecuteCommand();
        }
        catch
        {
            return false;
        }

        return true;
    }


    public async Task<bool> Query<T>(Action<List<T>> results)
    {
        if (this.Client is null)
            return false;

        return await Task<bool>.Factory.StartNew(() =>
        {
            try
            {
                results?.Invoke(this.Client.Queryable<T>().ToList());
            }
            catch
            {
                return false;
            }
            return true;
        });
    }

    public async Task<bool> Query<T>(string tableName, Action<List<T>> results)
    {
        if (this.Client is null)
            return false;

        return await Task<bool>.Factory.StartNew(() =>
        {
            try
            {
                if (string.IsNullOrEmpty(tableName))
                    results?.Invoke(this.Client.Queryable<T>().ToList());
                else
                    results?.Invoke(this.Client.Queryable<T>().AS(tableName).ToList());
            }
            catch
            {
                return false;
            }
            return true;
        });
    }

    public bool Query<T>(string tableName, out List<T>? results)
    {
        results = null;
        if (this.Client is null)
            return false;

        try
        {
            results = string.IsNullOrEmpty(tableName) ?
                      this.Client.Queryable<T>().ToList() :
                      this.Client.Queryable<T>().AS(tableName).ToList();
        }
        catch
        {
            return false;
        }

        return true;
    }

    public bool Query<T>(out List<T>? results)
    {
        results = null;
        if (this.Client is null)
            return false;

        try
        {
            results = this.Client.Queryable<T>().ToList();
        }
        catch
        {
            return false;
        }

        return true;
    }

    public bool Query<T>(string tableName, Expression<Func<T, bool>> expression, out List<T>? results)
    {
        results = default;
        if (this.Client is null)
            return false;

        try
        {
            results = string.IsNullOrEmpty(tableName) ?
                this.Client.Queryable<T>().Where(expression).ToList() :
                this.Client.Queryable<T>().AS(tableName).Where(expression).ToList();
        }
        catch
        {
            return false;
        }


        return true;
    }


    public async Task<bool> Query<T>(Expression<Func<T, bool>> expression, Action<List<T>> results)
    {
        if (this.Client is null)
            return false;

        return await Task<bool>.Factory.StartNew(() =>
            {
                try
                {
                    results?.Invoke(this.Client.Queryable<T>().Where(expression).ToList());
                }
                catch
                {
                    return false;
                }
                return true;
            });
    }

    public async Task<bool> Query<T>(string tableName, Expression<Func<T, bool>> expression, Action<List<T>> results)
    {
        if (this.Client is null)
            return false;

        return await Task<bool>.Factory.StartNew(() =>
          {
              try
              {
                  if (string.IsNullOrEmpty(tableName))
                      results?.Invoke(this.Client.Queryable<T>().Where(expression).ToList());
                  else
                      results?.Invoke(this.Client.Queryable<T>().AS(tableName).Where(expression).ToList());
              }
              catch
              {
                  return false;
              }

              return true;
          });
    }

    public bool Delete<T>(string tableName, Expression<Func<T, bool>> expression) where T : class, new()
    {
        if (this.Client is null)
            return false;
        try
        {
            this.Client.Deleteable<T>().AS(tableName).Where(expression).ExecuteCommand();
        }
        catch
        {
            return false;
        }
        return true;
    }

    public bool InsertOrUpdate<T>(string tableName, T data, Expression<Func<T, bool>> expression, out bool isNewData) where T : class, new()
    {
        isNewData = false;
        if (this.Client is null)
            return false;

        try
        {
            if (string.IsNullOrEmpty(tableName))
            {
                if (this.Client.Updateable(data).Where(expression).ExecuteCommand() == 0)
                {
                    isNewData = true;
                    return this.Insert(data);
                }
            }
            else
            {
                if (this.Client.Updateable(data).Where(expression).AS(tableName).ExecuteCommand() == 0)
                {
                    isNewData = true;
                    return this.Insert(tableName, data);
                }
            }
        }
        catch
        {
            return false;
        }
        return true;
    }


    public bool Update<T>(string tableName, T data) where T : class, new()
    {
        if (this.Client is null)
            return false;

        try
        {
            if (string.IsNullOrEmpty(tableName))
                return this.Client.Updateable(data).ExecuteCommand() != 0;
            else
                return this.Client.Updateable(data).AS(tableName).ExecuteCommand() != 0;
        }
        catch
        {
            return false;
        }
    }

    #region Disopse
    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                this.Client?.Dispose();
                this.Client = null;
            }

            disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
    #endregion
}