using Aitex.Core.RT.Log;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Common;
using System.Data;
using Sicentury.Core.Extensions;

namespace Aitex.Core.RT.DBCore
{
    public class PostgreSQLHelper : IDataBase
    {
        /// <summary>
        /// 数据库名称
        /// </summary>
        private readonly string _dbName;

        /// <summary>
        /// 连接字符串
        /// </summary>
        private readonly string _connString;

        #region Constructors

        public PostgreSQLHelper(string dBName)
        {
            _dbName = dBName;
            _connString = ConfigurationManager.ConnectionStrings["PostgreSQL"].ConnectionString;
        } 

        #endregion

        #region Properties

        public string DBName => _dbName;

        public string ConnectionString => _connString;

        #endregion

        public string GetSqlByNameType(string name, Type type)
        {
            string sql = string.Empty;

            if (type == typeof(int) || type == typeof(ushort) || type == typeof(short))
            {
                sql = $"\"{name}\" integer";
            }
            else if (type == typeof(double) || type == typeof(float))
            {
                sql = $"\"{name}\" real";
            }
            else if (type == typeof(string))
            {
                sql = $"\"{name}\" text";
            }
            else if (type == typeof(DateTime))
            {
                sql = $"\"{name}\" timestamp without time zone";
            }
            else if (type == typeof(bool))
            {
                sql = $"\"{name}\" boolean";
            }
            else if (type == typeof(byte[]))
            {
                sql = $"\"{name}\" bytea";
            }
            else if (type == typeof(Int64))
            {
                sql = $"\"{name}\" bigint";
            }

            return sql;
        }

        /// <summary>
        /// 准备Command
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="conn"></param>
        /// <param name="sql"></param>
        /// <param name="isChangeDB"></param>
        /// <param name="trans"></param>
        /// <param name="columnsName"></param>
        /// <param name="cmdParams"></param>
        private void PrepareCommand(NpgsqlCommand cmd, NpgsqlConnection conn, string sql, bool isChangeDB = true,
            NpgsqlTransaction trans = null, string[] columnsName = null, object[] cmdParams = null)
        {
            if (conn.State != ConnectionState.Open)
            {
                conn.Open();
            }

            //切换数据库
            if (isChangeDB)
            {
                conn.ChangeDatabase(DBName);
            }

            cmd.Connection = conn;
            cmd.CommandText = sql;
            cmd.CommandType = CommandType.Text;

            //事务
            if (trans != null)
            {
                cmd.Transaction = trans;
            }

            //参数
            if (cmdParams != null && columnsName != null && cmdParams.Length != 0 && columnsName.Length == cmdParams.Length)
            {
                for (int i = 0; i < cmdParams.Length; i++)
                {
                    cmd.Parameters.AddWithValue(columnsName[i], cmdParams[i]);
                }
            }
            else if (cmdParams != null)
            {
                foreach (object value in cmdParams)
                {
                    cmd.Parameters.AddWithValue(string.Empty, value);
                }
            }
        }

        /// <summary>
        /// 执行SQL语句
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="isChangeDB"></param>
        /// <returns></returns>
        public int ExecuteNonQuery(string sql, bool isChangeDB = true)
        {
            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    int count = 0;

                    try
                    {
                        PrepareCommand(cmd, conn, sql, isChangeDB);
                        count = cmd.ExecuteNonQuery();
                    }
                    catch (Exception ex)
                    {
                        LOG.Error("执行SQL语句出错," + sql.Truncate(200), ex);
                    }
                    finally
                    {
                        cmd.Parameters.Clear();
                        conn.Close();
                    }

                    return count;
                }
            }
        }

        /// <summary>
        /// 执行带参数SQL语句
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="columnsName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public int ExecuteNonQuery(string sql, string[] columnsName, params object[] args)
        {
            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    int count = 0;

                    try
                    {
                        PrepareCommand(cmd, conn, sql, true, null, columnsName, args);
                        count = cmd.ExecuteNonQuery();
                    }
                    catch (Exception ex)
                    {
                        LOG.Error("执行SQL语句出错," + sql.Truncate(200), ex);
                    }
                    finally
                    {
                        cmd.Parameters.Clear();
                        conn.Close();
                    }

                    return count;
                }
            }
        }

        /// <summary>
        /// 返回查询结果的首行首列数据
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public object ExecuteScalar(string sql, bool isChangeDB = true)
        {
            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    object obj = null;

                    try
                    {
                        PrepareCommand(cmd, conn, sql, isChangeDB);
                        obj = cmd.ExecuteScalar();
                    }
                    catch (Exception ex)
                    {
                        LOG.Error("执行查询出错," + sql, ex);
                    }
                    finally
                    {
                        cmd.Parameters.Clear();
                        conn.Close();
                    }

                    return obj;
                }
            }
        }

        /// <summary>
        /// 查询结果返回NpgsqlDataReader
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public DbDataReader ExecuteReader(string sql)
        {
            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    DbDataReader reader = null;

                    try
                    {
                        PrepareCommand(cmd, conn, sql);
                        reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                    }
                    catch (Exception ex)
                    {
                        LOG.Error("执行查询出错," + sql, ex);
                    }

                    return reader;
                }
            }
        }

        /// <summary>
        /// 查询结果返回DataSet
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public DataSet ExecuteDataSet(string sql)
        {
            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    DataSet ds = new DataSet();
                    try
                    {
                        PrepareCommand(cmd, conn, sql);
                        NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(cmd);
                        adapter.Fill(ds);
                    }
                    catch (Exception ex)
                    {
                        LOG.Error("执行查询出错," + sql, ex);
                    }
                    finally
                    {
                        cmd.Parameters.Clear();
                        conn.Close();
                    }

                    return ds;
                }
            }
        }

        /// <summary>
        /// 查询结果返回DataTable
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public DataTable ExecuteDataTable(string sql)
        {
            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using (NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    DataSet ds = new DataSet();
                    try
                    {
                        PrepareCommand(cmd, conn, sql);
                        NpgsqlDataAdapter adapter = new NpgsqlDataAdapter(cmd);
                        adapter.Fill(ds);
                    }
                    catch (Exception ex)
                    {
                        LOG.Error("执行查询出错," + sql, ex);
                    }
                    finally
                    {
                        cmd.Parameters.Clear();
                        conn.Close();
                    }

                    return ds.Tables.Count > 0 ? ds.Tables[0] : null;
                }
            }
        }

        /// <summary>
        /// 执行事务
        /// </summary>
        /// <param name="sqlList"></param>
        /// <returns></returns>
        public bool ExcuteTransAction(List<string> sqlList)
        {
            bool result = false;

            using (NpgsqlConnection conn = new NpgsqlConnection(_connString))
            {
                using(NpgsqlCommand cmd = new NpgsqlCommand())
                {
                    PrepareCommand(cmd, conn, "");
                    using (NpgsqlTransaction trans = conn.BeginTransaction())
                    {
                        cmd.Transaction = trans;
                        try
                        {
                            //循环
                            foreach (string sql in sqlList)
                            {
                                cmd.Parameters.Clear();
                                cmd.CommandText = sql;
                                int val = cmd.ExecuteNonQuery();
                            }
                            trans.Commit();
                            result = true;
                        }
                        catch (Exception ex)
                        {
                            trans.Rollback();
                            conn.Close();
                            LOG.Error("执行事务出错," + sqlList, ex);

                            result = false;
                        }
                        finally
                        {
                            conn.Close();
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 返回数据库存储文件路径。
        /// </summary>var
        /// <returns></returns>
        public string GetLocalDictionary()
        {
            const string SQL = "show data_directory;";
            using var conn = new NpgsqlConnection(_connString);
            using var cmd = new NpgsqlCommand();
            var path = "";

            try
            {
                PrepareCommand(cmd, conn, SQL);
                var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                if (reader.HasRows && reader.Read())
                    path = reader[0].ToString();
            }
            catch (Exception ex)
            {
                LOG.Error("执行查询出错," + SQL, ex);
            }

            return path;
        }

    }
}
