using AR;
using MySql.Data.MySqlClient;
using OpenQA.Selenium;
using Renci.SshNet;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO.Ports;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
using System.Windows.Forms;
using UniMarc.BaroService_TI;
using UniMarc.Properties;

namespace UniMarc
{

    public partial class Helper_DB
    {
        //static string cs = "";
        public enum eDbType
        {
            unimarc,
            cl_marc
        }
        public static MySqlConnection CreateConnection(eDbType dbtype)
        {
            var dbname = dbtype.ToString();
            string strConnection = string.Format(
                          "Server={0};" +
                          "Port={1};" +
                          $"Database={dbname};" +
                          "uid={2};" +
                          "pwd={3};", ServerData[0], DBData[0], DBData[1], DBData[2]);
            return new MySqlConnection(strConnection);
        }

        /// <summary>
        /// 입력한 쿼리의 결과를 데이터테이블로 반환합니다
        /// </summary>
        /// <param name="query"></param>
        /// <param name="cn"></param>
        /// <returns></returns>
        public static DataTable ExecuteDataTable(string query, MySqlConnection cn = null)
        {
            var bLocalCN = cn == null;
            DataTable dt = new DataTable();
            try
            {
                if (cn == null) cn = CreateConnection(eDbType.unimarc);// new MySqlConnection(cs);
                var cmd = new MySqlCommand(query, cn);
                cn.Open();
                using (MySqlDataAdapter adapter = new MySqlDataAdapter(cmd))
                {
                    adapter.Fill(dt);
                }
                cmd.Dispose();
                cn.Close();
                if (bLocalCN) cn.Dispose();
                return dt;
            }
            catch (Exception ex)
            {
                UTIL.MsgE(ex.ToString());
                return null;
            }
        }

        /// <summary>
        /// 오류발생시 null을 반환합니다
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="columns"></param>
        /// <param name="wheres"></param>
        /// <param name="orders"></param>
        /// <returns></returns>
        public static DataTable ExecuteQueryData(string tableName, string columns = "*", string wheres = "", string orders = "", MySqlConnection cn = null)
        {
            var sql = $"select {columns} from {tableName}";
            if (wheres.isEmpty() == false) sql += " where " + wheres;
            if (orders.isEmpty() == false) sql += " order by " + orders;
            return ExecuteDataTable(sql, cn);
        }



        /// <summary>
        /// 오류발생시 -1을 반환합니다
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public static (int applyCount, string errorMessage) ExcuteNonQuery(string query, MySqlConnection cn = null)
        {
            try
            {
                var bLocalCN = cn == null;
                if (cn == null) cn = CreateConnection(eDbType.unimarc);//new MySqlConnection(cs);
                var cmd = new MySqlCommand(query, cn);
                cn.Open();
                var cnt = cmd.ExecuteNonQuery();
                cmd.Dispose();
                if (bLocalCN) cn.Dispose();
                return (cnt, string.Empty);
            }
            catch (Exception ex)
            {
                return (-1, ex.Message);
            }
        }
        public static (long value, string errorMessage) ExcuteInsertGetIndex(string cmd, MySqlConnection cn = null)
        {
            long lastId = -1;
            var bLocalCN = cn == null;
            string message;
            try
            {

                if (cn == null) cn = CreateConnection(eDbType.unimarc);//new MySqlConnection(cs);
                using (var sqlcmd = new MySqlCommand(cmd, cn))
                {
                    cn.Open();
                    sqlcmd.ExecuteNonQuery();
                    lastId = sqlcmd.LastInsertedId;
                }
                if (bLocalCN) cn.Dispose();
                message = "";
            }
            catch (Exception ex)
            {
                lastId = -1;
                message = ex.Message;
            }
            return (lastId, message);
        }

        /// <summary>
        /// 단일항목값을 반환 합니다
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public static (object value, string errorMessage) ExcuteScalar(string query, MySqlConnection cn = null)
        {
            try
            {
                var bLocalCN = cn == null;
                if (cn == null) cn = CreateConnection(eDbType.unimarc);//new MySqlConnection(cs);
                var cmd = new MySqlCommand(query, cn);
                cn.Open();
                var val = cmd.ExecuteScalar();
                cmd.Dispose();
                if (bLocalCN) cn.Dispose();
                return (val, string.Empty);
            }
            catch (Exception ex)
            {
                return (null, ex.Message);
            }
        }

        /// <summary>
        /// Insert 명령을 수행한 후 자동 생성된 index값을 반환합니다.
        /// 오류발생시에는 -1을 반환합니다
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="cn"></param>
        /// <returns></returns>
        public static long DB_Send_CMD_Insert_GetIdx(string cmd, MySqlConnection cn = null)
        {
            long lastId = -1;
            var bLocalCN = cn == null;
            try
            {

                if (cn == null) cn = CreateConnection(eDbType.unimarc);//new MySqlConnection(cs);
                using (var sqlcmd = new MySqlCommand(cmd, cn))
                {
                    cn.Open();
                    sqlcmd.ExecuteNonQuery();
                    lastId = sqlcmd.LastInsertedId;
                }
                if (bLocalCN) cn.Dispose();
            }
            catch (Exception ex)
            {
                UTIL.MsgE($"데이터베이스 실행오류\n{ex.Message}");
            }
            return lastId;
        }


        public static MarcBasicInfo GetBasicMarcInfo(string FullMarc)
        {
            MarcBasicInfo retval = new MarcBasicInfo();


            //상황에 맞게 업데이트 명령을 처리한다.
            var parser = new MarcParser();
            var ret = parser.ParseFullMarc(FullMarc);
            if (ret.success == false)
            {
                retval.Success = false;
                retval.Message = ret.message;
                return retval;
            }


            //ISBN와 가격 (처음나오는 020태그의 값을 적용)
            var tag_020 = parser.GetTag<MarcField>("020").FirstOrDefault();
            if (tag_020 != null)
            {
                retval.ISBN = tag_020.GetSubfieldValue('a');
                retval.Price = tag_020.GetSubfieldValue('c');
            }

            //저자(100 -> 110 -> 111 순으로 적용)  
            var tag_100 = parser.GetTag<MarcField>("100").FirstOrDefault();
            var tag_110 = parser.GetTag<MarcField>("110").FirstOrDefault();
            var tag_111 = parser.GetTag<MarcField>("111").FirstOrDefault();
            if (tag_111 != null)
                retval.Author = tag_111.GetSubfieldValue('a');
            else if (tag_110 != null)
                retval.Author = tag_110.GetSubfieldValue('a');
            else if (tag_100 != null)
                retval.Author = tag_100.GetSubfieldValue('a');

            //서명
            retval.Title = parser.GetTag<MarcSubfield>("245a").FirstOrDefault()?.Value ?? string.Empty;
            //string x245 = parser.GetTag<MarcSubfield>("245x").FirstOrDefault()?.Value ?? string.Empty;
            //string b245 = parser.GetTag<MarcSubfield>("245b").FirstOrDefault()?.Value ?? string.Empty;
            //if (x245 != "") retval.Title += " = " + x245;
            //if (b245 != "") retval.Title += " : " + b245;


            //출판사   
            var tag_264b = parser.GetTag<MarcSubfield>("264b").FirstOrDefault();
            var tag_260b = parser.GetTag<MarcSubfield>("260b").FirstOrDefault();
            if (tag_264b != null)
                retval.Publisher = tag_264b?.Value ?? string.Empty;
            else if (tag_260b != null)
                retval.Publisher = tag_260b?.Value ?? string.Empty;

            //056a
            retval.Tag056 = parser.GetTag("056a").FirstOrDefault() ?? string.Empty;
            retval.Tag008 = parser.GetTag("008").FirstOrDefault() ?? string.Empty;

            //총서명(440a)
            retval.fulltitle = parser.GetTag("440a").FirstOrDefault() ?? string.Empty;

            //총서번호(440v)
            retval.fulltitleno = parser.GetTag("440v").FirstOrDefault() ?? string.Empty;

            //권차(245n)
            retval.kwoncha = parser.GetTag("245n").FirstOrDefault() ?? string.Empty;

            //권차서명(245p)
            retval.kwoncha_title = parser.GetTag("245p").FirstOrDefault() ?? string.Empty;

            //판차(250a)
            retval.pancha = parser.GetTag("250a").FirstOrDefault() ?? string.Empty;

            retval.Success = true;
            return retval;
        }

        public static string Make_InsertQuery(string tableName, Dictionary<string, object> column_and_values)
        {
            string columns = string.Join(", ", column_and_values.Keys.Select(k => $"`{k}`"));
            string values = string.Join(", ", column_and_values.Values.Select(v =>
            {
                string s = v?.ToString() ?? "";
                return $"\"{s.Replace("\"", "\"\"")}\"";
            }));

            return $"INSERT INTO `{tableName}` ({columns}) VALUES ({values});";
        }

        public static string Make_UpdateQuery(string tableName, Dictionary<string, object> column_and_values, Dictionary<string, object> where_column_and_values)
        {
            string setClause = string.Join(", ", column_and_values.Select(kv =>
            {
                string s = kv.Value?.ToString() ?? "";
                return $"`{kv.Key}` = \"{s.Replace("\"", "\"\"")}\"";
            }));

            string whereClause = string.Join(" AND ", where_column_and_values.Select(kv =>
            {
                string s = kv.Value?.ToString() ?? "";
                return $"`{kv.Key}` = \"{s.Replace("\"", "\"\"")}\"";
            }));

            return $"UPDATE `{tableName}` SET {setClause} WHERE {whereClause};";
        }


        public static (bool result, int newidx, string message) UpdateMarc(int midx, string FullMarc, int v_grade,
            string etc1, string etc2, string url, string v_orgmarc)
        {
            var isUpdate = midx > 0;

            var v_username = PUB.user.UserName;
            var v_compidx = PUB.user.CompanyIdx;
            string v_date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");


            //상황에 맞게 업데이트 명령을 처리한다.
            var basicinfo = GetBasicMarcInfo(FullMarc);
            if (basicinfo.Success == false) return (false, 0, basicinfo.Message);




            // 1. DB 작업 (저장 전략 결정: Status 기준)
            if (isUpdate == false)
            {
                var insertData = new Dictionary<string, object>
                {
                    { "compidx", v_compidx },
                    { "ISBN", basicinfo.ISBN },
                    { "서명", basicinfo.Title },
                    { "저자", basicinfo.Author },
                    { "출판사", basicinfo.Publisher },
                    { "가격", basicinfo.Price },
                    { "총서명", basicinfo.fulltitle },
                    { "총서번호", basicinfo.fulltitleno },

                         { "권차", basicinfo.kwoncha },
                    { "권차서명", basicinfo.kwoncha_title },
                         { "판차", basicinfo.pancha },

                    { "marc", FullMarc },
                    { "marc_chk", "1" },
                    { "비고1", etc1 },
                    { "비고2", etc2 },
                    { "url", url },
                    { "division", basicinfo.Tag056 },
                    { "008tag", basicinfo.Tag008 },
                    { "date", v_date },
                    { "user", v_username },
                    { "grade", v_grade.ToString() }
                };

                string Incmd = Make_InsertQuery("Marc", insertData);
                PUB.log.Add("INSERT", string.Format("{0}({1}) : {2}", v_username, v_compidx, Incmd));

                var newIdx = Helper_DB.DB_Send_CMD_Insert_GetIdx(Incmd);
                if (newIdx > 0)
                {
                    midx = (int)newIdx;
                }
            }
            else
            {
                var updateData = new Dictionary<string, object>
                {
                    { "ISBN", basicinfo.ISBN },
                    { "서명", basicinfo.Title },
                    { "저자", basicinfo.Author },
                    { "출판사", basicinfo.Publisher },
                    { "가격", basicinfo.Price },
                    { "총서명", basicinfo.fulltitle },
                    { "총서번호", basicinfo.fulltitleno },

                              { "권차", basicinfo.kwoncha },
                    { "권차서명", basicinfo.kwoncha_title },
                         { "판차", basicinfo.pancha },

                    { "marc", FullMarc },
                    { "marc1", v_orgmarc },
                    { "marc_chk", "1" },
                    { "marc_chk1", "0" },
                    { "비고1", etc1 },
                    { "비고2", etc2 },
                    { "url", url },
                    { "division", basicinfo.Tag056 },
                    { "008tag", basicinfo.Tag008 },
                    { "date", v_date },
                    { "user", v_username },
                    { "grade", v_grade.ToString() }
                };

                var whereClause = new Dictionary<string, object>
                {
                    { "idx", midx },
                    { "compidx", v_compidx }
                };

                string U_cmd = Make_UpdateQuery("Marc", updateData, whereClause);
                PUB.log.Add("Update", string.Format("{0}({1}) : {2}", v_username, v_compidx, U_cmd.Replace("\r", " ").Replace("\n", " ")));
                var ret = Helper_DB.ExcuteNonQuery(U_cmd);
                if (ret.applyCount != 1)
                {
                    return (false, midx, $"업데이트된 행의 수가 1이 아닙니다. 적용된 행 수: {ret.applyCount}, 오류 메시지: {ret.errorMessage}");
                }
            }
            return (true, midx, string.Empty);
        }

    }
}
