using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using Repository.Helper;
using Repository.Models;
using Repository.BaseRepository.Interface;
using System.Reflection;
using System.Data;
using System.Text;
using System.Data.SqlClient;
using MySql.Data.MySqlClient;
using Repository.Services.Implement;

namespace Repository.BaseRepository.Implement
{
    public class BaseRepository : IBaseRepository
    {
        protected readonly IDatabaseHelper _databaseHelper;
        protected string UseDB;
        protected IDbConnection con;
        private BackgroundService backgroundService;

        //排除要加入至派送的資料表
        private List<string> exclude_data_delivery = new List<string>()
        {
            "background_service_message_notification_task",
            "background_service_message_notification_task_log",
            "background_service_plan",
            "background_service_task",
            "background_service_task_log",
            "device_import_temp",
            "device_import_ckeck_temp",
            "operation_back_log",
            "operation_log",
            "task_detail",
            "api_earthquake",
            "api_rain",
            "api_sync",
            "api_typhoon",
            "api_weateher",
            "archive_electric_meter_day",
            "archive_electric_meter_hour",
            "archive_electric_meter_month",
            "archive_electric_meter_week"
        };

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

        public IDbConnection GetDbConnection()
        {
            IDbConnection conn;
            if (UseDB == "MSSQL")
            {
                conn = new SqlConnection(this._databaseHelper.GetMSSqlConnectionString());
            }
            else
            {
                conn = new MySqlConnection(this._databaseHelper.GetMySqlConnectionString());
            }

            return conn;
        }

        public string InsertGenerateString(List<string> properties, string table_name)
        {
            var insertQuery = new StringBuilder($"INSERT INTO {table_name} ");

            insertQuery.Append("(");

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

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

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

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

            return insertQuery.ToString();
        }
        public string UpdateGenerateString(List<string> properties, string table_name, string sWhere)
        {
            var updateQuery = new StringBuilder($"UPDATE {table_name} SET ");

            properties.ForEach(property =>
            {
                if (property.Contains("@"))
                {
                    updateQuery.Append($"{property.Replace("@", "")}={property},");
                }
            });

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

            return updateQuery.ToString();
        }


        #region GetAll

        /// <summary>
        /// 取得所有資料(根據條件以及排序)
        /// </summary>
        /// <typeparam name="A"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="sWhere"></param>
        /// <param name="param">Ex: new { status = 1}</param>
        /// <returns></returns>
        public virtual async Task<List<A>> GetAllAsync<A>(string tableName, string sWhere, object param = null, string sOrderBy = "")
        {
            List<A> result;
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    var sql = $"SELECT * FROM {tableName}";
                    if (!string.IsNullOrEmpty(sWhere))
                    {
                        sql += $" WHERE {sWhere}";
                    }

                    if (!string.IsNullOrEmpty(sOrderBy))
                    {
                        sql += $" ORDER BY {sOrderBy}";
                    }

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

        /// <summary>
        /// 根據SQL語句抓取整包資料
        /// </summary>
        /// <typeparam name="A"></typeparam>
        /// <param name="sqlString"></param>
        /// <returns></returns>
        public virtual async Task<List<A>> GetAllAsync<A>(string sqlString, object param = null)
        {
            List<A> result;
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    var sql = sqlString;

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

        /// <summary>
        /// 取得多筆資料的某個欄位變成列表
        /// </summary>
        /// <param name="guid">流水號</param>
        /// <param name="table_name">資料表名稱</param>
        /// <param name="idName">指定欄位名稱的流水號</param>
        /// <param name="selCol">選擇陣列資料庫欄位名稱</param>
        /// <returns></returns>
        public virtual async Task<List<object>> GetAllWithCustomDBNameAndTableAsync(string guid, string table_name, string idName, string selCol)
        {
            List<object> result;
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    var sql = $"SELECT {selCol} FROM {table_name} WHERE {idName} = @Guid";

                    result = (await conn.QueryAsync<object>(sql, new { Guid = guid })).ToList();
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }

        #endregion GetAll

        #region GetOne
        /// <summary>
        /// 取得單一筆資料(排序)
        /// </summary>
        /// <typeparam name="A"></typeparam>
        /// <param name="tableName"></param>
        /// <param name="sWhere"></param>
        /// <param name="param">參數值</param>
        /// <param name="sOrderBy"></param>
        /// <returns></returns>
        public virtual async Task<A> GetOneAsync<A>(string tableName, string sWhere, object param = null, string sOrderBy = "")
        {
            A result;
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    var sql = $"SELECT * FROM {tableName}";

                    if (!string.IsNullOrEmpty(sWhere))
                    {
                        sql += $" WHERE {sWhere}";
                    }

                    if (!string.IsNullOrEmpty(sOrderBy))
                    {
                        sql += $" ORDER BY {sOrderBy}";
                    }

                    result = await conn.QueryFirstOrDefaultAsync<A>(sql, param);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }
        /// <summary>
        /// 取得單一筆資料某一欄位(排序)
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="sWhere"></param>
        /// <param name="selCol">填放欄位</param>
        /// <param name="param">參數值</param>
        /// <param name="sOrderBy"></param>
        /// <returns></returns>
        public virtual async Task<object> GetOneAsync(string tableName, string sWhere, string selCol, object param = null, string sOrderBy = "")
        {
            object result;
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    var sql = $"SELECT {selCol} FROM {tableName}";
                    if (!string.IsNullOrEmpty(sWhere))
                    {
                        sql += $" WHERE {sWhere}";
                    }
                    if (!string.IsNullOrEmpty(sOrderBy))
                    {
                        sql += $" ORDER BY {sOrderBy}";
                    }

                    result = await conn.QueryFirstOrDefaultAsync<object>(sql, param);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }

        /// <summary>
        /// 取得單一筆資料(根據自訂SQL, 自訂參數)
        /// </summary>
        /// <param name="sqlString"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public virtual async Task<A> GetOneAsync<A>(string sqlString, object param = null)
        {
            A result;
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    var sql = sqlString;

                    result = await conn.QueryFirstOrDefaultAsync<A>(sql, param);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                return result;
            }
        }
        #endregion GetOne

        #region DeleteOne (軟刪除)

        /// <summary>
        /// 透過guid,軟刪除單一筆資料
        /// UPDATE {tableName} SET deleted = 1 WHERE {idName} = @Guid
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        public virtual async Task DeleteOne(string guid, string tableName, string idName)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"UPDATE {tableName} SET deleted = 1 WHERE {idName} = @Guid";

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(tableName))
                        {
                            Dictionary<string, object> dict = new Dictionary<string, object>()
                            {
                                { idName, guid},
                                { "@deleted", 1},
                            };

                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", tableName, "delete", dict, null, guid);
                        }

                        await conn.ExecuteAsync(sql, new { Guid = guid }, trans);

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

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

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(table_name))
                        {
                            Dictionary<string, object> dict = new Dictionary<string, object>()
                            {
                                { idName, guid},
                                { "@deleted", 1},
                            };

                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", $"{db_name}.{table_name}", "delete", dict, null, guid);
                        }

                        await conn.ExecuteAsync(sql, new { Guid = guid }, trans);

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

        #endregion DeleteOne (軟刪除)

        #region PurgeOne (硬刪除)
        /// <summary>
        /// 根據Where條件進行刪除
        /// </summary>
        /// <param name="table_name"></param>
        /// <param name="sWhere"></param>
        /// <returns></returns>
        public virtual async Task PurgeOneByGuidWithCustomDBNameAndTable(string table_name, string sWhere)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"DELETE FROM {table_name} WHERE {sWhere}";

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(table_name))
                        {
                            Dictionary<string, object> dict = new Dictionary<string, object>();

                            //判斷是否有and的情況
                            var swhere_splits_and = sWhere.Split("and");
                            foreach (var sWhere_and in swhere_splits_and)
                            {
                                var swhere_split = sWhere_and.Split('=');
                                if (swhere_split.Length > 0)
                                {
                                    dict.Add(swhere_split[0].Trim(), swhere_split[1].Replace("'", "").Trim());
                                }
                            }

                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", table_name, "purge", dict);
                        }

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

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }
        /// <summary>
        /// 指定單一欄位,實際刪除單一筆資料
        /// </summary>
        /// <param name="guid"></param>
        /// <param name="table_name"></param>
        /// <param name="idName"></param>
        /// <returns></returns>
        public virtual async Task PurgeOneAsync(string guid, string table_name, string idName)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        var sql = $"DELETE FROM {table_name} WHERE {idName} = @Guid";

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(table_name))
                        {
                            Dictionary<string, object> dict = new Dictionary<string, object>()
                            {
                                { idName, guid},
                            };

                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", table_name, "purge", dict, null, guid);
                        }

                        await conn.ExecuteAsync(sql, new { Guid = guid }, trans);

                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }
        #endregion PurgeOne (硬刪除)

        #region AddMuti
        /// <summary>
        /// 新增Table多筆資料
        /// </summary>
        /// <param name="dict"></param>
        /// <param name="Table_name"></param>
        /// <returns></returns>
        public async Task AddMutiByCustomTable(List<Dictionary<string, object>> dict, string Table_name)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        List<string> properties = dict[0].Keys.ToList();
                        string sql = InsertGenerateString(properties, Table_name);

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

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(Table_name))
                        {
                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", Table_name, "insert_list", dict);
                        }

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

        #region AddOne
        /// <summary>
        /// 新增table一筆資料
        /// </summary>
        /// <param name="dict">新增資料庫名稱以及值</param>
        /// <param name="Table_name">資料表名稱</param>
        /// <returns></returns>
        public async Task AddOneByCustomTable(Dictionary<string, object> dict, string Table_name)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        List<string> properties = dict.Keys.ToList();
                        string sql = InsertGenerateString(properties, Table_name);
                        await conn.ExecuteAsync(sql, dict, trans);

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(Table_name))
                        {
                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", Table_name, "insert", dict);
                        }

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

        #region UpdateOne
        /// <summary>
        /// 更新Table一筆資料
        /// </summary>
        /// <param name="dict">更新資料庫名稱以及值</param>
        /// <param name="Table_name">資料表名稱</param>
        /// <param name="sWhere">Where條件</param>
        /// <returns></returns>
        public async Task UpdateOneByCustomTable(Dictionary<string, object> dict, string Table_name, string sWhere)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        List<string> properties = dict.Keys.ToList();
                        string sql = UpdateGenerateString(properties, Table_name, sWhere);

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

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(Table_name))
                        {
                            //判斷是否有and的情況
                            var swhere_splits_and = sWhere.Split("and");
                            foreach(var sWhere_and in swhere_splits_and)
                            {
                                var swhere_split = sWhere_and.Split('=');
                                if (swhere_split.Length > 0)
                                {
                                    dict.Add(swhere_split[0].Trim(), swhere_split[1].Replace("'", "").Trim());
                                }
                            }

                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", Table_name, "update", dict);
                        }

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

        #region UpdateList
        /// <summary>
        /// 更新Table多筆筆資料
        /// </summary>
        /// <param name="dict">更新資料庫名稱以及值</param>
        /// <param name="Table_name">資料表名稱</param>
        /// <param name="sWhere">Where條件</param>
        /// <returns></returns>
        public async Task UpdateListByCustomTable(List<Dictionary<string, object>> dicts, string Table_name, string sWhere)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        List<string> properties = dicts.First().Keys.ToList();
                        string sql = UpdateGenerateString(properties, Table_name, sWhere);

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

                        if (UseDB == "MSSQL" && !exclude_data_delivery.Contains(Table_name))
                        {
                            backgroundService = new BackgroundService(conn, trans);
                            await backgroundService.AddTask("", "", Table_name, "update_list", dicts);
                        }

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


        /// <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 = GetDbConnection())
            {
                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;
        }

        /// <summary>
        /// 取資料表當前優先序
        /// <para>SELECT Priority FROM {Table_name}  WHERE {where} ORDER BY Priority DESC</para>
        /// </summary>
        /// <param name="Table_name"></param>
        /// <param name="where"></param>
        /// <returns></returns>
        public async Task<int> GetCurrentPriority(string Table_name, string where = "")
        {
            int Num = 0;
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    var sql = @$"SELECT Priority FROM {Table_name}";
                    if (!string.IsNullOrEmpty(where))
                    {
                        sql += $" WHERE {where}";
                    }
                    sql += " ORDER BY Priority DESC";
                    Num = await conn.QueryFirstOrDefaultAsync<int>(sql);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }

            return Num;
        }

        /// <summary>
        /// 透過id來搜尋此筆資料是否存在
        /// </summary>
        /// <param name="id">id值</param>
        /// <param name="Table_name">table名稱</param>
        /// <param name="id_name">id名稱</param>
        /// <returns></returns>
        public async Task<Boolean> HasExistsWithGuid(string guid, string Table_name, string id_name)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                Boolean hasExists = false;
                conn.Open();
                try
                {
                    var sql = $"SELECT * FROM {Table_name} WHERE {id_name} = @Guid";

                    var result = await conn.QueryFirstOrDefaultAsync<string>(sql, new { Guid = guid });

                    if (result != null)
                    {
                        hasExists = true;
                    }
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }

                return hasExists;
            }
        }
        /// <summary>
        /// 根據SQL條件搜尋此筆資料是否存在
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public async Task<Boolean> HasExistsWithParam(string sql, object param = null)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                Boolean hasExists = false;
                conn.Open();
                try
                {
                    var result = await conn.QueryFirstOrDefaultAsync<object>(sql, param);

                    if (result != null)
                    {
                        hasExists = true;
                    }
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }

                return hasExists;
            }
        }

        /// <summary>
        /// 直接執行SQL語句
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public async Task ExecuteSql(string sql, object param = null)
        {
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    var result = await conn.ExecuteAsync(sql, param);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }
        }

        #region AddOneReturnId
        /// <summary>
        /// 新增table一筆資料
        /// </summary>
        /// <param name="dict">新增資料庫名稱以及值</param>
        /// <param name="Table_name">資料表名稱</param>
        /// <returns></returns>
        public async Task<int> AddOneByCustomTableReturnId(Dictionary<string, object> dict, string Table_name, bool returnId = true)
        {
            var id = 0;
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (var trans = conn.BeginTransaction())
                {
                    try
                    {
                        List<string> properties = dict.Keys.ToList();
                        string sql = InsertGenerateString(properties, Table_name);
                        if (returnId)
                        {
                            sql += " SELECT SCOPE_IDENTITY()";
                        }
                        id = await conn.QueryFirstOrDefaultAsync<int>(sql, dict, trans);
                        //await conn.ExecuteAsync(sql, dict, trans);
                        trans.Commit();
                    }
                    catch (Exception exception)
                    {
                        trans.Rollback();
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }

            return id;
        }

        #endregion AddOneReturnId
    }
}