using Microsoft.EntityFrameworkCore;
using Nancy.Json;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RB_MES_API.Context;
using RB_MES_API.Controllers;
using RB_MES_API.Controllers.Cloud;
using RB_MES_API.Models;
using RB_MES_API.Models.Cloud;
using RB_MES_API.Models.Pangu;
using RB_MES_APICore.Models.Pangu;
using RB_MES_APICore.Models;
using System.Data;
using System.Net;
using System.Reflection;
using System.Text;

namespace RB_MES_API.HostService
{
    /// <summary>
    /// 定时推送服务
    /// </summary>
    public class PushMesInterface : IPushMesInterface,IDisposable
    {
        private List<Timer> _timer = new List<Timer>();
        private readonly RBContext _context;
        private readonly IConfiguration _config;
        private readonly IShareController _shareController;
        private readonly IKDCloudHttpClient _KdhttpClient;
        private readonly IKDSqlHelper _kdsqlhelper;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context">注入的数据库连接</param>
        /// <param name="shareController">共享方法</param>
        /// <param name="httpClient">金蝶API</param>
        /// <param name="configuration"></param>
        /// <param name="kdsqlhelper"></param>
        public PushMesInterface(RBContext context, IConfiguration configuration, IShareController shareController
            , IKDCloudHttpClient httpClient, IKDSqlHelper kdsqlhelper)
        {
            _context = context;
            _config = configuration;
            _shareController = shareController;
            _KdhttpClient = httpClient;
            _kdsqlhelper = kdsqlhelper;
        }
        /// <inheritdoc/>
        public void Dispose()
        {
            foreach (Timer timer in _timer)
            {
                timer?.Dispose();
            }
        }
        private void DoWork(object state)
        {
            // 定时任务逻辑
            try
            {
                List<CloudBillQuery> queryJsons = _shareController.GetAPIList<CloudBillQuery>().Result;
                var clouds = queryJsons.Where(s => s.GetFormID.FDocType == state.ToString()).ToList();
                if (clouds.Count > 0)
                {
                    new TaskFactory().StartNew(async () =>
                    {
                        CloudBillQuery query = clouds.FirstOrDefault();
                        //启动新线程执行推送业务
                        Type type = this.GetType();
                        MethodInfo? methodInfo = type.GetMethod(query.GetFormID.FActionName);
                        if (methodInfo != null)
                        {
                            object[] para = new object[] { query.GetFormID.FID, query.GetFormID.functions.FUrl };

                            methodInfo.Invoke(this, para);
                        }
                    });
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(string.Format("TimedHostedService的BackgroundServiceWork发生错误：{0}", ex.Message));
            }
        }
        public async void DoTimerWorkAsyn()
        {
            await Task.Run(() => LoadLocalStaticRequest());
            try
            {
                string v = LocalStaticRequest.GetSystemProfile(1, "AutoPushTask");
                int at = 0;
                int.TryParse(v, out at);
                if (at > 0)
                {
                    //只要FTimer > 0的接口都有一个属于自己的定时器！
                    List<ApiHostService> systimeds = _shareController.GetAPIList<ApiHostService>().Result;
                    if (systimeds.Count == 0) { return; }
                    var sys = from a in systimeds
                              select new
                              {
                                  worktime = a.FTimer,
                                  name = a.FDocType
                              };
                    foreach (var item in sys)
                    {
                        _timer.Add(new Timer(DoWork, item.name, TimeSpan.Zero, TimeSpan.FromMinutes(item.worktime)));
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(string.Format("TimedHostedService的StartAsync发生错误：{0}", ex.Message));
            }
            return;
        }
        /// <summary>
        /// 优先加载系统必须的静态数据
        /// </summary>
        private void LoadLocalStaticRequest()
        {
            try
            {
                //如果缓存中不存在，就从数据库里取出来再放进缓存
                List<SystemProfile> sys = CacheHelper.GetCache<List<SystemProfile>>("sys");
                if (sys == null)
                {
                    //先初始化全局变量
                    sys = _context.r_SystemProfiles!.AsNoTracking().ToList();
                    if (sys.Any())
                    {
                        CacheHelper.Set_AbsluteExpire("sys", sys);
                    }
                }
                LocalStaticRequest.sysprofile = sys;    //定时刷新
            }
            catch (Exception ex)
            {
                //如果金蝶Cloud登录失败，就会发生异常，可能导致程序崩溃...
                string msg = ex.Message;
                LogHelper.WriteLog(string.Format("TimedHostedService的LoadHostFunctionAsync发生错误：{0}", msg));
            }

            //将计量单位加入缓存
            Thread uthread = new Thread(AddUnitCache);
            uthread.Start();

            //无论是否推送，静态全局设置应该首先加载
            string orgid = LocalStaticRequest.GetSystemProfile(2, "DefaultORGID");
            string orgno = LocalStaticRequest.GetSystemProfile(2, "DefaultORG");
            LocalStaticRequest.DefaultOrgID = string.IsNullOrEmpty(orgid) ? 1 : int.Parse(orgid);
            LocalStaticRequest.DefaultOrg = string.IsNullOrEmpty(orgno) ? "100" : orgno;
        }
        private void AddUnitCache()
        {
            try
            {
                List<BD_UNIT> units = new List<BD_UNIT>();
                string messstr = string.Empty;
                string fileds = "FUNITID,FNumber,FName,FIsBaseUnit,FPrecision,FDocumentStatus,FForbidStatus,FConvertDenominator,FConvertNumerator,FRoundType";
                //从云星空中把所有计量单位获取过来
                BillQuery queryJson = new BillQuery()
                {
                    FieldKeys = fileds,
                    FormId = "BD_UNIT",
                    FilterString = "FDOCUMENTSTATUS='C' AND FFORBIDSTATUS='A'"
                };
                if (!LocalStaticRequest.Islogin)
                {
                    if (!_KdhttpClient.LoginErp().Result)
                    {
                        return;
                    }
                }
                //从云星空查询
                string jsonstr = JsonConvert.SerializeObject(queryJson);
                List<object> Parameters = new List<object>();
                Parameters.Add(jsonstr);
                string content = JsonConvert.SerializeObject(Parameters);
                string url = LocalStaticRequest.GetSystemProfile(4, "TokenUrl");
                string myurl = url + "Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.ExecuteBillQuery.common.kdsvc";
                //string result = _KdhttpClient.AsyncRequest();
                string result = _KdhttpClient.AsyncRequest(myurl, content);
                //如果访问正常
                if (_KdhttpClient.CloudExecuteQueryStatus(result, ref messstr))
                {
                    //将获取到的结果转为List<BD_UNIT>
                    units = _shareController.GetNewObjForChild<BD_UNIT>(fileds, result, ref messstr);
                    CacheHelper.Set_AbsluteExpire("BD_UNIT", units);
                }
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(string.Format("TimedHostedService的AddUnitCache发生错误：{0}", e.Message));
            }
        }
        /// <summary>
        /// 将自定义SQL查询结果推送给MES
        /// </summary>
        /// <param name="formtypeid"></param>
        /// <param name="url"></param>
        public void GetSelfDBbaseAsync(int formtypeid, string url)
        {
            string result = GetCustomRequestTBAsync(formtypeid);
            List<CloudBillQuery> query = _shareController.GetAPIList<CloudBillQuery>().Result;
            if (query.Count == 0) return;
            string doctype = query.Find(s => s.FFormIDTypeID == formtypeid).GetFormID.FDocType;
            if (string.IsNullOrEmpty(result)) { return; }
            try
            {
                //可以将datatable直接转为JSON，但这里约定的格式用不上
                //new JsonSerializer().Deserialize(new JsonTextReader(new StringReader(result)));
                JArray array = (JArray)JsonConvert.DeserializeObject(result);
                if (array == null || array.Count == 0) { return; }
                PanguPostBill mes = new PanguPostBill()
                {
                    DocType = doctype,
                    DataSet = _shareController.GetClassList(array)
                };
                string json = JsonConvert.SerializeObject(mes);
                ////推送给MES
                //PanguBreakJson mesjson = PushMesData(json).Result;
                //if (mesjson.Status)
                //{
                //    LogHelper.WriteLog(string.Format("推送【{0}】成功：{1}", doctype, json), doctype);
                //}
                //else
                //{
                //    LogHelper.WriteLog(string.Format("TimedHostedService的GetSelfDBbaseAsync方法推送【{0}】失败：{1}\n原始数据：{2}", doctype, mesjson.Message, json), doctype);
                //}
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(string.Format("TimedHostedService推送【{0}】动态GetSelfDBbaseAsync方法出错：{1}", doctype, ex.Message), doctype);
            }
        }
        /// <summary>
        /// 查询Cloud表单。注意：基础资料表单查询有多用途，FFieldKeys中必须有Key。同理...
        /// </summary>
        /// <param name="formtypeid"></param>
        /// <param name="url"></param>
        public async void GetBDbase(int formtypeid, string url)      //动态引用
        {
            string reason = string.Empty;
            List<CloudBillQuery> queryJsons = await _shareController.GetAPIList<CloudBillQuery>();
            if (queryJsons.Count == 0)
            {
                LogHelper.WriteLog("TimedHostedService的GetBDbase方法_shareController.GetAPIList<CloudBillQuery>()缓存过期");
                return;
            }
            else
            {
                var formtype = queryJsons.Where(s => s.GetFormID.FID == formtypeid).FirstOrDefault()!.GetFormID;
                int fid = formtype.FFunctionID;
                string doctype = formtype.FDocType;
                var queryJson = from a in queryJsons.Where(s => s.GetFormID.FID == formtypeid)
                                select new BillQuery
                                {
                                    FieldKeys = a.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg),
                                    FormId = a.GetFormID.FDBName,
                                    FilterString = a.FFiledString.Replace("GETDATE", DateTime.Now.ToShortDateString()).Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg),
                                    Limit = a.FLimit,
                                    OrderString = a.FOrderString,
                                    StartRow = a.FStartRow,
                                    SubSystemId = a.FSubSystemID,
                                    TopRowCount = a.FTopRowCount
                                };
                if (queryJson.Any())
                {
                    try
                    {
                        if (!LocalStaticRequest.Islogin)
                        {
                            await _KdhttpClient.LoginErp();
                        }
                        if (!LocalStaticRequest.Islogin)
                        {
                            reason = "KdhttpClient登录失败";
                            goto EBread;
                        }
                        string jsonstr = JsonConvert.SerializeObject(queryJson.FirstOrDefault());
                        List<object> Parameters = new List<object>() { jsonstr };
                        string content = JsonConvert.SerializeObject(Parameters);
                        //string result = _KdhttpClient.AsyncRequest();
                        string result = _KdhttpClient.AsyncRequest(url, content);
                        if (_KdhttpClient.CloudExecuteQueryStatus(result, ref reason)) //查询表单数据接口使用
                        {
                            if (!string.IsNullOrEmpty(reason)) { goto EBread; }
                            JArray jArray = (JArray)JsonConvert.DeserializeObject(result.ToString()!)!;  //特别注意！！！
                                                                                                         //为dt添加数据
                            if (jArray != null && jArray.Count > 0)
                            {
                                string fileds = queryJson.FirstOrDefault()!.FieldKeys!;
                                //根据字段对应关系分配字段
                                DataTable dt = _shareController.JsonConvertDatatable(fileds, result, ref reason);
                                if (!string.IsNullOrEmpty(reason)) { goto EBread; }
                                //DataTable dt = CreateFFieldKeys(fileds);
                                //由于jArray没有字段名，只能先装填DataTable
                                //LoadDatatable(jArray, ref dt);
                                if (dt.Rows.Count > 0)
                                {
                                    List<Dictionary<string, object>> dic = GetClassList(fid, doctype, dt);
                                    if (dic.Any())
                                    {
                                        //json = JsonConvert.SerializeObject(dic);
                                        PanguPostBill mes = new PanguPostBill()
                                        {
                                            DocType = doctype,
                                            DataSet = dic
                                        };
                                        string json = JsonConvert.SerializeObject(mes);
                                        ////推送给MES
                                        //PanguBreakJson mesjson = await PushMesData(json);
                                        //if (mesjson.Status)
                                        //{
                                        //    reason = string.Format("推送【{0}】成功：{1}", doctype, json);
                                        //}
                                        //else
                                        //{
                                        //    reason = string.Format("TimedHostedService的GetBDbase方法推送【{0}】失败：{1}\r原始数据：{2}", doctype, mesjson.Message, json);
                                        //}
                                    }
                                    else
                                    {
                                        reason = string.Format("TimedHostedService的GetBDbase方法推送【{0}】失败：!GetClassList(fid, doctype, dt)Any()：{1}}", doctype, result);
                                    }
                                }
                            }
                        }
                        else
                        {
                            //将message写入错误日志
                            reason = string.Format("TimedHostedService的GetBDbase方法推送【{0}】返回结果：{1}", doctype, result.ToString());
                        }
                    }
                    catch (Exception ex)
                    {
                        reason = string.Format("TimedHostedService的GetBDbase方法推送【{0}】过程中发生错误：{1}", doctype, ex.Message);
                    }
                }
            EBread:
                if (!string.IsNullOrEmpty(reason)) { LogHelper.WriteLog(reason, doctype); }

            }
        }
        /// <summary>
        /// 向第三方系统推送数据
        /// </summary>
        /// <param name="inputdata">推送的JSON文本</param>
        /// <returns></returns>
        private async Task<PanguBreakJson> PushMesData(string inputdata)
        {
            PanguBreakJson breadJson = new PanguBreakJson() { Status = false };
            string mesurl = _config.GetConnectionString("MesUrl");
            mesurl += mesurl.Substring(mesurl.Length - 1, 1) == "/" ? "" : "/";         //如果最后一个字符不是"/"就添加上去
            mesurl += "updateimsdata";
            HttpWebRequest reqest = (HttpWebRequest)WebRequest.Create(mesurl);
            reqest.Timeout = 20 * 60 * 1000;
            reqest.Method = "POST";
            reqest.ContentType = "application/json";
            try
            {
                byte[] bytes = Encoding.UTF8.GetBytes(inputdata);
                reqest.ContentLength = bytes.Length;
                string result = string.Empty;
                reqest.GetRequestStream().Write(bytes, 0, bytes.Length);
                HttpWebResponse webResponse = (HttpWebResponse)await reqest.GetResponseAsync();
                //读取返回数据
                StreamReader streamReader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8);
                result = streamReader.ReadToEnd();
                streamReader.Dispose();
                webResponse.Dispose();

                breadJson = JsonConvert.DeserializeObject<PanguBreakJson>(result);
            }
            catch (Exception ex)
            {
                breadJson.Message = ex.Message;
            }
            return breadJson;
        }
        /// <summary>
        /// 根据字段转换的目标名称，从table获取数据，生成新的键值对集合。
        /// 适用于定时推送任务！或其他不需要存储的动态对象。与接口_shareController中同名方法有异曲同工之处
        /// </summary>
        /// <param name="fid">FFunctionID</param>
        /// <param name="decname">目标表名</param>
        /// <param name="table">需要转换的表，没有字段名</param>
        /// <returns></returns>
        private List<Dictionary<string, object>> GetClassList(int fid, string decname, DataTable table)
        {
            List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
            //先检查是否需要作字段转换
            List<SelectClumnConvert> scc = _shareController.GetAPIList<SelectClumnConvert>().Result;
            scc = scc.Where(s => s.FFunctionID == fid && s.FDesTableName.Equals(decname)).ToList();
            if (scc.Any())
            {
                foreach (DataRow row in table.Rows)
                {
                    Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
                    foreach (SelectClumnConvert clumnConvert in scc)
                    {
                        string oldname = clumnConvert.FSourceName;
                        if (table.Columns.Contains(oldname))
                        {
                            string newname = clumnConvert.FDesName;
                            keyValuePairs.Add(newname, row[oldname].ToString()!);
                        }
                    }
                    list.Add(keyValuePairs);
                }
            }
            return list;
        }
        /// <summary>
        /// 来源于接口_ChiledSelect，但由于本地服务启动与依赖性注入底层冲突而不能直接调用接口
        /// </summary>
        /// <param name="formtypeid"></param>
        /// <returns></returns>
        private string GetCustomRequestTBAsync(int formtypeid)
        {
            //throw new NotImplementedException();
            string tbjson = string.Empty;
            try
            {
                List<CloudBillQuery> queryJsons = _shareController.GetAPIList<CloudBillQuery>().Result;
                var query = queryJsons.Where(s => s.FFormIDTypeID == formtypeid).FirstOrDefault();
                //查询脚本
                string sql = query.FFieldKeys.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString());
                if (string.IsNullOrEmpty(sql)) { return tbjson; }
                //这个方法无法携带参数，只能先写在查询语句里，然后替换之...
                string param = query.FFiledString.Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString()).Replace("@defaultorgno", LocalStaticRequest.DefaultOrg);
                if (!string.IsNullOrEmpty(param))
                {
                    string[] strings = param.Split(',');
                    foreach (string s in strings)
                    {
                        string[] cs = s.Split('=');
                        string key = cs[0];
                        string value = cs[1].Replace("GETDATE", DateTime.Now.ToShortDateString()).Replace("@defaultorgid", LocalStaticRequest.DefaultOrgID.ToString());
                        sql = sql.Replace(key, value);
                    }
                }

                string doctype = query.GetFormID.FDocType;
                string costomsql = _config.GetValue<string>("CustomRequestRemoteSql");

                if (costomsql.ToUpper() == "Y")
                {
                    string result = GetCustomReaderAsync(sql, "ExecuteDataSet");
                    //string errmess = string.Empty;
                    try
                    {
                        if (string.IsNullOrWhiteSpace(result)) { return tbjson; }
                        //先将返回数据格式化
                        JObject jobj = (JObject)JsonConvert.DeserializeObject(result);
                        tbjson = JsonConvert.SerializeObject(jobj["Table"]);
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteLog(string.Format("TimedHostedService的BackgroundServiceWork({0})本地查询{1}发生错误：{2}", formtypeid, doctype, ex.Message));
                    }
                }
                else
                {
                    string mess = string.Empty;
                    //实施本地化查询
                    DataSet dataSet = _kdsqlhelper.GetDataSet(CommandType.Text, sql, null, ref mess, true);
                    if (mess != null || dataSet == null)
                    {
                        LogHelper.WriteLog(string.Format("TimedHostedService的BackgroundServiceWork({0})网络查询{1}发生错误：{2}", formtypeid, doctype, mess));
                    }
                    else if (dataSet.Tables.Count != 0)
                    {
                        DataTable dataTable = dataSet.Tables[0];
                        tbjson = JsonConvert.SerializeObject(dataTable);
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(string.Format("TimedHostedService的BackgroundServiceWork({0})发生错误：{1}", formtypeid, ex.Message));
            }
            return tbjson;
        }
        /// <summary>
        /// 来源于接口_ChiledSelect，但由于本地服务启动与依赖性注入底层冲突而不能直接调用接口
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="servicesstubname"></param>
        /// <returns></returns>
        private string GetCustomReaderAsync(string sql, string servicesstubname)
        {
            string tbjson = string.Empty;
            try
            {
                //获取自定义查询的服务器端插件地址...
                string apiid = LocalStaticRequest.GetSystemProfile(1, "APIGrouID");
                var sv = _context.r_CustomServices.Where(s => s.FApiGroupID == int.Parse(apiid) && s.FActionName == servicesstubname);
                if (!sv.Any()) { return tbjson; }
                CustomService customservice = sv.FirstOrDefault();
                if (customservice == null) { return tbjson; }
                //查询前必须先登录...
                if (!LocalStaticRequest.Islogin) { _KdhttpClient.LoginErp(); }                
                if (!LocalStaticRequest.Islogin) { return tbjson; }
                string url = LocalStaticRequest.GetSystemProfile(4, "TokenUrl");
                url += url.Substring(url.Length - 1, 1) == "/" ? "" : "/";         //如果最后一个字符不是"/"就添加上去
                url += customservice.FNamespace + "." + customservice.FClassName + "." + customservice.FActionName + "," + customservice.FAppComponents;
                List<object> Parameters = new List<object>
            {
                sql//服务中可添加参数SqlParam[]，但要引用金蝶组件！
            };
                string content = JsonConvert.SerializeObject(Parameters);
                tbjson = _KdhttpClient.AsyncRequest(url, content);
            }
            catch (Exception ex)
            {
                LogHelper.WriteLog(string.Format("TimedHostedService的BackgroundServiceWork({0},{1})发生错误：{1}", sql, servicesstubname, ex.Message));
            }
            return tbjson;
            //throw new NotImplementedException();
        }

    }
}
