using Aitex.Common.Util;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Aitex.Core.RT.DBCore
{
    public class DataBaseManager : IDataBaseManager
    {
        private string _dbName = "sicdb";

        private IDataBase _dataBase;

        private string _sqlFile = PathManager.GetCfgDir() + "DBModel.sql";

        private PeriodicJob _monitorJob;

        private FixSizeQueue<string> _sqlQueue = new FixSizeQueue<string>(1000);

        public void Initialize()
        {
            DB.Manager = this;

            _dataBase = new PostgreSQLHelper(_dbName);

            CreateDataBase();

            ExecuteScript(_sqlFile);

            UpdateTable();

            _monitorJob = new PeriodicJob(100, Monitor,"DataBaseManager", true);

        }

        /// <summary>
        /// 创建数据库
        /// </summary>
        public void CreateDataBase()
        {
            try
            {
                object obj = _dataBase.ExecuteScalar($"select datname from pg_catalog.pg_database where datname='{_dbName}'", false);
                if (obj == null)
                {
                    string cmdText = $"CREATE DATABASE {_dbName} WITH OWNER = postgres ENCODING = 'UTF8' TABLESPACE = pg_default CONNECTION LIMIT = -1";

                    _dataBase.ExecuteNonQuery(cmdText, false);
                }
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message, ex);
            }
        }

        /// <summary>
        /// 执行SQL脚本
        /// </summary>
        /// <param name="sqlFile"></param>
        private void ExecuteScript(string sqlFile)
        {
            if (string.IsNullOrEmpty(sqlFile) || !File.Exists(sqlFile))
            {
                LOG.Warning("没有更新Sql数据库，文件：" + sqlFile);
                return;
            }

            try
            {
                using StreamReader streamReader = new StreamReader(sqlFile);
                string cmdText = streamReader.ReadToEnd();
                _dataBase.ExecuteNonQuery(cmdText);
                LOG.Info("Database updated by sql file " + sqlFile);
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message, ex);
            }
        }

        /// <summary>
        /// 创建Table
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columns"></param>
        /// <param name="addPID"></param>
        /// <param name="primaryKey"></param>
        public void CreateTable(string tableName, Dictionary<string, Type> columns, string primaryKey, Type primaryKeyType)
        {
            string sql = "";

            DbDataReader dataReader = _dataBase.ExecuteReader($"select column_name from information_schema.columns where table_name = '{tableName}'");
            if (!dataReader.HasRows)
            {
                if (!string.IsNullOrEmpty(primaryKey) && primaryKeyType != null && !columns.Keys.Contains(primaryKey))
                {
                    sql += _dataBase.GetSqlByNameType(primaryKey, primaryKeyType);
                    sql += ',';
                }

                //列名
                foreach (KeyValuePair<string, Type> column in columns)
                {
                    sql += _dataBase.GetSqlByNameType(column.Key, column.Value);
                    sql += ',';
                }

                if (string.IsNullOrEmpty(sql))
                {
                    return;
                }

                //主键
                if (!string.IsNullOrEmpty(primaryKey))
                {
                    sql += $"CONSTRAINT \"{tableName}_pkey\" PRIMARY KEY (\"{primaryKey}\" )";
                }
                else
                {
                    if (!string.IsNullOrEmpty(sql) && sql.LastIndexOf(',') == sql.Length - 1)
                    {
                        sql = sql.Remove(sql.Length - 1);
                    }
                }

                sql = $"CREATE TABLE \"{tableName}\"({sql}) WITH ( OIDS=FALSE );";

                sql += $"ALTER TABLE  \"{tableName}\" OWNER TO postgres;";

                //表的Select权限授权给postgres用户
                sql += $"GRANT SELECT ON TABLE \"{tableName}\" TO postgres;";

                _dataBase.ExecuteNonQuery(sql);
            }
        }

        public void CreateTableIndex(string table, string index, string sql)
        {
            DbDataReader dataReader = _dataBase.ExecuteReader("select* from pg_indexes where tablename='" + table + "' and indexname = '" + index + "'");
            if (!dataReader.HasRows)
            {
                ExecuteNonQuery(sql);
            }
        }

        private void UpdateTable()
        {
            CreateTableIndex("recipe_step_data", "recipe_step_data_idx1", "CREATE INDEX recipe_step_data_idx1 ON public.recipe_step_data USING btree(\"process_data_guid\", \"step_number\");");

            CreateTableIndex("step_fdc_data", "step_fdc_data_idx1", "CREATE INDEX step_fdc_data_idx1 ON public.step_fdc_data USING btree(\"process_data_guid\", \"step_number\");");

            CreateTableIndex("lot_wafer_data", "lot_wafer_data_idx1", "CREATE INDEX lot_wafer_data_idx1 ON public.lot_wafer_data USING btree(lot_data_guid COLLATE pg_catalog.\"default\", wafer_data_guid COLLATE pg_catalog.\"default\");");

            //AddTableColumn("carrier_data", new Dictionary<string, Type>
            //{
            //    {"rootname",typeof(string) },
            //    {"recipename",typeof(string)},
            //    {"filedetail",typeof(byte[]) }
            //});

            //AlterTableColumn("carrier_data", new Dictionary<string, string>
            //{
            //    {"rootname","rootname1" },
            //    {"recipename","recipename1"},
            //    {"filedetail","filedetail1" }
            //});

            //DeleteTableColumn("carrier_data", new List<string>
            //{
            //    "rootname1",
            //    "recipename1",
            //    "filedetail1"
            //});
        }

        /// <summary>
        /// 新增Table列名
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columns"></param>
        public void AddTableColumn(string tableName, Dictionary<string, Type> columns)
        {
            string sql = $"select column_name from information_schema.columns where table_name = '{tableName}';";
            DbDataReader dataReader = _dataBase.ExecuteReader(sql);
            string cmdText = string.Empty;
            List<string> list = new List<string>();
            while (dataReader.Read())
            {
                for (int i = 0; i < dataReader.FieldCount; i++)
                {
                    list.Add(dataReader[i].ToString());
                }
            }

            if (list.Count > 0)
            {
                foreach (KeyValuePair<string, Type> column in columns)
                {
                    if (!list.Contains(column.Key))
                    {
                        cmdText = ((!(column.Value == typeof(bool))) ? ((!(column.Value == typeof(double)) && !(column.Value == typeof(float))) ? ((!(column.Value == typeof(DateTime))) ? ((!(column.Value == typeof(int)) && !(column.Value == typeof(ushort)) && !(column.Value == typeof(short))) ? (cmdText + string.Format("ALTER TABLE \"{0}\" ADD COLUMN \"{1}\" {2};", tableName, column.Key, "text")) : (cmdText + string.Format("ALTER TABLE \"{0}\" ADD COLUMN \"{1}\" {2};", tableName, column.Key, "integer"))) : (cmdText + string.Format("ALTER TABLE \"{0}\" ADD COLUMN \"{1}\" {2};", tableName, column.Key, "timestamp without time zone"))) : (cmdText + string.Format("ALTER TABLE \"{0}\" ADD COLUMN \"{1}\" {2};", tableName, column.Key, "real"))) : (cmdText + string.Format("ALTER TABLE \"{0}\" ADD COLUMN \"{1}\" {2};", tableName, column.Key, "Boolean")));
                    }
                }

                if (!string.IsNullOrEmpty(cmdText))
                {
                    _dataBase.ExecuteNonQuery(cmdText);
                }
            }
        }

        /// <summary>
        /// 更新Table列名
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columns"></param>
        public void AlterTableColumn(string tableName, Dictionary<string, string> columns)
        {
            string sql = $"select column_name from information_schema.columns where table_name = '{tableName}';";
            DbDataReader dataReader = _dataBase.ExecuteReader(sql);
            string cmdText = string.Empty;
            List<string> list = new List<string>();
            while (dataReader.Read())
            {
                for (int i = 0; i < dataReader.FieldCount; i++)
                {
                    list.Add(dataReader[i].ToString());
                }
            }

            if (list.Count > 0)
            {
                foreach (KeyValuePair<string, string> column in columns)
                {
                    if (list.Contains(column.Key))
                    {
                        cmdText += $"ALTER TABLE \"{tableName}\" RENAME COLUMN \"{column.Key}\" TO {column.Value};";
                    }
                }

                if (!string.IsNullOrEmpty(cmdText))
                {
                    _dataBase.ExecuteNonQuery(cmdText);
                }
            }
        }

        public void DeleteTableColumn(string tableName, List<string> columns)
        {
            string sql = $"select column_name from information_schema.columns where table_name = '{tableName}';";
            DbDataReader dataReader = _dataBase.ExecuteReader(sql);
            string cmdText = string.Empty;
            List<string> list = new List<string>();
            while (dataReader.Read())
            {
                for (int i = 0; i < dataReader.FieldCount; i++)
                {
                    list.Add(dataReader[i].ToString());
                }
            }

            if (list.Count > 0)
            {
                foreach (string column in columns)
                {
                    if (list.Contains(column))
                    {
                        cmdText += $"ALTER TABLE \"{tableName}\" DROP COLUMN \"{column}\";";
                    }
                }

                if (!string.IsNullOrEmpty(cmdText))
                {
                    _dataBase.ExecuteNonQuery(cmdText);
                }
            }
        }

        public string GetSqlByNameType(string name, Type type)
        {
            return _dataBase.GetSqlByNameType(name, type);
        }

        public void InsertSql(string sql)
        {
            _sqlQueue.Enqueue(sql);
        }

        public DbDataReader ExecuteReader(string sql)
        {
            return _dataBase.ExecuteReader(sql);
        }

        public int ExecuteNonQuery(string sql, bool isChangeDB = true)
        {
            return _dataBase.ExecuteNonQuery(sql, isChangeDB);
        }

        public int ExecuteNonQuery(string sql, string[] columnsName, params object[] args)
        {
            return _dataBase.ExecuteNonQuery(sql, columnsName, args);
        }

        public object ExecuteScalar(string sql, bool isChangeDB = true)
        {
            return _dataBase.ExecuteScalar(sql, isChangeDB);
        }

        public DataSet ExecuteDataSet(string sql)
        {
            return _dataBase.ExecuteDataSet(sql);
        }

        public DataTable ExecuteDataTable(string sql)
        {
            return _dataBase.ExecuteDataTable(sql);
        }

        public bool ExcuteTransAction(List<string> sqlList)
        {
            return _dataBase.ExcuteTransAction(sqlList);
        }

        private bool Monitor()
        {
            try
            {
                if (_sqlQueue.Count == 0)
                {
                    return true;
                }

                if (_sqlQueue.TryDequeue(out string sql))
                {
                    _dataBase.ExecuteNonQuery(sql);
                }

                return true;
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message, ex);
                return true;
            }
        }

        public void Terminate()
        {
            _monitorJob?.Stop();
            _monitorJob = null;
        }

        /// <summary>
        /// DataTable转成List
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public IList<T> DataTableToList<T>(DataTable dt) where T : new()
        {
            IList<T> list = new List<T>();
            Type type = typeof(T);

            foreach (DataRow dr in dt.Rows)
            {
                T t = new T();
                PropertyInfo[] properties = t.GetType().GetProperties();
                foreach (PropertyInfo pi in properties)
                {
                    if (dt.Columns.Contains(pi.Name))
                    {
                        if (!pi.CanWrite)
                        {
                            continue;
                        }

                        object value = dr[pi.Name];
                        if (value != DBNull.Value)
                        {
                            pi.SetValue(t, value, null);
                        }
                    }
                }

                list.Add(t);
            }

            return list;
        }

        /// <summary>
        /// DataTable转成T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dt"></param>
        /// <returns></returns>
        public T DataTableToObject<T>(DataTable dt) where T : new()
        {
            T t = new T();

            if (dt.Rows == null || dt.Rows.Count == 0)
            {
                return t;
            }

            DataRow dr = dt.Rows[0];

            PropertyInfo[] properties = t.GetType().GetProperties();
            foreach (PropertyInfo pi in properties)
            {
                if (dt.Columns.Contains(pi.Name))
                {
                    if (!pi.CanWrite)
                    {
                        continue;
                    }

                    object value = dr[pi.Name];
                    if (value != DBNull.Value)
                    {
                        pi.SetValue(t, value, null);
                    }
                }
            }

            return t;
        }

        public string GetLocalDictionary()
        {
            return _dataBase.GetLocalDictionary();
        }
    }
}
