
using Dapper;
using SolarPower.Helper;
using SolarPower.Models;
using SolarPower.Repository.Interface;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SolarPower.Repository.Implement
{
    public class RepositoryBase<T> : IRepositoryBase<T> where T : class
    {
        protected readonly IDatabaseHelper _databaseHelper;
        protected string tableName;
        protected IEnumerable<PropertyInfo> GetProperties = typeof(T).GetProperties();

        public RepositoryBase(IDatabaseHelper databaseHelper)
        {
            this._databaseHelper = databaseHelper;
        }

        /// <summary>
        /// 新增資料
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task<int> AddAsync(T entity, List<string> properties)
        {
            int count;
            using (IDbConnection conn = _databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        string sql = GenerateInsertQuery(properties);

                        count = await conn.ExecuteAsync(sql, entity, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }

                return count;
            }
        }

        /// <summary>
        /// 新增單一筆資料
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="properties"></param>
        /// <returns></returns>
        public virtual async Task<int> AddOneAsync(T entity, List<string> properties)
        {
            int id;
            using (IDbConnection conn = _databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        string sql = GenerateInsertQuery(properties);

                        sql += "SELECT LAST_INSERT_ID();";

                        id = (await conn.QueryAsync<int>(sql, entity, trans)).Single();

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }

                return id;
            }
        }


        /// <summary>
        /// 透過Id，軟刪除單一筆資料
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task DeleteOne(int id)
        {
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"UPDATE {tableName} SET deleted = 1 WHERE id = @Id";

                        await conn.ExecuteAsync(sql, new { Id = id }, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }

                }
            }
        }

        /// <summary>
        /// 透過table_name、Id，軟刪除指定資料表的單一筆資料
        /// </summary>
        /// <param name="id"></param>
        /// <param name="table_name"></param>
        /// <returns></returns>
        public virtual async Task DeleteOneByIdWithCustomTable(int id, string table_name)
        {
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"UPDATE {table_name} SET Deleted = 1 WHERE Id = @Id";

                        await conn.ExecuteAsync(sql, new { Id = id }, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }

                }
            }
        }

        /// <summary>
        /// 透過Id、db_name、table_name，刪除指定的資料庫之資料表的一筆資料
        /// </summary>
        /// <param name="id"></param>
        /// <param name="db_name"></param>
        /// <param name="table_name"></param>
        /// <returns></returns>
        public virtual async Task DeleteOneByIdWithCustomDBNameAndTable(int id, string db_name, string table_name)
        {
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"UPDATE {db_name}.{table_name} SET Deleted = 1 WHERE Id = @Id";

                        await conn.ExecuteAsync(sql, new { Id = id }, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }

        /// <summary>
        /// 透過Id、db_name、table_name，硬刪除指定的資料庫之資料表的一筆資料
        /// </summary>
        /// <param name="id"></param>
        /// <param name="db_name"></param>
        /// <param name="table_name"></param>
        /// <returns></returns>
        public virtual async Task PurgeOneByIdWithCustomDBNameAndTable(int id, string db_name, string table_name)
        {
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"DELETE FROM {db_name}.{table_name} WHERE id = @Id";

                        await conn.ExecuteAsync(sql, new { Id = id }, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }


        /// <summary>
        /// 取得所有資料
        /// </summary>
        /// <returns></returns>
        public virtual async Task<List<T>> GetAllAsync()
        {
            List<T> result;
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                try
                {
                    var sql = $"SELECT * FROM {tableName}";

                    result = (await conn.QueryAsync<T>(sql)).ToList();
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }

        /// <summary>
        /// 取得單一筆資料
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<T> GetOneAsync(int id)
        {
            T result;
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                try
                {
                    var sql = $"SELECT * FROM {tableName} WHERE id = @Id";

                    result = await conn.QueryFirstOrDefaultAsync<T>(sql, new { Id = id });
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }

        /// <summary>
        /// 取得單一筆資料(客製化資料庫及資料表)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task<A> GetOneWithCustomDBNameAndTableAsync<A>(int id, string db_name, string table_name)
        {
            A result;
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                try
                {
                    var sql = $"SELECT * FROM {db_name}.{table_name} WHERE id = @Id";

                    result = await conn.QueryFirstOrDefaultAsync<A>(sql, new { Id = id });
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }

        /// <summary>
        /// 透過Id，實際刪除單一筆資料
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual async Task PurgeOneAsync(int id)
        {
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"DELETE FROM {tableName} WHERE id = @Id";

                        await conn.ExecuteAsync(sql, new { Id = id }, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }

        /// <summary>
        /// 修改資料
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task Update(T entity, List<string> properties)
        {
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = GenerateUpdateQuery(properties);

                        await conn.ExecuteAsync(sql, entity, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }

        /// <summary>
        /// 透過name list，取得指定的多筆設定變數
        /// </summary>
        /// <param name="names"></param>
        /// <returns>回傳為字典格式</returns>
        public virtual async Task<Dictionary<string, string>> GetDictVariableByNames(List<string> names)
        {
            Dictionary<string, string> dict;
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                try
                {
                    var sql = $"SELECT * FROM variable WHERE Name IN @Names";

                    var result = (await conn.QueryAsync<Variable>(sql, new { Names = names })).ToList();

                    dict = result.ToDictionary(x => x.Name, x => x.Value);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return dict;
            }
        }

        /// <summary>
        /// 透過name，取得單一設定變數
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual async Task<string> GetOneVariableByName(string name)
        {
            string result;
            using (IDbConnection conn = this._databaseHelper.GetConnection())
            {
                try
                {
                    var sql = $"SELECT Value FROM variable WHERE Name = @Name";

                    result = await conn.QueryFirstOrDefaultAsync<string>(sql, new { Name = name });
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }

        /// <summary>
        /// 產生Insert語句
        /// </summary>
        /// <returns></returns>
        protected string GenerateInsertQuery(List<string> properties)
        {
            var insertQuery = new StringBuilder($"INSERT INTO {tableName} ");

            insertQuery.Append("(");

            properties.ForEach(prop => { insertQuery.Append($"{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(") VALUES (");

            properties.ForEach(prop => { insertQuery.Append($"@{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(");");

            return insertQuery.ToString();
        }

        /// <summary>
        /// 產生Insert語句，可選擇自己要加入資料表
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="table_name">欲新增至目標資料表</param>
        /// <returns></returns>
        protected string GenerateInsertQueryWithCustomTable(List<string> properties, string table_name)
        {
            var insertQuery = new StringBuilder($"INSERT INTO `{table_name}` ");

            insertQuery.Append("(");

            properties.ForEach(prop => { insertQuery.Append($"{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(") VALUES (");

            properties.ForEach(prop => { insertQuery.Append($"@{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(");");

            return insertQuery.ToString();
        }

        /// <summary>
        /// 產生Insert語句，可選擇自己要加入資料庫及資料表
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="db_name">欲新增至目標資料庫</param>
        /// <param name="table_name">欲新增至目標資料表</param>
        /// <returns></returns>
        protected string GenerateInsertQueryWithCustomDBNameAndTable(List<string> properties, string db_name, string table_name)
        {
            var insertQuery = new StringBuilder($"INSERT INTO `{db_name}`.`{table_name}` ");

            insertQuery.Append("(");

            properties.ForEach(prop => { insertQuery.Append($"`{table_name}`.{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(") VALUES (");

            properties.ForEach(prop => { insertQuery.Append($"@{prop},"); });

            insertQuery
                .Remove(insertQuery.Length - 1, 1)
                .Append(");");

            return insertQuery.ToString();
        }

        /// <summary>
        /// 產生Update語句
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        protected string GenerateUpdateQuery(List<string> properties)
        {
            var updateQuery = new StringBuilder($"UPDATE {tableName} SET ");

            properties.ForEach(property =>
            {
                if (!property.Equals("Id"))
                {
                    updateQuery.Append($"{property}=@{property},");
                }
            });

            updateQuery.Remove(updateQuery.Length - 1, 1); //remove last comma
            updateQuery.Append(" WHERE id = @Id");

            return updateQuery.ToString();
        }

        /// <summary>
        /// 產生Update語句，可選擇自己要加入資料表
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="table_name">欲新增至目標資料表</param>
        /// <returns></returns>
        protected string GenerateUpdateQueryWithCustomTable(List<string> properties, string table_name)
        {
            var updateQuery = new StringBuilder($"UPDATE {table_name} SET ");

            properties.ForEach(property =>
            {
                if (!property.Equals("Id"))
                {
                    updateQuery.Append($"{property}=@{property},");
                }
            });

            updateQuery.Remove(updateQuery.Length - 1, 1); //remove last comma
            updateQuery.Append(" WHERE id = @Id");

            return updateQuery.ToString();
        }
        /// <summary>
        /// 產生Update語句，可選擇自己要加入資料表，填入想要的WHERE條件
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="table_name"></param>
        /// <param name="WHERE"></param>
        /// <returns></returns>
        protected string GenerateUpdateQueryWithCustomTableAndWHERE(List<string> properties, string table_name,string WHERE)
        {
            var updateQuery = new StringBuilder($"UPDATE {table_name} SET ");

            properties.ForEach(property =>
            {
                if (!property.Equals("Id"))
                {
                    updateQuery.Append($"{property}=@{property},");
                }
            });

            updateQuery.Remove(updateQuery.Length - 1, 1); //remove last comma
            updateQuery.Append($" WHERE {WHERE}");

            return updateQuery.ToString();
        }

        protected string GenerateUpdateQueryWithCustomDBNameAndTable(List<string> properties, string db_name, string table_name)
        {
            var updateQuery = new StringBuilder($"UPDATE {db_name}.{table_name} SET ");

            properties.ForEach(property =>
            {
                if (!property.Equals("Id"))
                {
                    updateQuery.Append($"{table_name}.{property}=@{property},");
                }
            });

            updateQuery.Remove(updateQuery.Length - 1, 1); //remove last comma
            updateQuery.Append(" WHERE id = @Id");

            return updateQuery.ToString();
        }
        /// <summary>
        /// 取資料庫當前流水號
        /// </summary>
        /// <param name="Table_name"></param>
        /// <param name="where"></param>
        /// <returns></returns>
        public async Task<string> GetCurrentSerialNumber(string Table_name, string where = "")
        {
            string Num;
            using IDbConnection conn = _databaseHelper.GetConnection();
            conn.Open();
            try
            {
                var sql = @$"SELECT SerialNumber FROM {Table_name}";
                if (!string.IsNullOrEmpty(where))
                {
                    sql += $" WHERE {where}";
                }
                sql += " ORDER BY SerialNumber DESC";
                Num = await conn.QueryFirstOrDefaultAsync<string>(sql);
            }
            catch (Exception exception)
            {
                throw exception;
            }
            finally
            {
                conn.Close();
            }
            return Num;
        }
    }
}
