using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace Tiobon.Core.OPS.Tool.OPS.Tool.View
{
    public partial class Frm_DbExport : Form
    {
        #region 初始化
        string m_ConnStr = "";
        public Frm_DbExport(string connStr)
        {
            InitializeComponent();
            m_ConnStr = connStr;
            LoadTables();
            LoadConf();
        }

        private void Form_DbExport_Load(object sender, EventArgs e)
        {
            timer_Main.Start();
        }
        #endregion

        #region 选择文件路径
        string m_FileName = "";
        private void btn_select_Click(object sender, EventArgs e)
        {
            using (FolderBrowserDialog path = new FolderBrowserDialog())
            {
                if (path.ShowDialog() == DialogResult.OK)
                {
                    if (System.IO.File.Exists($"{ path.SelectedPath}\\hdis_{DateTime.Now.ToString("yyyy_MM_dd")}.sql"))
                    {
                        if (MessageBox.Show("文件已存在，是否覆盖？", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
                        {
                            return;
                        }
                    }
                    this.txt_fname.Text = $"{ path.SelectedPath}\\hdis_{DateTime.Now.ToString("yyyy_MM_dd")}.sql";//返回文件的完整路径   
                }
            }
        }
        #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;
                }
            }
            catch { }
        }
        private static List<string> m_Logs = new List<string>();
        private void SendLog(string oprator, string msg)
        {
            lock (m_Logs)
            {
                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 加载表名
        Dictionary<string, List<string>> m_Tables = new Dictionary<string, List<string>>();
        private void LoadTables()
        {
            try
            {
                m_Tables.Clear();
                string sql = $"SELECT * FROM INFORMATION_SCHEMA.`COLUMNS` C WHERE  C.TABLE_SCHEMA = 'hdis';";
                DataTable dt = MySqlHelper.ExecuteDataset(m_ConnStr, sql).Tables[0];
                foreach (DataRow dr in dt.Rows)
                {
                    if (!m_Tables.ContainsKey(dr["TABLE_NAME"].ToString()))
                    {
                        m_Tables.Add(dr["TABLE_NAME"].ToString(), new List<string>
                        {
                            dr["COLUMN_NAME"].ToString()
                        });
                        treeView_Main.Nodes[0].Nodes.Add(dr["TABLE_NAME"].ToString());
                    }
                    else
                    {
                        m_Tables[dr["TABLE_NAME"].ToString()].Add(dr["COLUMN_NAME"].ToString());
                    }
                }
                treeView_Main.ExpandAll();
                foreach (TreeNode node in treeView_Main.Nodes[0].Nodes)
                {
                    node.Checked = true;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("读取数据库结构失败：" + ex.Message);
            }
        }
        #endregion

        #region 选中节点
        private void treeView_Main_BeforeCheck(object sender, TreeViewCancelEventArgs e)
        {
            TreeViewCheck.CheckControl(e);
        }
        #endregion

        #region 导出
        List<string> tabels = new List<string>();
        private void btn_Export_Click(object sender, EventArgs e)
        {
            m_FileName = this.txt_fname.Text;
            if (m_FileName == "")
                return;
            if (b_In)
            {
                return;
            }
            tabels.Clear();
            foreach (TreeNode node in treeView_Main.Nodes[0].Nodes)
            {
                if (node.Checked)
                {
                    tabels.Add(node.Text);
                }
            }

            if (tabels.Count > 0)
            {
                System.Threading.Thread th = new System.Threading.Thread(Export);
                th.Start();
            }
        }
        bool b_In = false;
        object m_Lock = new object();
        private void Export()
        {
            lock (m_Lock)
            {
                if (b_In)
                {
                    return;
                }
                else
                {
                    b_In = true;
                }
            }
            DateTime dtStart = DateTime.Now;
            try
            {
                SendLog("", "共有表 " + tabels.Count);

                if (File.Exists(m_FileName))
                {
                    File.Delete(m_FileName);
                }
                int tcount = 0;
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("SET FOREIGN_KEY_CHECKS = 0;");
                tabels.ForEach(tabelname =>
                {
                    SetTotal(tabels.Count, ++tcount);
                    sb.AppendLine($"-- 正在导出  表 hdis.{tabelname} 结构");
                    SendLog("", $"-- 正在导出  表 hdis.{tabelname} 结构");
                    //删除原表
                    //sb.AppendLine($"DROP TABLE IF EXISTS `{tabelname}`;");

                    //导出表结构
                    sb.AppendLine($"{MySqlHelper.ExecuteDataset(m_ConnStr, $"SHOW CREATE TABLE `hdis`.`{tabelname}`;").Tables[0].Rows[0]["Create Table"].ToString()};");
                    sb.AppendLine();

                    //判断配置
                    string strWhere = string.Empty;
                    if (m_DBExportConf != null && m_DBExportConf.Content != null)
                    {
                        if (m_DBExportConf.Content.Except != null && !m_DBExportConf.Content.Except.Contains(tabelname)
                        && m_DBExportConf.Content.Limit != null && !m_DBExportConf.Content.Limit.Contains(tabelname)
                        && m_DBExportConf.Content.All != null && !m_DBExportConf.Content.All.Contains(tabelname))
                        {
                            //只导出表结构不导出数据
                            WriteToFile($"{sb.ToString()}");
                            sb.Clear();
                            SendLog("", $"表 {tabelname} [未配置] 只导出结构不导出数据");
                            return;
                        }

                        if (m_DBExportConf.Content.Except != null && m_DBExportConf.Content.Except.Contains(tabelname))
                        {
                            //只导出表结构不导出数据
                            WriteToFile($"{sb.ToString()}");
                            sb.Clear();
                            SendLog("", $"表 {tabelname} 只导出结构不导出数据");
                            return;
                        }

                        if (m_DBExportConf.Content.Limit != null && m_DBExportConf.Content.Limit.Contains(tabelname))
                        {
                            //导出规定时间内的数据，包含create_time字段
                            if (m_Tables[tabelname].Contains("create_time"))
                            {
                                SendLog("", $"表 {tabelname} 只导出 {m_DBExportConf.LimitTime} 天以内的数据");
                                strWhere = $" where create_time >= '{DateTime.Now.AddDays(m_DBExportConf.LimitTime * -1).ToString("yyyy-MM-dd HH:mm:ss")}'";
                            }
                        }
                    }

                    //导出数据
                    sb.AppendLine($"-- 正在导出  表 hdis.{tabelname} 数据");
                    int rows = int.Parse(MySqlHelper.ExecuteScalar(m_ConnStr, $"SELECT COUNT(0) FROM `hdis`.`{tabelname}`{strWhere};").ToString());
                    SendLog("", $"-- 正在导出  表 hdis.{tabelname} 数据，共有数据 {rows}");

                    int sum = 0;
                    using (MySqlDataReader reader = MySqlHelper.ExecuteReader(m_ConnStr, $"SELECT * FROM `hdis`.`{tabelname}`{strWhere};"))
                    {
                        if (reader.HasRows)
                        {
                            //字段类型
                            Dictionary<int, string> dic = new Dictionary<int, string>();

                            //添加字段
                            //sb.AppendLine($"DELETE FROM `{tabelname}`;");

                            StringBuilder sb_insert = new StringBuilder();
                            sb_insert.Append($"INSERT INTO `{tabelname}` (");
                            for (int i = 0; i < reader.FieldCount; ++i)
                            {
                                sb_insert.Append($"`{reader.GetName(i)}`");
                                if (i != reader.FieldCount - 1)
                                    sb_insert.Append(", ");

                                dic.Add(i, reader.GetDataTypeName(i));
                            }
                            sb_insert.Append(") VALUES");

                            sb_insert.AppendLine();
                            //表开始
                            WriteToFile($"{sb.ToString()}");
                            sb.Clear();

                            int index = 0;
                            //添加数据
                            while (reader.Read())
                            {
                                sum++;
                                sb.Append("\t(");
                                for (int i = 0; i < reader.FieldCount; ++i)
                                {
                                    sb.Append($"{ConvertData(reader[i], dic[i])}");
                                    if (i != reader.FieldCount - 1)
                                        sb.Append(", ");
                                }
                                sb.Append(")");
                                //500行数据入库一次
                                if (index++ >= 500)
                                {
                                    sb.Append(";");

                                    sb.AppendLine();
                                    sb.AppendLine();
                                    WriteToFile($"{sb_insert.ToString()}{sb.ToString()}");
                                    sb.Clear();
                                    index = 0;
                                    continue;
                                }
                                //1024KB为一组插入语句
                                if (sb.Length >= 100 * 1024)
                                {
                                    sb.Append(";");
                                    sb.AppendLine();
                                    sb.AppendLine();
                                    WriteToFile($"{sb_insert.ToString()}{sb.ToString()}");
                                    sb.Clear();
                                    index = 0;
                                    continue;
                                }
                                if (sum != rows)
                                    sb.Append(",");
                                else
                                    sb.Append(";");

                                sb.AppendLine();
                            }

                            //表结束
                            if (sb.Length > 0)
                            {
                                sb.AppendLine();
                                WriteToFile($"{sb_insert.ToString()}{sb.ToString()}");
                                sb.Clear();
                                index = 0;
                            }
                        }
                    }

                    sb.AppendLine();
                });
                sb.AppendLine("SET FOREIGN_KEY_CHECKS = 1;");
                if (sb.Length > 0)
                    WriteToFile(sb.ToString());

                DateTime dtEnd = DateTime.Now;
                SendLog("", $"导出数据完毕，耗时 {(int)((dtEnd - dtStart).TotalSeconds / 60)}:{(int)((dtEnd - dtStart).TotalSeconds % 60)}");
            }
            catch (Exception ex)
            {
                SendLog("", "导出数据异常" + ex.ToString());
            }
            finally
            {
                lock (m_Lock)
                {
                    b_In = false;
                }
            }
        }

        private string ConvertData(object data, string type)
        {
            if (data.GetType().FullName == "System.DBNull")
                return "NULL";
            string value;
            switch (type)
            {
                case "VARCHAR":
                    {
                        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("\\", "\\\\");
                        value = $"'{value}'";
                        break;
                    }
                case "JSON":
                    {
                        value = data.ToString().Trim() == "" ? "NULL" : $"'{data.ToString()}'";
                        break;
                    }
                case "TIMESTAMP":
                    value = data.ToString().Trim() == "" ? "NULL" : $"'{Convert.ToDateTime(data.ToString()).ToString("yyyy-MM-dd HH:mm:ss")}'";
                    break;
                case "DATE":
                    value = data.ToString().Trim() == "" ? "NULL" : $"'{Convert.ToDateTime(data.ToString()).ToString("yyyy-MM-dd")}'";
                    break;
                default:
                    value = data.ToString().Trim() == "" ? "NULL" : data.ToString();
                    break;
            }
            return value;
        }

        private void WriteToFile(string content)
        {
            if (!File.Exists(m_FileName))
            {
                using (StreamWriter sw = File.CreateText(m_FileName))
                {
                    sw.Write(content);
                }
            }
            else
            {
                using (StreamWriter sw = File.AppendText(m_FileName))
                {
                    sw.Write(content);
                }
            }
        }
        #endregion

        #region 进度
        private void SetTotal(int max, int value)
        {
            try
            {
                this.BeginInvoke(new EventHandler(delegate
                {
                    progressBar_Main.Minimum = 0;
                    progressBar_Main.Maximum = max;
                    progressBar_Main.Value = value;
                    lbl_pro.Text = $"{value}/{max}";
                }));
            }
            catch { }
        }
        #endregion

        #region 加载配置项
        DBExportConf m_DBExportConf;
        private void LoadConf()
        {
            try
            {
                string fname = AppDomain.CurrentDomain.BaseDirectory + @"\Config\DBExportConfig.json";
                if (File.Exists(fname))
                    m_DBExportConf = Newtonsoft.Json.JsonConvert.DeserializeObject<DBExportConf>(File.ReadAllText(fname));
            }
            catch { }
        }
        #endregion
    }

    #region 配置项
    public class Content
    {
        /// <summary>
        /// 
        /// </summary>
        public List<string> All { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<string> Except { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public List<string> Limit { get; set; }
    }

    public class DBExportConf
    {
        /// <summary>
        /// 
        /// </summary>
        public Content Content { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public int LimitTime { get; set; }
    }
    #endregion
}
