using System;
using System.Configuration;
using System.IdentityModel.Configuration;
using System.IdentityModel.Services;
using System.IdentityModel.Services.Configuration;
using System.IdentityModel.Services.Tokens;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel.Security;


namespace ExampleRelyingParty_UsingClientless
{
    public static class IdentityConfig
    {
        #region Public Properties
        public static string AudienceUri { get; set; }
        public static string Realm { get; set; }
        public static string Authority { get; set; }
        public static string Issuer { get; set; }
        public static string EncryptionThumb { get; set; }
        public static string SigningThumb { get; set; }
        public static string FederationMetadataLocation { get; set; }
        #endregion

        public enum StsConfiguration
        {
            system,
            unit,
            prod,
            devToUnit
        }


        #region Public Methods

        public static void ConfigureIdentity(StsConfiguration stsConfiguration)
        {
            ConfigureIdentity(stsConfiguration.ToString());
        }
        public static void ConfigureIdentity(string configurationKeyPrefix)
        {
            if (!configurationKeyPrefix.EndsWith(":"))
                configurationKeyPrefix = string.Concat(configurationKeyPrefix, ":");

            Realm = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "Realm"));
            AudienceUri = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "AudienceUri"));
            FederationMetadataLocation = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "FederationMetadataLocation"));
            Authority = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "Authority"));
            Issuer = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "Issuer"));
            SigningThumb = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "SigningThumb"));
            EncryptionThumb = ConfigurationManager.AppSettings.Get(string.Concat(configurationKeyPrefix, "EncryptionThumb"));
            if (configurationKeyPrefix != "ida:")
            {
                ConfigurationManager.AppSettings.Set("ida:Realm", Realm);
                ConfigurationManager.AppSettings.Set("ida:AudienceUri", AudienceUri);
                ConfigurationManager.AppSettings.Set("ida:Authority", Authority);
                ConfigurationManager.AppSettings.Set("ida:Issuer", Issuer);
                ConfigurationManager.AppSettings.Set("ida:FederationMetadataLocation", FederationMetadataLocation);
                ConfigurationManager.AppSettings.Set("ida:SigningThumb", SigningThumb);
                ConfigurationManager.AppSettings.Set("ida:EncryptionThumb", EncryptionThumb);
            }

            // AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
            FederatedAuthentication.FederationConfigurationCreated +=
                  FederatedAuthentication_FederationConfigurationCreated;
        }
        #endregion

        #region Event Handlers
        private static void FederatedAuthentication_FederationConfigurationCreated(object sender, FederationConfigurationCreatedEventArgs e)
        {
            FederatedAuthentication.FederationConfigurationCreated -= FederatedAuthentication_FederationConfigurationCreated;
            var federationConfiguration = new FederationConfiguration();

            e.FederationConfiguration = Create(federationConfiguration);

        }
        #endregion

        #region Helpers
        public static FederationConfiguration Create(FederationConfiguration federationConfiguration)
        {
            var encryptionCertificate = FindCertificate(EncryptionThumb);

            if (encryptionCertificate == null && Realm != ConfigurationManager.AppSettings["devToUnit:Realm"])
            {
                throw new ArgumentOutOfRangeException("EncryptionThumb", "Encryption Certificate is required");
            }



            var issuingAuthority = BuildIssuingAuthority();
            var issuerNameRegistry = BuildIssuerNameRegistry(issuingAuthority);
            var identityConfiguration = BuildIdentityConfiguration(issuerNameRegistry);
            var cookieHandler = BuildCookieHandler();

            federationConfiguration.CookieHandler = cookieHandler;
            federationConfiguration.IdentityConfiguration = identityConfiguration;
            var authModesQs = string.Format("authModes={0}", ConfigurationManager.AppSettings.Get("authModes"));
            AddWsFederationConfiguration(federationConfiguration, authModesQs);
            ConfigureCertificates(federationConfiguration, encryptionCertificate);
            ConfigureSSL(federationConfiguration);
            federationConfiguration.IdentityConfiguration.SecurityTokenHandlers.AddOrReplace(
                    new MachineKeySessionSecurityTokenHandler());

            return federationConfiguration;
        }

        private static IssuingAuthority BuildIssuingAuthority()
        {
            IssuingAuthority issuingAuthority = new IssuingAuthority(Authority);
            issuingAuthority.Issuers.Add(Authority);
            issuingAuthority.Issuers.Add(Issuer);
            return issuingAuthority;
        }

        private static ValidatingIssuerNameRegistry BuildIssuerNameRegistry(IssuingAuthority issuingAuthority)
        {
            return new ValidatingIssuerNameRegistry(issuingAuthority);
        }

        private static void AddWsFederationConfiguration(FederationConfiguration federationConfiguration, string authModes)
        {
            var wsFederationConfiguration = new WsFederationConfiguration(Issuer, Realm) { SignInQueryString = authModes };
            federationConfiguration.WsFederationConfiguration = wsFederationConfiguration;
            federationConfiguration.WsFederationConfiguration.PassiveRedirectEnabled = true;
        }

        private static IdentityConfiguration BuildIdentityConfiguration(ValidatingIssuerNameRegistry issuerNameRegistry)
        {
            IdentityConfiguration identityConfiguration = new IdentityConfiguration();
            identityConfiguration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(Realm));
            identityConfiguration.IssuerNameRegistry = issuerNameRegistry;
            identityConfiguration.DetectReplayedTokens = true;
            return identityConfiguration;
        }

        private static CookieHandler BuildCookieHandler()
        {
            return new ChunkedCookieHandler();
        }

        private static void ConfigureCertificates(FederationConfiguration federationConfiguration, X509Certificate2 encryptionCertificate)
        {
            if (Realm != ConfigurationManager.AppSettings["devToUnit:Realm"])
                federationConfiguration.ServiceCertificate = encryptionCertificate;

            var validatingRegistry = federationConfiguration.IdentityConfiguration.IssuerNameRegistry as ValidatingIssuerNameRegistry;

            if (validatingRegistry != null &&
                Realm != ConfigurationManager.AppSettings["localSts:Realm"] &&
                Realm != ConfigurationManager.AppSettings["devToUnit:Realm"])
            {
                //not local or test
                validatingRegistry.IssuingAuthorities.First().Thumbprints.Add(SigningThumb);
                federationConfiguration.IdentityConfiguration.CertificateValidationMode = X509CertificateValidationMode.ChainTrust;
                federationConfiguration.IdentityConfiguration.ServiceCertificate = encryptionCertificate;
            }
            else if (validatingRegistry != null &&
                Realm == ConfigurationManager.AppSettings["devToUnit:Realm"])
            {
                //test
                validatingRegistry.IssuingAuthorities.First().Thumbprints.Add(SigningThumb);
                federationConfiguration.IdentityConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;
            }
            else if (validatingRegistry != null && Realm == ConfigurationManager.AppSettings["localSts:Realm"])
            {
                //local
                validatingRegistry.IssuingAuthorities.First().Thumbprints.Add(SigningThumb);
                federationConfiguration.IdentityConfiguration.CertificateValidationMode = X509CertificateValidationMode.None;
                federationConfiguration.IdentityConfiguration.RevocationMode = X509RevocationMode.NoCheck;
                federationConfiguration.IdentityConfiguration.ServiceCertificate = encryptionCertificate;
            }
        }

        private static void ConfigureSSL(FederationConfiguration federationConfiguration)
        {
            federationConfiguration.CookieHandler.RequireSsl = true;
            federationConfiguration.WsFederationConfiguration.RequireHttps = true;
        }

        private static X509Certificate2 FindCertificate(string thumb)
        {
            X509Certificate2 cert = null;
            if (!string.IsNullOrEmpty(thumb))
            {
                var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
                store.Open(OpenFlags.ReadOnly);
                try
                {
                    var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, thumb, true);
                    if (certificate.Count > 0)
                    {
                        cert = certificate[0];
                    }
                }
                finally
                {
                    store.Close();
                }

            }
            return cert;
        }
        #endregion
    }
}