using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace Valg.EVA.Skanning.Signing
{
    public class Certificates : ICertificates
    {
        private static readonly Collection<Func<X509Certificate2Collection, X509Certificate2Collection>> FilterFunctions =
            new Collection<Func<X509Certificate2Collection, X509Certificate2Collection>>();
        private const string certificateProviderFilters = "BuyPass|Buypass AS-983163327";

        public Certificates()
        {
            if (!FilterFunctions.Contains(FilterBuyPassCertificates))
                FilterFunctions.Add(FilterBuyPassCertificates);
        }

        private X509Certificate2Collection FilterBuyPassCertificates(X509Certificate2Collection certificates)
        {
            var filterNames = certificateProviderFilters.ToLower().Split('|').ToList();
            var newCertificates = new X509Certificate2Collection();
            foreach (X509Certificate2 ocert in certificates)
            {
                string[] elem = ocert.Issuer.Split(',');
                IDictionary<string, string> issues = GetCertificateInfo(elem);

                string value;
                if (issues.TryGetValue("O", out value))
                {
                    if (filterNames.Contains(value))
                    {
                        if (ocert.HasPrivateKey)
                        {
                            CspKeyContainerInfo scp = GetPrivateKeyContainerInformation(ocert);
                            if (scp.KeyNumber.ToString().ToLower() == "signature")
                            {
                                newCertificates.Add(ocert);
                            }
                        }
                        else
                        {
                            newCertificates.Add(ocert);
                        }
                    }
                }
            }

            return newCertificates;
        }

        public X509Certificate2 GetCertificateFromStoreTimeValid(StoreName storeName, StoreLocation location, string searchExpression, X509FindType findTypeCert)
        {
            var store = new X509Store(storeName, location);
            try
            {
                store.Open(OpenFlags.ReadOnly);
                X509Certificate2Collection certCollection = store.Certificates;
                X509Certificate2Collection validCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
                X509Certificate2Collection currentCert = validCerts.Find(findTypeCert, searchExpression, false);
                return currentCert.Count == 0 ? null : currentCert[0];
            }
            finally
            {
                store.Close();
            }
        }

        public X509Certificate2 FindSertificate()
        {
            return SelectCertificate(FilterBuyPassCertificates(GetCertificates()));
        }

        private IDictionary<string, string> GetCertificateInfo(IEnumerable<string> elem)
        {
            IDictionary<string, string> issues = new Dictionary<string, string>();
            foreach (string el in elem)
            {
                string[] issue = el.Split('=');
                string key = issue[0].Trim();
                string value = issue[1].Trim().ToLower();

                if (issue.Length == 2 && !issues.ContainsKey(key))
                    issues.Add(key, value);
            }
            return issues;
        }

        private CspKeyContainerInfo GetPrivateKeyContainerInformation(X509Certificate2 certificate)
        {
            if (certificate == null)
            {
                throw new SecurityException("certificate is null");
            }

            var key = certificate.PrivateKey as ICspAsymmetricAlgorithm;
            if (key == null)
            {
                throw new InvalidOperationException("Unknown key type.");
            }

            return key.CspKeyContainerInfo;
        }

        private X509Certificate2Collection GetCertificates()
        {
            var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            certStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
            X509Certificate2Collection certificates = certStore.Certificates;
            certStore.Close();
            return certificates;
        }

        private X509Certificate2 SelectCertificate(X509Certificate2Collection certificates)
        {
            return (certificates.Count == 1) ? certificates[0] : null;
        }
    }
}
