using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;

namespace Webxpay_Sample_API.NET_Framework.Controllers
{
    public class HomeController : Controller
    {

        string publicKey = @"-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEfR0daQF6cK2RuO/f+/3ENvdQ
gi/bw8ICgCJVt3O0IR5hiYpx0zYAX8IGTj5w5+OTpy/L4dC6vfG7YaguLfJwZRJ3
lDPHrG4pbu+ztKZSm0czP+X1U9MznhH8s+yphjtN2F/aQKu5fuLoJauYW66z0xmj
vCEBVlFRfecQuQ49uwIDAQAB
-----END PUBLIC KEY-----";


        [HttpGet]
        public ActionResult Index()
        {
            var secretKey = "6257b767-1ff5-405b-b38b-5b4d39c8453a";
            var orderNumber = Guid.NewGuid().ToString();
            var amount = 100;

            var encPaymentString = EncryptPayments(publicKey, orderNumber, amount);
            ViewBag.PaymentInfo = encPaymentString;
            ViewBag.SecretKey = secretKey;
            return View();
        }


        // You can setup your redirect URL from Webxpay Dashboard
        // After payment finish, the web browser will redirect to this method
        [HttpPost]
        public ActionResult DecryptReturnedPaymentInfor(WebxpayResponse response)
        {
            var isValid = Verify(response.Payment, response.Signature, publicKey);
            return View();
        }

        private static bool Verify(string payment, string signature, string publicKey)
        {
            var pubKeyFixed = publicKey
                    .Replace("-----BEGIN PUBLIC KEY-----", "")
                    .Replace("-----END PUBLIC KEY-----", "");
            var payment64 = Convert.FromBase64String(payment);
            var signature64 = Convert.FromBase64String(signature);
            byte[] rawPublicKey = Convert.FromBase64String(pubKeyFixed);

            AsymmetricKeyParameter aKey = PublicKeyFactory.CreateKey(rawPublicKey);
            RsaKeyParameters rKey = (RsaKeyParameters)aKey;

            PssSigner pss = new PssSigner(new RsaEngine(), new Sha1Digest(), 20);
            pss.Init(false, rKey);
            pss.BlockUpdate(payment64, 0, payment64.Length);

            var result = pss.VerifySignature(signature64);
            return result;
        }

        // Encrypt payment information
        // This method has a dependency to a Nuget package: "Portable.BouncyCastle"
        private static string EncryptPayments(string merchantPublicKey, string orderNumber, double amount)
        {
            try
            {
                Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(merchantPublicKey
                    .Replace("-----BEGIN PUBLIC KEY-----", "")
                    .Replace("-----END PUBLIC KEY-----", "")
                ));

                DerSequence publicKeySequence = (DerSequence)obj;

                DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
                DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());

                DerInteger modulus = (DerInteger)publicKey[0];
                DerInteger exponent = (DerInteger)publicKey[1];

                RsaKeyParameters keyParameters =
                    new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
                RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(parameters);

                var order = orderNumber + "|" + amount;

                byte[] dataToEncrypt = Encoding.UTF8.GetBytes(order);
                byte[] encryptedData = rsa.Encrypt(dataToEncrypt, true);

                var paymentEnc = Convert.ToBase64String(encryptedData);

                return paymentEnc;
            }
            catch (Exception)
            {
                // TODO: handle exception appropriately
                return null;
            }
        }
    }

    public class WebxpayResponse
    {
        public string Payment { get; set; }
        public string Signature { get; set; }
        public string Custom_fields { get; set; }
    }
}