using Hncore.Infrastructure.Common;
using Hncore.Infrastructure.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml;

namespace Hncore.Pass.PaymentCenter.WxPay.WechatJsPay
{
    /// <summary>
    /// 微信小程序、公众号支付，H5支付 数据签名校验
    /// </summary>
    public class WxPayChecker: SortedDictionary<string, string>
    {
        public string SignType { get; set; } = "MD5";// "HMAC-SHA256";
        public string MchKey { get; set; }

        /**
        * 生成时间戳，标准北京时间，时区为东八区，自1970年1月1日 0点0分0秒以来的秒数
         * @return 时间戳
        */
        public string GenerateTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        /**
        * 生成随机串，随机串包含字母或数字
        * @return 随机串
        */
        public string GenerateNonceStr()
        {
            return Guid.NewGuid().ToString("N");
        }

        public virtual string CreateSign()
        {
            string sign = "";
            foreach (var item in this)
            {
                if (item.Key == "sign"|| item.Key == "paySign") continue;
                sign += item.Key + "=" + item.Value + "&";
            }
            if(this.MchKey.Has())
                sign += "key=" + this.MchKey;
            LogHelper.Trace("CreateSign Str", sign);
            if (SignType == "MD5")
            {
                sign = SecurityHelper.GetMd5Hash(sign);
            }
            else if (SignType == "HMAC-SHA256")
            {
                sign = CalcHMACSHA256Hash(sign, this.MchKey);
            }
            this["sign"] = sign;
            return sign;
        }

        public bool IsSet(string key)
        {
            if (!this.ContainsKey(key)) return false;
            return this[key].Has();
        }

        public virtual bool CheckSign()
        {
            //如果没有设置签名，则跳过检测
            if (!this.IsSet("sign"))
            {
                LogHelper.Error("CheckSign", "签名不存在!");
                return false;
            }
            //获取接收到的签名
            string return_sign = this["sign"];          

            //在本地计算新的签名
            string cal_sign = CreateSign();

            if (cal_sign == return_sign)
            {
                return true;
            }
            return true;
        }


        private string CalcHMACSHA256Hash(string plaintext, string salt)
        {
            string result = "";
            var enc = Encoding.Default;
            byte[]
            baText2BeHashed = enc.GetBytes(plaintext),
            baSalt = enc.GetBytes(salt);
            System.Security.Cryptography.HMACSHA256 hasher = new HMACSHA256(baSalt);
            byte[] baHashedText = hasher.ComputeHash(baText2BeHashed);
            result = string.Join("", baHashedText.ToList().Select(b => b.ToString("x2")).ToArray());
            return result;
        }

        public string ToXml()
        {
            CreateSign();
            StringBuilder sb = new StringBuilder("<xml>\n");
            foreach (var item in this)
            {
                string key = item.Key;
                sb.Append("\t<").Append(key).Append("><![CDATA[").Append(item.Value).Append("]]></").Append(key)
                    .Append(">\n");
            }

            return sb.Append("</xml>").ToString();
        }

        public WxPayChecker FromXml(string xml)
        {
            if (string.IsNullOrEmpty(xml))
            {
                return null;
            }

            LogHelper.Info("FromXml", xml);

            this.Clear();
            XmlDocument xmlDoc = new XmlDocument() { XmlResolver = null };
            xmlDoc.LoadXml(xml);
            XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
            XmlNodeList nodes = xmlNode.ChildNodes;
            foreach (XmlNode xn in nodes)
            {
                XmlElement xe = (XmlElement)xn;
                this[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
            }
            if (!CheckSign())
            {
                return null;
            }
            return this;
        }

        public WxPayChecker FromXmlNoCheckSign(string xml)
        {
            if (string.IsNullOrEmpty(xml))
            {
                return null;
            }

            LogHelper.Info("FromXml", xml);

            this.Clear();
            XmlDocument xmlDoc = new XmlDocument() { XmlResolver = null };
            xmlDoc.LoadXml(xml);
            XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
            XmlNodeList nodes = xmlNode.ChildNodes;
            foreach (XmlNode xn in nodes)
            {
                XmlElement xe = (XmlElement)xn;
                this[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
            }           
            return this;
        }

    }
}