using DingTalk.Api;
using DingTalk.Api.Request;
using DingTalk.Api.Response;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XYY.Common.Standard;
using XYY.Data.Standard.DingTalk;
using XYY.Model.Standard.DingTalk;

namespace XYY.Service.Standard.Finance
{
    public interface IDingTalk_PaymentService
    {
        Task<string> DingTalkStartProcessEvent(string code);
        Task ApplySubmit(string code);
        Task Refuse(string code);
        Task Cancel(string code);

        Task<PageResult<DingTalk_Payment>> QueryPage(QueryModel qm);
        Task<List<CascadeChoice>> GetPaymentCascadeChoice();
        Task<List<DingTalk_PaymentDetail>> GetPaymentDetails(int PaymentId);
    }
    public class DingTalk_PaymentService : IDingTalk_PaymentService
    {
        private readonly IDingDingBaseService _dingDingBaseService;
        private readonly IDingTalk_PaymentRepository _repository;
        public DingTalk_PaymentService(IDingDingBaseService dingDingBaseService, IDingTalk_PaymentRepository PaymentRepository)
        {
            _dingDingBaseService = dingDingBaseService;
            _repository = PaymentRepository;
        }
        #region 创建申请
        /// <summary>
        /// 获取任务详情
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public async Task<string> DingTalkStartProcessEvent(string code)
        {
            long taskid = 0;
            try
            {
                string token = await _dingDingBaseService.GetTokenAsCache();
                IDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/processinstance/get");
                OapiProcessinstanceGetRequest req = new OapiProcessinstanceGetRequest();
                req.ProcessInstanceId = code;
                OapiProcessinstanceGetResponse rsp = client.Execute(req, token);

                //获取用户信息
                var user = await _dingDingBaseService.GetUserInfo(rsp.ProcessInstance.OriginatorUserid);
                taskid = long.Parse(rsp.ProcessInstance.Tasks[0].Taskid);
                //数据转换
                var data = await ConvertPayment(rsp, user, code);
                List<DingTalk_PaymentDetail> detailData = data.details;
                //数据写入
                var longId = await _repository.InsertAsync(data);
                await _repository.insertDetail(Convert.ToInt32(longId), detailData);

                return "成功";
            }
            catch (Exception e)
            {
                string message = e.Message;
                if (taskid > 0)
                    message = await _dingDingBaseService.RefuseDingTalk(code, e.Message, taskid);
                return message;
            }
        }
        private async Task<DingTalk_Payment> ConvertPayment(OapiProcessinstanceGetResponse rsp, XYY.Model.Standard.User_Info user, string code)
        {
            var data = new DingTalk_Payment()
            {
                ProcessCode = code,
                originator_dept_id = rsp.ProcessInstance.OriginatorDeptId,
                originator_dept_name = rsp.ProcessInstance.OriginatorDeptName,
                originator_userid = rsp.ProcessInstance.OriginatorUserid,
                wms_userid = user.Id,
                wms_userName = user.NickName,
                title = rsp.ProcessInstance.Title,
                business_id = rsp.ProcessInstance.BusinessId,
                Status = XYY.Model.Standard.ObjectApplyEnum.待审,
                startTime = Convert.ToDateTime(rsp.ProcessInstance.CreateTime),
                finish_time = !string.IsNullOrEmpty(rsp.ProcessInstance.FinishTime) ? Convert.ToDateTime(rsp.ProcessInstance.FinishTime) : null,
            };
            List<DingTalk_PaymentDetail> details = new List<DingTalk_PaymentDetail>();
            foreach (var item in rsp.ProcessInstance.FormComponentValues)
            {
                switch (item.Name) {
                    case "付款流程":
                        data.PaymentFlow = item.Value;
                        break;
                    case "付款币种":
                        var currency = JsonConvert.DeserializeObject<List<string>>(item.Value);
                        if (currency != null && currency.Count() > 0)
                            data.PaymentCurrency = string.Join(',', currency);
                        break;
                    case "付款方式":
                        data.PaymentMethod = item.Value;
                        break;
                    case "付款总额":
                        data.PaymentTotal =Convert.ToDecimal(item.Value);
                        var upper=JsonConvert.DeserializeObject<form_ext_value_MoneyField>(item.ExtValue);
                        data.PaymentTotalUpper = upper.upper;
                        break;
                    case "支付日期":
                        data.PaymentTime =Convert.ToDateTime(item.Value);
                        break;
                    case "支付对象":
                        data.PaymentObject = item.Value;
                        break;
                    case "开户行":
                        data.DepositBank = item.Value;
                        break;
                    case "银行账户":
                        data.BankAccount = item.Value;
                        break;
                    case "图片":
                        var urls = JsonConvert.DeserializeObject<List<string>>(item.Value);
                        if (urls != null && urls.Count() > 0)
                            data.ImageUrl =string.Join(',', urls);
                        break;
                    case "附件":
                        //不做处理，无法访问，且易引发异常
                        //var urls1 = JsonConvert.DeserializeObject<List<fileInfo>>(item.Value);
                        //if(urls1!=null&&urls1.Count()>0)
                        //    data.InvoiceUrl = string.Join(',', urls1.Select(x=>x.fileId));
                        break;
                }
                if (item.Name=="表格") {
                    var value_value = JsonConvert.DeserializeObject<List<form_value_tablefieid>>(item.Value);
                    if (value_value.Count == 0) { continue; }
                    foreach (var value in value_value)
                    {
                        DingTalk_PaymentDetail detail = new DingTalk_PaymentDetail();
                        foreach (var ext in value.rowValue)
                        {
                            if (ext.label == "款项所属")
                            {
                                detail.companyDId = ext.extendValue.id;
                                detail.companyDName = ext.extendValue.name;
                            }
                            else if (ext.label == "付款类别")
                            {
                                var ids = ext.extendValue.fullIdPath.Split('/');
                                var names = ext.extendValue.fullNamePath.Split('/');
                                detail.Categories1_Id = Convert.ToInt32(ids[0]);
                                detail.Categories1_Name = names[0];
                                detail.Categories2_Id = ids.Count() > 1 ? Convert.ToInt32(ids[1]) : Convert.ToInt32(ids[0]);
                                detail.Categories2_Name = names.Count() > 1 ? names[1] : names[0];
                            }
                            else if (ext.label == "金额")
                            {
                                detail.PaymentAmount = Convert.ToDecimal(ext.value);
                            }
                            else if (ext.label == "摘要")
                            {
                                detail.Remark = ext.value.ToString();
                            }
                        }
                        details.Add(detail);
                    }
                }
            }
            data.PaymentAmount = details.Sum(x => x.PaymentAmount);
            data.details = details;
            await InsPaymentOptions(details);
            return data;
        }
        /// <summary>
        /// 8月旧模板数据同步
        /// 8.1-8.13 0点 之前（8.1-8.12）
        /// </summary>
        /// <returns></returns>
        private async Task<DingTalk_Payment> ConvertPayment8(OapiProcessinstanceGetResponse rsp, XYY.Model.Standard.User_Info user, string code)
        {
            var Status = XYY.Model.Standard.ObjectApplyEnum.待审;
            if (rsp.ProcessInstance.Result == "agree") { Status = XYY.Model.Standard.ObjectApplyEnum.通过; }
            else if (rsp.ProcessInstance.Result == "refuse") { Status = XYY.Model.Standard.ObjectApplyEnum.驳回; }
            else if (rsp.ProcessInstance.Status == "CANCELED") { Status = XYY.Model.Standard.ObjectApplyEnum.撤销; }

            var data = new DingTalk_Payment()
            {
                ProcessCode = code,
                originator_dept_id = rsp.ProcessInstance.OriginatorDeptId,
                originator_dept_name = rsp.ProcessInstance.OriginatorDeptName,
                originator_userid = rsp.ProcessInstance.OriginatorUserid,
                wms_userid = user.Id,
                wms_userName = user.NickName,
                title = rsp.ProcessInstance.Title,
                business_id = rsp.ProcessInstance.BusinessId,
                Status = Status,
                startTime = Convert.ToDateTime(rsp.ProcessInstance.CreateTime),
                finish_time = !string.IsNullOrEmpty(rsp.ProcessInstance.FinishTime) ? Convert.ToDateTime(rsp.ProcessInstance.FinishTime) : null,
            };
            List<DingTalk_PaymentDetail> details = new List<DingTalk_PaymentDetail>();
            int companyDId = 0;string companyDName = string.Empty;
            foreach (var item in rsp.ProcessInstance.FormComponentValues)
            {
                switch (item.Name)
                {
                    case "款项所属":
                        var json = JsonConvert.DeserializeObject<form_control_extendValue>(item.ExtValue);
                        companyDId = json.id;
                        companyDName = json.name;
                        break;
                    case "付款流程":
                        data.PaymentFlow = item.Value;
                        break;
                    case "付款币种":
                        var currency = JsonConvert.DeserializeObject<List<string>>(item.Value);
                        if (currency != null && currency.Count() > 0)
                            data.PaymentCurrency = string.Join(',', currency);
                        break;
                    case "付款方式":
                        data.PaymentMethod = item.Value;
                        break;
                    case "付款总额":
                        data.PaymentTotal = Convert.ToDecimal(item.Value);
                        var upper = JsonConvert.DeserializeObject<form_ext_value_MoneyField>(item.ExtValue);
                        data.PaymentTotalUpper = upper.upper;
                        break;
                    case "支付日期":
                        data.PaymentTime = Convert.ToDateTime(item.Value);
                        break;
                    case "支付对象":
                        data.PaymentObject = item.Value;
                        break;
                    case "开户行":
                        data.DepositBank = item.Value;
                        break;
                    case "银行账户":
                        data.BankAccount = item.Value;
                        break;
                    case "图片":
                        var urls = JsonConvert.DeserializeObject<List<string>>(item.Value);
                        if (urls != null && urls.Count() > 0)
                            data.ImageUrl = string.Join(',', urls);
                        break;
                    case "附件":
                        var urls1 = JsonConvert.DeserializeObject<List<fileInfo>>(item.Value);
                        if (urls1 != null && urls1.Count() > 0)
                            data.InvoiceUrl = string.Join(',', urls1.Select(x => x.fileId));
                        break;
                }
                if (item.Name == "表格")
                {
                    var value_value = JsonConvert.DeserializeObject<List<form_value_tablefieid>>(item.Value);
                    if (value_value.Count == 0) { continue; }
                    foreach (var value in value_value)
                    {
                        DingTalk_PaymentDetail detail = new DingTalk_PaymentDetail();
                        detail.companyDId = companyDId;
                        detail.companyDName = companyDName;
                        foreach (var ext in value.rowValue)
                        {
                            if (ext.label == "付款类别")
                            {
                                var ids = ext.extendValue.fullIdPath.Split('/');
                                var names = ext.extendValue.fullNamePath.Split('/');
                                detail.Categories1_Id = Convert.ToInt32(ids[0]);
                                detail.Categories1_Name = names[0];
                                detail.Categories2_Id = ids.Count() > 1 ? Convert.ToInt32(ids[1]) : Convert.ToInt32(ids[0]);
                                detail.Categories2_Name = names.Count() > 1 ? names[1] : names[0];
                            }
                            else if (ext.label == "金额")
                            {
                                detail.PaymentAmount = Convert.ToDecimal(ext.value);
                            }
                            else if (ext.label == "事由")
                            {
                                detail.Remark = ext.value.ToString();
                            }
                        }
                        details.Add(detail);
                    }
                }
            }
            data.PaymentAmount = details.Sum(x => x.PaymentAmount);
            data.details = details;
            await InsPaymentOptions(details);
            return data;
        }
        private async Task InsPaymentOptions(List<DingTalk_PaymentDetail> details)
        {
            List<DingTalk_PaymentOptions> insertOptions = new List<DingTalk_PaymentOptions>();
            List<DingTalk_PaymentOptions> updateOptions = new List<DingTalk_PaymentOptions>();
            //表数据量不大，取全表
            var dbData = await _repository.GetAllPaymentOptions();
            foreach (var item in details)
            {
                //检测款项所属
                var company = dbData.Where(x => x.DId == item.companyDId && x.OptionType == OptionTypeEnum.paymentcompany).FirstOrDefault();
                if (insertOptions.Any(x => x.DId == item.companyDId)) { }
                else if (company == null)
                {
                    DingTalk_PaymentOptions model = new DingTalk_PaymentOptions()
                    {
                        DId = item.companyDId,
                        DName = item.companyDName,
                        OptionType = OptionTypeEnum.paymentcompany,
                        DParentId = 0,
                        LevelNum = 0,
                    };
                    insertOptions.Add(model);
                }
                else if (company.DName != item.companyDName)
                {
                    company.DName = item.companyDName; company.LevelNum = 0; company.DParentId = 0; updateOptions.Add(company);
                }
                //检测一级菜单
                var level0 = dbData.Where(x => x.DId == item.Categories1_Id && x.OptionType == OptionTypeEnum.paymentcategory).FirstOrDefault();
                if (insertOptions.Any(x => x.DId == item.Categories1_Id)) { }
                else if (level0 == null)
                {
                    DingTalk_PaymentOptions model = new DingTalk_PaymentOptions()
                    {
                        DId = item.Categories1_Id ?? 0,
                        DName = item.Categories1_Name,
                        OptionType = OptionTypeEnum.paymentcategory,
                        DParentId = 0,
                        LevelNum = 0,
                    };
                    insertOptions.Add(model);
                }
                else if (level0.DName != item.Categories1_Name)
                {
                    level0.DName = item.Categories1_Name; level0.LevelNum = 0; level0.DParentId = 0; updateOptions.Add(level0);
                }
                //检测二级菜单 没有二级菜单的时候，会默认写一级菜单的数据
                var level1 = dbData.Where(x => x.DId == item.Categories2_Id && x.OptionType == OptionTypeEnum.paymentcategory).FirstOrDefault();
                if (insertOptions.Any(x => x.DId == item.Categories2_Id)) { }
                else if (level1 == null)
                {
                    DingTalk_PaymentOptions model = new DingTalk_PaymentOptions()
                    {
                        DId = item.Categories2_Id ?? 0,
                        DName = item.Categories2_Name,
                        OptionType = OptionTypeEnum.paymentcategory,
                        DParentId = item.Categories1_Id,
                        LevelNum = 1,
                    };
                    insertOptions.Add(model);
                }
                else if (level1.DName != item.Categories1_Name)
                {
                    level1.DName = item.Categories2_Name; level1.LevelNum = 1; level1.DParentId = item.Categories1_Id; updateOptions.Add(level1);
                }
            }
            if (insertOptions.Count > 0) { await _repository.insertOptions(insertOptions); }
            if (updateOptions.Count > 0) { await _repository.BatchUpdateOptionsEntiy(updateOptions); }
        }

        #endregion

        #region 审核、撤销操作
        public async Task ApplySubmit(string code)
        {
            var records = await _repository.QueryAsync(i => i.ProcessCode == code);
            var record = records.FirstOrDefault();
            if (record == null)
            {
                throw new Exception($"不存在审批记录:{code}");
            }
            record.Status = XYY.Model.Standard.ObjectApplyEnum.通过;
            record.finish_time = DateTime.Now;
            await _repository.UpdateAsync(record);
        }
        public async Task Refuse(string code)
        {
            await _repository.UpdateByConditionAsync(i => i.ProcessCode == code, new
            {
                Status = (int)XYY.Model.Standard.ObjectApplyEnum.驳回,
                finish_time = DateTime.Now,
            });
        }
        public async Task Cancel(string code)
        {
            await _repository.UpdateByConditionAsync(i => i.ProcessCode == code, new
            {
                Status = (int)XYY.Model.Standard.ObjectApplyEnum.撤销,
                finish_time = DateTime.Now,
            });
        }
        #endregion

        #region 申请记录查询
        public async Task<PageResult<DingTalk_Payment>> QueryPage(QueryModel qm)
        {
            return await _repository.QueryPage(qm);
        }
        public async Task<List<DingTalk_PaymentOptions>> GetOptionsByType(int OptionType)
        {
            return await _repository.GetOptionsByType(OptionType);
        }
        public async Task<List<CascadeChoice>> GetPaymentCascadeChoice()
        {
            List<CascadeChoice> cascadeChoices = new List<CascadeChoice>();
            var dbData = await GetOptionsByType((int)OptionTypeEnum.paymentcategory);
            foreach (var item in dbData.Where(x => x.LevelNum == 0))
            {
                var child = await ConvertToChoice(dbData.Where(x => x.DParentId == item.DId).ToList());
                CascadeChoice model = new CascadeChoice()
                {
                    label = item.DName,
                    value = item.DId,
                    children = child.Count > 0 ? child : new List<Choice>() { new Choice() { label = item.DName, value = item.DId, } },
                };
                cascadeChoices.Add(model);
            }
            return cascadeChoices;
        }
        private async Task<List<Choice>> ConvertToChoice(List<DingTalk_PaymentOptions> options)
        {
            List<Choice> choices = new List<Choice>();
            foreach (var item in options)
            {
                choices.Add(new Choice()
                {
                    label = item.DName,
                    value = item.DId,
                });
            }
            return choices;
        }
        public async Task<List<DingTalk_PaymentDetail>> GetPaymentDetails(int PaymentId)
        {
            return await _repository.GetPaymentDetails(PaymentId);
        }
        #endregion
    }
}
