using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine.Purchasing;
namespace UnityEngine.Purchasing.Security
{
    /// 
    /// Security exception for when the store is not supported by the CrossPlatformValidator.
    /// 
    public class StoreNotSupportedException : IAPSecurityException
    {
        /// 
        /// Constructs an instance with a message.
        /// 
        ///  The message that describes the error. 
        public StoreNotSupportedException(string message) : base(message)
        {
        }
    }
    /// 
    /// Security exception for an invalid App bundle ID.
    /// 
    public class InvalidBundleIdException : IAPSecurityException { }
    /// 
    /// Security exception for invalid receipt Data.
    /// 
    public class InvalidReceiptDataException : IAPSecurityException { }
    /// 
    /// Security exception for a missing store secret.
    /// 
    public class MissingStoreSecretException : IAPSecurityException
    {
        /// 
        /// Constructs an instance with a message.
        /// 
        ///  The message that describes the error. 
        public MissingStoreSecretException(string message) : base(message)
        {
        }
    }
    /// 
    /// Security exception for an invalid public key.
    /// 
    public class InvalidPublicKeyException : IAPSecurityException
    {
        /// 
        /// Constructs an instance with a message.
        /// 
        ///  The message that describes the error. 
        public InvalidPublicKeyException(string message) : base(message)
        {
        }
    }
    /// 
    /// A generic exception for CrossPlatformValidator issues.
    /// 
    public class GenericValidationException : IAPSecurityException
    {
        /// 
        /// Constructs an instance with a message.
        /// 
        ///  The message that describes the error. 
        public GenericValidationException(string message) : base(message)
        {
        }
    }
    /// 
    /// Class that validates receipts on multiple platforms that support the Security module.
    /// Note that this currently only supports GooglePlay and Apple platforms.
    /// 
    public class CrossPlatformValidator
    {
        private GooglePlayValidator google;
        private AppleValidator apple;
        private string googleBundleId, appleBundleId;
        /// 
        /// Constructs an instance and checks the validity of the certification keys
        /// which only takes input parameters for the supported platforms and uses a common bundle ID for Apple and GooglePlay.
        /// 
        ///  The GooglePlay public key. 
        ///  The Apple certification key. 
        ///  The bundle ID for all platforms. 
        public CrossPlatformValidator(byte[] googlePublicKey, byte[] appleRootCert,
            string appBundleId) : this(googlePublicKey, appleRootCert, null, appBundleId, appBundleId, null)
        {
        }
        /// 
        /// Constructs an instance and checks the validity of the certification keys
        /// which uses a common bundle ID for Apple and GooglePlay.
        /// 
        ///  The GooglePlay public key. 
        ///  The Apple certification key. 
        ///  The Unity Channel public key. Not used because Unity Channel is no longer supported. 
        ///  The bundle ID for all platforms. 
        public CrossPlatformValidator(byte[] googlePublicKey, byte[] appleRootCert, byte[] unityChannelPublicKey_not_used,
            string appBundleId)
            : this(googlePublicKey, appleRootCert, null, appBundleId, appBundleId, appBundleId)
        {
        }
        /// 
        /// Constructs an instance and checks the validity of the certification keys
        /// which only takes input parameters for the supported platforms.
        /// 
        ///  The GooglePlay public key. 
        ///  The Apple certification key. 
        ///  The GooglePlay bundle ID. 
        ///  The Apple bundle ID. 
        public CrossPlatformValidator(byte[] googlePublicKey, byte[] appleRootCert,
            string googleBundleId, string appleBundleId)
            : this(googlePublicKey, appleRootCert, null, googleBundleId, appleBundleId, null)
        {
        }
        /// 
        /// Constructs an instance and checks the validity of the certification keys.
        /// 
        ///  The GooglePlay public key. 
        ///  The Apple certification key. 
        ///  The Unity Channel public key. Not used because Unity Channel is no longer supported. 
        ///  The GooglePlay bundle ID. 
        ///  The Apple bundle ID. 
        ///  The Xiaomi bundle ID. Not used because Xiaomi is no longer supported. 
        public CrossPlatformValidator(byte[] googlePublicKey, byte[] appleRootCert, byte[] unityChannelPublicKey_not_used,
            string googleBundleId, string appleBundleId, string xiaomiBundleId_not_used)
        {
            try
            {
                if (null != googlePublicKey)
                {
                    google = new GooglePlayValidator(googlePublicKey);
                }
                if (null != appleRootCert)
                {
                    apple = new AppleValidator(appleRootCert);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidPublicKeyException("Cannot instantiate self with an invalid public key. (" +
                    ex.ToString() + ")");
            }
            this.googleBundleId = googleBundleId;
            this.appleBundleId = appleBundleId;
        }
        /// 
        /// Validates a receipt.
        /// 
        ///  The receipt to be validated. 
        ///  An array of receipts parsed from the validation process 
        public IPurchaseReceipt[] Validate(string unityIAPReceipt)
        {
            try
            {
                var wrapper = (Dictionary)MiniJson.JsonDecode(unityIAPReceipt);
                if (null == wrapper)
                {
                    throw new InvalidReceiptDataException();
                }
                var store = (string)wrapper["Store"];
                var payload = (string)wrapper["Payload"];
                switch (store)
                {
                    case "GooglePlay":
                    {
                        if (null == google)
                        {
                            throw new MissingStoreSecretException(
                                "Cannot validate a Google Play receipt without a Google Play public key.");
                        }
                        var details = (Dictionary)MiniJson.JsonDecode(payload);
                        var json = (string)details["json"];
                        var sig = (string)details["signature"];
                        var result = google.Validate(json, sig);
                        // [IAP-1696] Check googleBundleId if packageName is present inside the signed receipt.
                        // packageName can be missing when the GPB v1 getPurchaseHistory API is used to fetch.
                        if (!string.IsNullOrEmpty(result.packageName) &&
                            !googleBundleId.Equals(result.packageName))
                        {
                            throw new InvalidBundleIdException();
                        }
                        return new IPurchaseReceipt[] { result };
                    }
                    case "AppleAppStore":
                    case "MacAppStore":
                    {
                        if (null == apple)
                        {
                            throw new MissingStoreSecretException(
                                "Cannot validate an Apple receipt without supplying an Apple root certificate");
                        }
                        var r = apple.Validate(Convert.FromBase64String(payload));
                        if (!appleBundleId.Equals(r.bundleID))
                        {
                            throw new InvalidBundleIdException();
                        }
                        return r.inAppPurchaseReceipts.ToArray();
                    }
                    default:
                    {
                        throw new StoreNotSupportedException("Store not supported: " + store);
                    }
                }
            }
            catch (IAPSecurityException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw new GenericValidationException("Cannot validate due to unhandled exception. (" + ex + ")");
            }
        }
    }
}