using Tiobon.Core.OPS.Tool.OPS.Tool.Helper;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Threading;

namespace Tiobon.Core.OPS.Tool.OPS.Tool.View
{
    public partial class Frm_DBCompare_Step3 : Form
    {
        #region 初始化
        string m_ConnStr = "";
        string m_Version = "";
        string m_HdisConnStr = "";
        SSHHelper sSH;
        bool isLinux = Const.config.system == "Linux";
        string installpath = Const.config.install_item_dir;
        public Frm_DBCompare_Step3(string hdisConnStr, string connStr, string ver, SSHHelper sSH, FormWindowState windowState)
        {
            InitializeComponent();
            m_ConnStr = connStr;
            m_HdisConnStr = hdisConnStr;
            m_Version = ver;
            this.sSH = sSH;
            LoadConf();
            timer_Main.Start();
            WindowState = windowState;
            this.txt_fname.Text = $"{Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)}\\系统数据比对结果_{DateTime.Now.ToString("yyyy_MM_dd")}.sql";
        }

        bool m_Stop = false;
        bool b_Closed = false;
        private void Frm_DBCompare_Step3_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (b_Closed)
                return;
            m_Stop = true;
            if (isLinux)
            {
                try
                {
                    //写入数据库版本 
                    this.sSH.Excute_Longcmd_NoError($"sudo echo {m_Version} > /home/{sSH.user}/ihdis/produse/DbVersion");
                }
                catch { }
                bool isRestart = MessageBox.Show("是否重新启动服务！", "提示信息！", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes;
                if (isRestart)
                {
                    try
                    {
                        string tempCommand = $"cd /home/{sSH.user}/ihdis/produse;sudo docker-compose docker-compose.yml restart";
                        Task.Factory.StartNew(() =>
                        {
                            this.sSH.Excute_Longcmd_NoError(tempCommand);
                        });
                    }
                    catch
                    {

                    }
                }
            }
            else
            {
                bool isRestart = MessageBox.Show("是否重新启动服务！", "提示信息！", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes;
                if (isRestart)
                {
                    CmdHelper.RunCmd($"{installpath.Split(':')[0].Trim()}: \r\ncd {$"{installpath}/"}" +
                     $"\r\ndocker-compose down" +
                      $"\r\n", out _);

                    Thread.Sleep(1000 * 10);

                    CmdHelper.RunCmd($"{installpath.Split(':')[0].Trim()}: \r\ncd {$"{installpath}/"}" +
                      $"\r\ndocker-compose up -d \r\nexit", out _);
                    Thread.Sleep(1000 * 10);
                }
            }

        }
        #endregion

        #region 加载配置项
        CompareTables m_CompareTablesConf;
        private void LoadConf()
        {
            try
            {
                string fname = AppDomain.CurrentDomain.BaseDirectory + @"Config\CompareConfig.json";
                if (File.Exists(fname))
                    m_CompareTablesConf = Newtonsoft.Json.JsonConvert.DeserializeObject<CompareTables>(File.ReadAllText(fname));
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }
        #endregion

        #region 日志操作
        private static List<string> m_LogsTemp = new List<string>();
        private void timer_Main_Tick(object sender, EventArgs e)
        {
            try
            {
                lock (m_Logs)
                {
                    m_LogsTemp.Clear();
                    m_Logs.ForEach(log => m_LogsTemp.Add(log));
                    m_Logs.Clear();
                }

                if (m_LogsTemp.Count > 0)
                {
                    if (this.lb_Logger.Items.Count > 2000)
                        this.lb_Logger.Items.Clear();
                    m_LogsTemp.ForEach(log => this.lb_Logger.Items.Add(log));
                    this.lb_Logger.TopIndex = this.lb_Logger.Items.Count - 1;
                }

                lock (m_LockContent)
                {
                    if (!string.IsNullOrEmpty(m_Content))
                    {
                        if (m_Content == "clear")
                        {
                            this.rtb_Sql.Text = string.Empty;
                        }
                        else
                        {
                            this.rtb_Sql.Text = m_Content;
                        }
                        m_Content = string.Empty;
                    }
                }

                lock (m_LockProgressBar)
                {
                    progressBar_Main.Maximum = m_PBMax;
                    progressBar_Main.Value = m_PBValue;
                }
            }
            catch { }
        }
        private static List<string> m_Logs = new List<string>();
        private void SendLog(string oprator, string msg)
        {
            lock (m_Logs)
            {
                Const.write_log($"[{oprator}] {msg}");
                if (string.IsNullOrEmpty(oprator))
                    m_Logs.Add($"{DateTime.Now.ToString("HH:mm:ss")} {msg}");
                else
                    m_Logs.Add($"{DateTime.Now.ToString("HH:mm:ss")} [{oprator}] {msg}");
            }
        }
        #endregion

        #region 退出
        bool b_save = false;
        private void btn_Quit_Click(object sender, EventArgs e)
        {
            //if (!b_save)
            //{
            //    if (!string.IsNullOrEmpty(this.rtb_Sql.Text))
            //    {
            //        MessageBox.Show("比对已完成，但是结果未保存或复制！", "提示");
            //        b_save = true;
            //        return;
            //    }
            //}
            this.Close();
        }
        #endregion

        #region 执行比对 
        private static readonly string EmptyGuid = "00000000-0000-0000-0000-000000000000";

        string m_Content = string.Empty;
        object m_LockContent = new object();
        bool b_In = false;
        object m_LockIn = new object();
        int m_PBMax;
        int m_PBValue;
        object m_LockProgressBar = new object();
        /// <summary>
        /// 相应事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Compare_Click(object sender, EventArgs e)
        {
            lock (m_LockIn)
            {
                if (b_In)
                {
                    return;
                }
                b_In = true;
            }
            if (m_CompareTablesConf == null || m_CompareTablesConf.Tables == null || m_CompareTablesConf.Tables.Count == 0)
            {
                MessageBox.Show($"没有需要比对的表，请检查配置文件[\\Config\\CompareConfig.json]", "提示");
            }
            new System.Threading.Thread(Compare).Start();
        }
        /// <summary>
        /// 数据比对
        /// </summary>
        private void Compare()
        {
            var sum = 0;
            var sb = new StringBuilder();
            try
            {
                var bHasContent = false;
                SendLog("", "开始比对数据");   // 添加注释头
                sb.AppendLine("SET FOREIGN_KEY_CHECKS = 0;\r\n");

                sum = m_CompareTablesConf.Tables.Count;
                lock (m_LockProgressBar)
                {
                    m_PBMax = sum;
                }
                var index = 0;

                //1.查询hdis所有租户
                const string tenantQuery = "select * from SystemTenant";
                var tenantDs = MySqlHelper.ExecuteDataset(m_HdisConnStr, tenantQuery);

                //清理id映射表
                IdMappers.Clear();

                //2.循环所有表
                m_CompareTablesConf.Tables.ForEach(t =>
                {
                    SendLog("", $"比对表 {t.TableName} 开始");

                    if (t.HaveTenant.Equals("true"))
                    {
                        //3.循环所有租户数据处理
                        for (var i = 0; i < tenantDs.Tables[0].Rows.Count; i++)
                        {
                            var tenant = tenantDs.Tables[0].Rows[i];

                            var tenantId = tenant[0].ToString();

                            Guid.TryParse(tenantId, out var tenantGuid);

                            ExecCompare(t, sb, ref bHasContent, tenantGuid);
                        }
                    }
                    else
                    {
                        ExecCompare(t, sb, ref bHasContent);
                    }

                    SendLog("", $"比对表 {t.TableName} 结束");
                    lock (m_LockProgressBar)
                    {
                        m_PBValue = index++;
                    }
                });

                sb.AppendLine("SET FOREIGN_KEY_CHECKS = 1;\r\n");
                if (m_Stop)
                    return;
                b_save = false;
                if (bHasContent)
                {
                    lock (m_LockContent)
                        m_Content = sb.ToString();
                    SendLog("", "比较数据完毕");
                    HeaderComment(); //     添加注释
                }
                else
                {
                    SendLog("", "比较数据完毕，数据完全一致");
                    lock (m_LockContent)
                        m_Content = "clear";
                }
            }
            catch (Exception ex)
            {
                SendLog("", $"比对失败：{ex}");
            }
            finally
            {
                lock (m_LockIn)
                {
                    b_In = false;
                }
                lock (m_LockProgressBar)
                {
                    m_PBMax = sum;
                    m_PBValue = sum;
                }
            }
        }

        private void ExecCompare(CompareTablesItem t, StringBuilder sb, ref bool hasContent, Guid? tenantId = null)
        {
            #region step 1 比对数据-删除
            var ps = new List<string>();

            var tenantWhere = string.Empty;

            if (t.Delete != null && t.Delete.Any())
            {
                if (tenantId.HasValue) tenantWhere = $" AND TenantId = '{tenantId}'";

                //1.1查询待删除的数据
                var sql = $"SELECT s.* FROM `hdis`.`{t.TableName}` s WHERE {UnionKey(t.Insert, "s")} " +
                          $"NOT IN (SELECT {UnionKey(t.Insert, string.Empty)} FROM `hdis_compare`.`{t.TableName}` ) {tenantWhere};";
                SendLog("删除", $"{sql}");
                var ds = MySqlHelper.ExecuteDataset(m_ConnStr, sql);
                if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
                {
                    SendLog("", $"{t.TableName} 待删除数据量 {ds.Tables[0].Rows.Count}");
                    sb.AppendLine($"/* DELTE DATA {ds.Tables[0].Rows.Count} ROWS, DELETE KEYS: {KeyComment(t.Delete)} */");

                    //1.2生成待删除的数据
                    var sbContent = GenerateRemoveSql(ds.Tables[0], t.TableName, t.Delete, ps, tenantId, t.ParentMapper, t.ForeignMapper);
                    if (sbContent.Length > 0)
                    {
                        hasContent = true;
                        sb.Append(sbContent);
                    }
                }
                else
                {
                    SendLog("", $"{t.TableName} 没有待删除数据");
                }
            }
            else
            {
                SendLog("", $"{t.TableName} 无需删除数据");
            }
            #endregion

            #region step 2 比对数据-新增
            SendLog("", $"比对表 {t.TableName} 开始");
            if (t.Insert != null && t.Insert.Any())
            {
                if (tenantId.HasValue) tenantWhere = $" WHERE TenantId = '{tenantId}'";

                var orderSql = string.Empty;
                if (!string.IsNullOrWhiteSpace(t.ParentMapper?.ParentFiled) && t.ParentMapper.ParentFiled == "ParentId")
                {
                    orderSql = " Order by s.ParentId";
                }

                //2.1查询待新增的数据
                var sql = $"SELECT s.* FROM  `hdis_compare`.`{t.TableName}` s WHERE {UnionKey(t.Insert, "s")} " +
                          $"NOT IN (SELECT {UnionKey(t.Insert, string.Empty)} FROM `hdis`.`{t.TableName}` {tenantWhere}) {orderSql};";

                SendLog("新增", $"{sql}");
                var ds = MySqlHelper.ExecuteDataset(m_ConnStr, sql);
                if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0)
                {
                    SendLog("", $"{t.TableName} 待新增数据量 {ds.Tables[0].Rows.Count}");
                    sb.AppendLine($"/* INSERT DATA {ds.Tables[0].Rows.Count} ROWS. INSERT KEYS: {KeyComment(t.Insert)} */");

                    //2.2生成待新增的数据 
                    var sbContent = GenerateAddSql(ds.Tables[0], t.TableName, tenantId, t.ParentMapper, t.ForeignMapper);
                    if (sbContent.Length > 0)
                    {
                        hasContent = true;
                        sb.Append(sbContent);
                    }
                }
                else
                {
                    SendLog("", $"{t.TableName} 没有待新增数据");
                }
            }
            else
            {
                SendLog("", $"{t.TableName} 无需新增数据");
            }
            #endregion

            #region step 3 比对数据-修改
            if (t.Update != null && t.Update.Any())
            {
                if (t.UpdateFiled.Count > 0)
                {
                    //3.1查询待修改的sql语句，查询待修改数据
                    var sql = GenerateWaitUpdateTSql(t.TableName, t.Update, t.UpdateFiled, tenantId);                                                                         // 待修改的数据，即是拿ihdis_compare 与 ihdis 比较，同一张表A 中，ihdis_compare数据库中的表A 与 ihdis数据库中的表A 中的数据会分为两类 
                    SendLog("修改", $"{sql}");                                                                                                                   // 1，ihdis_compare[A] 中的数据比 ihdis[A] 中多的部分
                    var dt = new DataTable();                                                                                                              // 2，ihdis_compare[A] 中的数据与 ihdis[A] 中相同的部分       
                    dt.Load(MySqlHelper.ExecuteReader(m_ConnStr, sql));                                                                                          // 3, ihdis_compare[A] 中的数据比 ihdis[A] 中少的部分                               
                    if (dt.Rows.Count > 0)
                        sb.AppendLine($"/* UPDATE DATA {dt.Rows.Count} ROWS, UPDATE KEYS: {KeyComment(t.Update)} UPDATE FIELD: {KeyComment(t.UpdateFiled)} */");

                    //3.2生成修改sql语句
                    var sbContent = GenerateModifySql(dt, t.TableName, t.Update, tenantId, t.ParentMapper, t.ForeignMapper);
                    if (sbContent.Length > 0)
                    {
                        hasContent = true;
                        sb.Append(sbContent);
                    }
                }
                else
                {
                    SendLog("", $"{t.TableName} 没有待修改的数据");
                }
            }
            else
            {
                SendLog("", $"{t.TableName} 无需修改数据");
            }
            #endregion

            #region step 4 清理垃圾数据
            if (t.CleanRubbish != null && t.CleanRubbish.Count > 0)   // 配置了清理垃圾数据的列
            {
                sb.AppendLine($"/* CLEAR RUBBISH DATA , CLEAN KEYS: {KeyComment(t.CleanRubbish.Select(x => x.CleanRubbishFiled).ToList())} */");
                var temp = CleanRubbish(t.TableName, t.CleanRubbish, tenantId);
                if (!string.IsNullOrEmpty(temp))
                {
                    hasContent = true;
                    sb.AppendLine(temp);
                }
                SendLog($"", $"清理垃圾：{temp}");
            }
            #endregion
        }


        /// <summary>
        /// 得到需要修改的SQL语句
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="keys"></param>
        /// <param name="updateField"></param>
        /// <returns></returns>
        private static string GenerateWaitUpdateTSql(string tableName, IReadOnlyList<string> keys, IReadOnlyList<string> updateField, Guid? tenantId)
        {
            if (keys == null || keys.Count <= 0 || updateField == null || updateField.Count <= 0) return string.Empty;

            var sb = new StringBuilder();
            var temp = keys.Union(updateField).ToList();
            sb.Append($"SELECT");
            for (var i = 0; i < temp.Count; i++)
            {
                sb.Append($" p.`{temp[i]}` ");
                if (i != temp.Count - 1) sb.Append(" , ");
            }
            sb.Append($" FROM  hdis.`{tableName}` s, hdis_compare.`{tableName}` p WHERE ");
            for (var i = 0; i < keys.Count; i++)
            {
                sb.Append($" s.`{keys[i]}` = p.`{keys[i]}` ");
                if (i != keys.Count - 1) sb.Append(" AND ");
            }
            sb.Append(" AND ( ");
            for (var i = 0; i < updateField.Count; i++)
            {
                sb.Append($"s.`{updateField[i]}` != p.`{updateField[i]}`");
                if (i != updateField.Count - 1) sb.Append(" OR ");
            }
            sb.Append(" ) ");

            if (tenantId.HasValue)
            {
                sb.Append($" AND s.`TenantId` = '{tenantId}' ");
            }

            return sb.ToString();
        }

        /// <summary>
        /// 连接键
        /// </summary>
        /// <param name="keys">键集合</param>
        /// <param name="dbName">数据库名称</param>
        /// <returns></returns>
        private static string UnionKey(IReadOnlyList<string> keys, string dbName)
        {
            if (keys == null || keys.Count <= 0) return string.Empty;                                                                                                            //如果键集合为空，直接返回空字符串

            var sb = new StringBuilder();
            sb.Append("concat(");
            for (var i = 0; i < keys.Count; ++i)
            {
                var temp = $"{(string.IsNullOrEmpty(dbName) ? string.Empty : $"{dbName}.")}`{keys[i]}`";

                sb.Append($"IFNULL({temp},'')");
                if (i != keys.Count - 1)
                {
                    sb.Append(",");
                }
            }

            sb.Append(")");
            return sb.ToString();
        }

        private StringBuilder GenerateRemoveSql(DataTable data, string tableName,
            List<string> primaryKeys, ICollection<string> ps, Guid? tenantId,
            CompareParentMapper parentMapper, List<CompareForeignMapper> foreignMappers)
        {
            var sql = new StringBuilder();
            foreach (DataRow row in data.Rows)
            {
                var key = UnionKey2Data(primaryKeys, row);
                ps.Add(key);
                sql.AppendLine($"DELETE FROM `hdis`.`{tableName}` WHERE {BuildUpdateOrDeleteWhere(tableName, row, primaryKeys, tenantId, parentMapper, foreignMappers)};");
                sql.AppendLine();
            }
            return sql;
        }

        //新老主键映射关系
        public readonly List<Tuple<Guid?, string, string, Guid>> IdMappers = new List<Tuple<Guid?, string, string, Guid>>();

        private StringBuilder GenerateAddSql(DataTable data, string tableName, Guid? tenantId,
             CompareParentMapper parentMapper, List<CompareForeignMapper> foreignMappers)
        {
            var sql = new StringBuilder();

            //1.初始化主键为新Id，并保留原Id映射关系  
            foreach (DataRow row in data.Rows)
            {
                var newGuid = Guid.NewGuid();

                IdMappers.Add(new Tuple<Guid?, string, string, Guid>(tenantId, tableName, row["Id"].ToString(), newGuid));

                row["Id"] = newGuid;
            }

            foreach (DataRow row in data.Rows)
            {
                var temp = InsertComment(row, tableName, "id");
                if (!string.IsNullOrEmpty(temp))
                {
                    sql.AppendLine(temp);
                }

                int? tenantColumnIndex = null;
                int? parentIdIndex = null;
                var foreignIndexMappers = new List<Tuple<int, string, string, string>>();

                sql.Append($"{(!string.IsNullOrEmpty(temp) ? "/*" : string.Empty)}INSERT INTO `hdis`.`{tableName}` (");
                for (var i = 0; i < data.Columns.Count; i++)
                {
                    //2.涉及递归字段与跨表问题，记录字段索引 
                    var filedName = data.Columns[i].ToString();

                    if (filedName == parentMapper?.ParentFiled)
                    {
                        parentIdIndex = i;//父节点字段索引
                    }

                    if (filedName == "TenantId")
                    {
                        tenantColumnIndex = i; //租户字段索引
                    }

                    var foreignFiled = foreignMappers.FirstOrDefault(x => x.ForeignKey == filedName);
                    if (foreignFiled != null)
                    {
                        foreignIndexMappers.Add(new Tuple<int, string, string, string>(
                            i, foreignFiled.ForeignTable, foreignFiled.ForeignKey, foreignFiled.SearchFiled));//外键字段索引
                    }

                    if (i != 0) sql.Append(" ");

                    sql.Append($"`{data.Columns[i]}`");

                    if (i != data.Columns.Count - 1) sql.Append(",");
                }
                sql.Append($") VALUES (");
                for (var i = 0; i < row.ItemArray.Length; i++)
                {
                    //3.涉及递归字段与跨表问题，规则获取 
                    var rowValue = row.ItemArray[i].ToString();

                    if (tenantColumnIndex.HasValue && i == tenantColumnIndex)
                    {
                        sql.Append($"'{tenantId}'");
                    }
                    else if (parentIdIndex.HasValue && i == parentIdIndex)
                    {
                        //从本地或数据库查询父节点ID 
                        var parentId = GetParentId(tableName, parentMapper.SearchFiled, rowValue, tenantId);

                        sql.Append($"'{parentId}'");
                    }
                    else if (foreignIndexMappers.Select(x => x.Item1).Contains(i))
                    {
                        var foreign = foreignIndexMappers.FirstOrDefault(x => x.Item1 == i);

                        //根据表名到Compare库查Name，在到HDIS库查询外键ID 
                        var foreignId = GetForeignId(foreign?.Item2, foreign?.Item4, rowValue, tenantId);

                        sql.Append($"'{foreignId}'");
                    }
                    else
                    {
                        sql.Append($"{GetSqlDataByType(row.ItemArray[i])}");
                    }

                    if (i != data.Columns.Count - 1)
                        sql.Append(",");
                }

                sql.Append($");{(!string.IsNullOrEmpty(temp) ? "*/" : string.Empty)}\r\n");
                sql.AppendLine();
            }

            return sql;
        }

        private string GetParentId(string tabName, string filedName, string parentFiledValue, Guid? tenantId)
        {
            //1.先从本地关系表找上级id
            Guid parentId;

            if (EmptyGuid == parentFiledValue) return EmptyGuid;

            var idMapper = IdMappers?.FirstOrDefault(x => x.Item1 == tenantId && x.Item2 == tabName && x.Item3 == parentFiledValue);
            if (idMapper != null)
            {
                return idMapper.Item4.ToString();
            }

            //2.从hdis库找上级id
            var tenantCondition = string.Empty;
            if (tenantId.HasValue)
            {
                tenantCondition += $"AND `TenantId`= '{tenantId}'";
            }

            var sql = $"SELECT s.Id FROM `hdis`.`{tabName}` s WHERE `{filedName}` = (SELECT `{filedName}` FROM `hdis_compare`.`{tabName}` " +
                      $"where `Id`= '{parentFiledValue}') {tenantCondition}; ";

            var result = MySqlHelper.ExecuteScalar(m_ConnStr, sql);

            return result?.ToString();
        }

        private string GetForeignId(string foreignTableName, string searchFiledName, string filedValue, Guid? tenantId)
        {
            //1.先从本地关系表找外键id
            var idMapper = IdMappers?.FirstOrDefault(x => x.Item1 == tenantId && x.Item2 == foreignTableName && x.Item3 == filedValue);
            if (idMapper != null)
            {
                return idMapper.Item4.ToString();
            }

            //2.从hdis库找外键id
            var tenantCondition = string.Empty;
            if (tenantId.HasValue)
            {
                tenantCondition += $"AND s.`TenantId`= '{tenantId}'";
            }

            var sql = $"SELECT s.Id FROM `hdis`.`{foreignTableName}` s WHERE s.`{searchFiledName}` = " +
                      $"(SELECT `{searchFiledName}` FROM `hdis_compare`.`{foreignTableName}` where `Id`= '{filedValue}') {tenantCondition}; ";

            var result = MySqlHelper.ExecuteScalar(m_ConnStr, sql);

            return result?.ToString();
        }


        private StringBuilder GenerateModifySql(DataTable data, string tableName, IReadOnlyList<string> primaryKeys, Guid? tenantId,
            CompareParentMapper parentMapper, List<CompareForeignMapper> foreignMappers)
        {
            var sql = new StringBuilder();

            foreach (DataRow row in data.Rows)
            {
                sql.Append($"UPDATE  `hdis`.`{tableName}` SET ");
                for (var i = 0; i < data.Columns.Count; i++)
                {
                    if (i != 0) sql.Append(" ");

                    var columnName = data.Columns[i].ToString();
                    var columnValue = row.ItemArray[i].ToString();

                    //根据父节点映射更新值
                    if (!string.IsNullOrWhiteSpace(parentMapper?.ParentFiled) && parentMapper.ParentFiled == columnName)
                    {
                        columnValue = GetParentId(tableName, parentMapper.SearchFiled, columnValue, tenantId);

                        sql.Append($"`{data.Columns[i]}` = '{columnValue}' ");
                    }
                    //根据外键映射更新值
                    else if (foreignMappers.Any(x => x.ForeignKey == columnName))
                    {
                        var foreignMapper = foreignMappers.FirstOrDefault(x => x.ForeignKey == columnName);

                        columnValue = GetForeignId(foreignMapper?.ForeignTable, foreignMapper?.SearchFiled, columnValue, tenantId);

                        sql.Append($"`{data.Columns[i]}` = '{columnValue}' ");
                    }
                    else
                    {
                        sql.Append($"`{data.Columns[i]}` = {GetSqlDataByType(row.ItemArray[i])} ");
                    }

                    if (i != data.Columns.Count - 1) sql.Append(",");
                }

                sql.Append($" WHERE {BuildUpdateOrDeleteWhere(tableName, row, primaryKeys, tenantId, parentMapper, foreignMappers)};\r\n");
                sql.AppendLine();
            }

            return sql;
        }

        /// <summary>
        /// 创建 update 和 delete 条件后的 where 语句
        /// </summary>
        /// <returns></returns>
        private string BuildUpdateOrDeleteWhere(string tableName, DataRow row, IReadOnlyList<string> keys, Guid? tenantId,
            CompareParentMapper parentMapper, List<CompareForeignMapper> foreignMappers)
        {
            var temp = string.Empty;

            if (keys == null || keys.Count <= 0) return temp;

            for (var i = 0; i < keys.Count; i++)
            {
                var whereFiled = keys[i];
                var whereValue = row[whereFiled].ToString();

                //映射父节点条件值
                if (!string.IsNullOrWhiteSpace(parentMapper?.ParentFiled) && parentMapper.ParentFiled == whereFiled)
                {
                    temp += $"`{whereFiled}` = '{GetParentId(tableName, parentMapper.SearchFiled, whereValue, tenantId)}' ";
                }
                //根据外键映射更新值
                else if (foreignMappers.Any(x => x.ForeignKey == whereFiled))
                {
                    var foreignMapper = foreignMappers.FirstOrDefault(x => x.ForeignKey == whereFiled);

                    whereValue = GetForeignId(foreignMapper?.ForeignTable, foreignMapper?.SearchFiled, whereValue, tenantId);

                    temp += $"`{whereFiled}` = '{whereValue}' ";
                }
                else
                {
                    temp += $" `{whereFiled}` = {GetSqlDataByType(whereValue)} ";
                }

                if (i != keys.Count - 1) temp += " AND ";
            }

            if (tenantId.HasValue)
            {
                temp += $" AND `TenantId` = '{tenantId}'";
            }

            return temp;
        }
        /// <summary>
        /// 连接两个键数据
        /// </summary>
        /// <param name="keys"></param>
        /// <param name="row"></param>
        /// <returns></returns>
        private string UnionKey2Data(List<string> keys, DataRow row)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < keys.Count; ++i)
            {
                sb.Append($"{GetSqlDataByType(row[keys[i]])}");
                if (i != keys.Count - 1)
                {
                    sb.Append("");
                }
            }
            return sb.ToString();
        }
        /// <summary>
        /// 转换数据库类型
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private string GetSqlDataByType(object data)
        {
            string typeName = data.GetType().Name;

            if (typeName == "DBNull")
                return "NULL";
            if (typeName == "Guid" || typeName == "String" || typeName == "DateTime")
            {
                if (data.ToString().Length == 19)
                {
                    try
                    {
                        DateTime dt = DateTime.ParseExact(data.ToString(), "MM/dd/yyyy HH:mm:ss", null, new System.Globalization.DateTimeStyles());
                        data = dt.ToString("yyyy/MM/dd HH:mm:ss");
                    }
                    catch
                    { }
                }
                else                                                                                                        // 是否为字符类型
                {
                    string value = data.ToString();
                    if (value.IndexOf("\r") > 0)
                        value = value.Replace("\r", "\\r");
                    if (value.IndexOf("\n") > 0)
                        value = value.Replace("\n", "\\n");
                    if (value.IndexOf("'") > 0)
                        value = value.Replace("'", "''");
                    if (value.IndexOf("\\") > 0)
                        value = value.Replace("\\", "\\\\");
                    data = value;
                }
                return $"'{data}'";
            }

            return data.ToString();
        }
        /// <summary>
        /// 头部注释信息
        /// </summary>
        /// <returns></returns>
        private string HeaderComment()
        {
            MySqlConnectionStringBuilder con = new MySqlConnectionStringBuilder(m_ConnStr);
            StringBuilder builder = new StringBuilder();
            SendLog("", "-- MySQL Server DataComapare Result");
            SendLog("", "--");
            SendLog("", $"-- Host: {con.Server}    Database: {con.Database}");
            SendLog("", "--------------------------------------------------------");
            SendLog("", "-- Server version   5.7.25");
            SendLog("", "");
            SendLog("", "-- 脚本执行注意事项:");
            SendLog("", "-- (1) 表中数据的比对若不是选择主键列为比对依据，则生成的插入数据中可能会会产生错误：");
            SendLog("", "--     Duplicate entry  for key 'PRIMARY',此时应根据提示找到该行数据，根据实际需要判断是否插入或不插入该行数据。");
            SendLog("", "-- (2) 若配置文件已经配置删除某表的数据，请注意浏览脚本，再次确定是否需要删除该数据。");
            SendLog("", "");
            return builder.ToString();
        }
        /// <summary>
        /// 键描述信息
        /// </summary>
        /// <returns></returns>
        private static string KeyComment(List<string> keys)
        {
            if (keys == null || keys.Count <= 0) return string.Empty;
            var builder = new StringBuilder();
            for (var i = 0; i < keys.Count; i++)
            {
                builder.Append(keys[i]);
                if (i != keys.Count - 1) builder.Append(",");
            }
            return builder.ToString();
        }
        /// <summary>
        /// 插入测试
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private string InsertComment(DataRow data, string tableName, string primaryKey)
        {
            if (data == null || string.IsNullOrEmpty(primaryKey)) return string.Empty;
            var sql = $"select count(1) from hdis.`{tableName}` where `{primaryKey}` = {GetSqlDataByType(data[primaryKey])}";
            var count = 0;
            try
            {
                count = int.Parse(MySqlHelper.ExecuteScalar(m_ConnStr, sql).ToString());
            }
            catch (Exception error)
            {
                SendLog("", error.ToString());
            }
            return count > 0 ? $"/* PRIMARY KEY 为 {data[primaryKey]} 的该行数据在 hdis 数据库中已经存在，插入数据可能会报错。 */ " : string.Empty;
        }
        /// <summary>
        /// 清理垃圾数据
        /// </summary>
        /// <returns></returns>
        private string CleanRubbish(string tableName, IReadOnlyList<CleanRubbishMapper> cleanMappers, Guid? tenantId)
        {
            if (string.IsNullOrEmpty(tableName) || cleanMappers == null | !cleanMappers.Any()) return string.Empty;

            var builder = new StringBuilder();
            builder.Append($"DELETE FROM hdis.`{tableName}` WHERE ");
            for (var i = 0; i < cleanMappers.Count; i++)
            {
                var cleanMapper = cleanMappers[i];

                builder.Append($"(`{cleanMapper.CleanRubbishFiled}` IS NOT NULL AND `{cleanMapper.CleanRubbishFiled}` " +
                               $"NOT  IN (SELECT `{cleanMapper.CleanRubbishTableFiled}` FROM hdis.`{cleanMapper.CleanRubbishTable}`))");

                if (i < cleanMappers.Count - 1) builder.Append(" AND ");
            }

            if (tenantId.HasValue)
            {
                builder.Append($" AND TenantId = '{tenantId}'");
            }

            builder.Append(";\r\n");

            var count = int.Parse(MySqlHelper.ExecuteScalar(m_ConnStr, builder.ToString().Replace("DELETE ", "SELECT COUNT(*) ")).ToString());

            return count > 0 ? builder.ToString() : string.Empty;
        }
        #endregion

        #region 拷贝
        /// <summary>
        /// 拷贝比对后的SQL语句
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ll_Copy_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (string.IsNullOrEmpty(this.rtb_Sql.Text))
            {
                return;
            }
            try
            {
                b_save = true;
                Clipboard.SetDataObject(this.rtb_Sql.Text.Trim(), true);
                MessageBox.Show("拷贝成功！", "提示");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        #endregion

        #region 保存至文件

        private void btn_select_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.rtb_Sql.Text))
            {
                return;
            }
            try
            {
                string fname = "";
                using (FolderBrowserDialog path = new FolderBrowserDialog())
                {
                    if (path.ShowDialog() == DialogResult.OK)
                    {
                        fname = $"{path.SelectedPath}\\ihdis_data_{DateTime.Now.ToString("yyyy_MM_dd")}.sql";
                        if (System.IO.File.Exists(fname))
                        {
                            if (MessageBox.Show("文件已存在，是否覆盖？", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
                            {
                                return;
                            }
                        }
                    }
                }
                if (!string.IsNullOrEmpty(fname))
                {
                    //System.IO.File.WriteAllText(fname, this.rtb_Sql.Text, Encoding.UTF8);
                    var utf8WithBom = new System.Text.UTF8Encoding(false);           // 用true来指定包含bom
                    using (StreamWriter sw = new StreamWriter(fname, false, utf8WithBom))
                    {
                        sw.Write(this.rtb_Sql.Text);
                    }
                    MessageBox.Show($"文件保存成功：\r\n{fname}", "提示");
                    b_save = true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"文件保存失败：{ex.ToString()}");
            }
        }
        #endregion

        #region 复制日志
        private void 复制ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetDataObject(this.lb_Logger.SelectedItem);
        }
        #endregion

        #region 执行sql
        bool b_execsql = false;
        private void Frm_DBCompare_Step3_KeyDown(object sender, KeyEventArgs e)
        {
            if (b_execsql)
                return;
            b_execsql = true;
            if (e.KeyCode == Keys.F5)
            {
                if (!string.IsNullOrEmpty(this.rtb_Sql.Text) && MessageBox.Show($"是否确定执行sql语句？", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    try
                    {
                        int res = MySqlHelper.ExecuteNonQuery(m_ConnStr.Replace("jlmedcompare", "jlmed"), this.rtb_Sql.Text);
                        SendLog("执行sql", $"sql语句执行完毕 {res}");
                    }
                    catch (Exception ex)
                    {
                        SendLog("执行sql", $"sql语句执行失败 {ex.ToString()}");
                    }
                }
            }
            b_execsql = false;
        }
        #endregion
    }

    #region 配置文件
    public class CompareTablesItem
    {
        /// <summary>
        /// 
        /// </summary>
        public string TableName { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<string> Insert { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<string> Update { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<string> UpdateFiled { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<string> Delete { get; set; }
        /// <summary>
        /// 清理垃圾字段
        /// </summary>
        public List<CleanRubbishMapper> CleanRubbish { set; get; }
        /// <summary>
        /// 
        /// </summary>
        public string Filter { get; set; }

        /// <summary>
        /// 是否租户隔离
        /// </summary>
        public string HaveTenant { get; set; }

        /// <summary>
        /// 父节点映射
        /// </summary>
        public CompareParentMapper ParentMapper { get; set; }

        /// <summary>
        /// 外键映射
        /// </summary>
        public List<CompareForeignMapper> ForeignMapper { get; set; }
    }

    public class CleanRubbishMapper
    {
        /// <summary>
        /// 清理比较字段
        /// </summary>
        public string CleanRubbishFiled { get; set; }

        /// <summary>
        /// 清理比较字段所属表
        /// </summary>
        public string CleanRubbishTable { get; set; }

        /// <summary>
        /// 清理比较字段所属表字段
        /// </summary>
        public string CleanRubbishTableFiled { get; set; }
    }

    public class CompareParentMapper
    {
        /// <summary>
        /// 父节点字段
        /// </summary>
        public string ParentFiled { get; set; }

        /// <summary>
        /// 查找字段
        /// </summary>
        public string SearchFiled { get; set; }
    }

    public class CompareForeignMapper
    {
        /// <summary>
        /// 外键表名
        /// </summary>
        public string ForeignTable { get; set; }

        /// <summary>
        /// 外键字段名
        /// </summary>
        public string ForeignKey { get; set; }

        /// <summary>
        /// 查找字段
        /// </summary>
        public string SearchFiled { get; set; }
    }

    public class CompareTables
    {
        /// <summary>
        /// 
        /// </summary>
        public List<CompareTablesItem> Tables { get; set; }
    }
    #endregion
}
