package com.sun.identity.saml2.meta;

import com.airlock.iam.openfedlib.saml2.XjcHelper;
import com.airlock.iam.opensso.jaxb.entityconfig.BaseConfigType;
import com.airlock.iam.opensso.jaxb.entityconfig.EntityConfigElement;
import com.airlock.iam.opensso.jaxb.entityconfig.EntityConfigType;
import com.airlock.iam.opensso.jaxb.entityconfig.IDPSSOConfigElement;
import com.airlock.iam.opensso.jaxb.entityconfig.SPSSOConfigElement;
import com.airlock.iam.opensso.jaxb.metadata.AffiliationDescriptorType;
import com.airlock.iam.opensso.jaxb.metadata.EntityDescriptorElement;
import com.airlock.iam.opensso.jaxb.metadata.EntityDescriptorType;
import com.airlock.iam.opensso.jaxb.metadata.IDPSSODescriptorType;
import com.airlock.iam.opensso.jaxb.metadata.SPSSODescriptorType;
import com.sun.identity.cot.COTException;
import com.sun.identity.cot.CircleOfTrustManager;
import com.sun.identity.plugin.configuration.ConfigurationException;
import com.sun.identity.plugin.configuration.ConfigurationInstance;
import com.sun.identity.plugin.configuration.ConfigurationManager;
import com.sun.identity.saml2.logging.LogUtil;
import jakarta.annotation.Nullable;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;

public class SAML2MetaManager {
   private static final String ATTR_METADATA = "sun-fm-saml2-metadata";
   private static final String ATTR_ENTITY_CONFIG = "sun-fm-saml2-entityconfig";
   private static final String SAML2 = "SAML2";
   private static CircleOfTrustManager cotm;
   private static ConfigurationInstance configInst;
   public static final String NAME_META_ALIAS_IN_URI = "metaAlias";

   public SAML2MetaManager() throws SAML2MetaException {
      if (configInst == null) {
         throw new SAML2MetaException("null_config", null);
      }
   }

   public EntityDescriptorElement getEntityDescriptor(String realm, String entityId) throws SAML2MetaException {
      if (entityId == null) {
         return null;
      }

      if (realm == null) {
         realm = "/";
      }

      String[] objs = new String[]{entityId, realm};
      EntityDescriptorElement descriptor = SAML2MetaCache.getEntityDescriptor(realm, entityId);
      if (descriptor != null) {
         if (SAML2MetaUtils.DEBUG.messageEnabled()) {
            SAML2MetaUtils.DEBUG.message("SAML2MetaManager.getEntityDescriptor: got descriptor from SAML2MetaCache " + entityId);
         }

         LogUtil.access(Level.FINE, "GOT_ENTITY_DESCRIPTOR", objs, null);
         return descriptor;
      } else {
         try {
            Map<String, Set<String>> attrs = configInst.getConfiguration(realm, entityId);
            if (attrs == null) {
               return null;
            }

            Set<String> values = attrs.get("sun-fm-saml2-metadata");
            if (values != null && !values.isEmpty()) {
               String value = values.iterator().next();
               if (SAML2MetaUtils.convertStringToJAXB(value) instanceof EntityDescriptorElement var11) {
                  SAML2MetaCache.putEntityDescriptor(realm, entityId, var11);
                  if (SAML2MetaUtils.DEBUG.messageEnabled()) {
                     SAML2MetaUtils.DEBUG.message("SAML2MetaManager.getEntityDescriptor: got descriptor from SMS " + entityId);
                  }

                  LogUtil.access(Level.FINE, "GOT_ENTITY_DESCRIPTOR", objs, null);
                  return var11;
               } else {
                  SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityDescriptor: invalid descriptor");
                  LogUtil.error(Level.INFO, "GOT_INVALID_ENTITY_DESCRIPTOR", objs, null);
                  throw new SAML2MetaException("invalid_descriptor", objs);
               }
            } else {
               return null;
            }
         } catch (ConfigurationException e) {
            SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityDescriptor", e);
            String[] data = new String[]{e.getMessage(), entityId, realm};
            LogUtil.error(Level.INFO, "CONFIG_ERROR_GET_ENTITY_DESCRIPTOR", data, null);
            throw new SAML2MetaException(e);
         } catch (JAXBException jaxbe) {
            SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityDescriptor", jaxbe);
            LogUtil.error(Level.INFO, "GOT_INVALID_ENTITY_DESCRIPTOR", objs, null);
            throw new SAML2MetaException("invalid_descriptor", objs);
         }
      }
   }

   public SPSSODescriptorType getSPSSODescriptor(String realm, String entityId) throws SAML2MetaException {
      EntityDescriptorElement eDescriptor = this.getEntityDescriptor(realm, entityId);
      return SAML2MetaUtils.getSPSSODescriptor(eDescriptor);
   }

   public IDPSSODescriptorType getIDPSSODescriptor(String realm, String entityId) throws SAML2MetaException {
      EntityDescriptorElement eDescriptor = this.getEntityDescriptor(realm, entityId);
      return SAML2MetaUtils.getIDPSSODescriptor(eDescriptor);
   }

   public AffiliationDescriptorType getAffiliationDescriptor(String realm, String entityId) throws SAML2MetaException {
      EntityDescriptorElement eDescriptor = this.getEntityDescriptor(realm, entityId);
      return eDescriptor == null ? null : ((EntityDescriptorType)eDescriptor.getValue()).getAffiliationDescriptor();
   }

   public EntityConfigElement getEntityConfig(String realm, String entityId) throws SAML2MetaException {
      if (entityId == null) {
         return null;
      }

      if (realm == null) {
         realm = "/";
      }

      String[] objs = new String[]{entityId, realm};
      EntityConfigElement config = SAML2MetaCache.getEntityConfig(realm, entityId);
      if (config != null) {
         if (SAML2MetaUtils.DEBUG.messageEnabled()) {
            SAML2MetaUtils.DEBUG.message("SAML2MetaManager.getEntityConfig: got entity config from SAML2MetaCache: " + entityId);
         }

         LogUtil.access(Level.FINE, "GOT_ENTITY_CONFIG", objs, null);
         return config;
      } else {
         try {
            Map<String, Set<String>> attrs = configInst.getConfiguration(realm, entityId);
            if (attrs == null) {
               return null;
            }

            Set<String> values = attrs.get("sun-fm-saml2-entityconfig");
            if (values != null && !values.isEmpty()) {
               String value = values.iterator().next();
               if (SAML2MetaUtils.convertStringToJAXB(value) instanceof EntityConfigElement var11) {
                  if (SAML2MetaUtils.DEBUG.messageEnabled()) {
                     SAML2MetaUtils.DEBUG.message("SAML2MetaManager.getEntityConfig: got entity config from SMS: " + entityId);
                  }

                  SAML2MetaCache.putEntityConfig(realm, entityId, var11);
                  LogUtil.access(Level.FINE, "GOT_ENTITY_CONFIG", objs, null);
                  return var11;
               } else {
                  SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityConfig: invalid config");
                  LogUtil.error(Level.INFO, "GOT_INVALID_ENTITY_CONFIG", objs, null);
                  throw new SAML2MetaException("invalid_config", objs);
               }
            } else {
               return null;
            }
         } catch (ConfigurationException e) {
            SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityConfig:", e);
            String[] data = new String[]{e.getMessage(), entityId, realm};
            LogUtil.error(Level.INFO, "CONFIG_ERROR_GET_ENTITY_CONFIG", data, null);
            throw new SAML2MetaException(e);
         } catch (JAXBException jaxbe) {
            SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityConfig:", jaxbe);
            LogUtil.error(Level.INFO, "GOT_INVALID_ENTITY_CONFIG", objs, null);
            throw new SAML2MetaException("invalid_config", objs);
         }
      }
   }

   @Nullable
   public BaseConfigType getSPSSOConfig(String realm, String entityId) throws SAML2MetaException {
      EntityConfigElement eConfig = this.getEntityConfig(realm, entityId);
      if (eConfig == null) {
         return null;
      }

      for (JAXBElement<BaseConfigType> obj : ((EntityConfigType)eConfig.getValue()).getIDPSSOConfigOrSPSSOConfigOrAuthnAuthorityConfig()) {
         if (obj instanceof SPSSOConfigElement) {
            return (BaseConfigType)obj.getValue();
         }
      }

      return null;
   }

   public BaseConfigType getIDPSSOConfig(String realm, String entityId) throws SAML2MetaException {
      EntityConfigElement eConfig = this.getEntityConfig(realm, entityId);
      if (eConfig == null) {
         return null;
      }

      for (JAXBElement<BaseConfigType> obj : ((EntityConfigType)eConfig.getValue()).getIDPSSOConfigOrSPSSOConfigOrAuthnAuthorityConfig()) {
         if (obj instanceof IDPSSOConfigElement) {
            return (BaseConfigType)obj.getValue();
         }
      }

      return null;
   }

   private List<String> getAllHostedEntities(String realm) throws SAML2MetaException {
      List<String> hostedEntityIds = new ArrayList<>();

      try {
         Set<String> entityIds = configInst.getAllConfigurationNames(realm);
         if (entityIds != null && !entityIds.isEmpty()) {
            for (String entityId : entityIds) {
               EntityConfigElement config = this.getEntityConfig(realm, entityId);
               if (config != null && XjcHelper.mapBooleanWrapperToPrimitiveWithFalseAsDefault(((EntityConfigType)config.getValue()).isHosted())) {
                  hostedEntityIds.add(entityId);
               }
            }
         }
      } catch (ConfigurationException e) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getAllHostedEntities:", e);
         String[] data = new String[]{e.getMessage(), realm};
         LogUtil.error(Level.INFO, "CONFIG_ERROR_GET_ALL_HOSTED_ENTITIES", data, null);
         throw new SAML2MetaException(e);
      }

      String[] objs = new String[]{realm};
      LogUtil.access(Level.FINE, "GOT_ALL_HOSTED_ENTITIES", objs, null);
      return hostedEntityIds;
   }

   public List<String> getAllHostedServiceProviderEntities(String realm) throws SAML2MetaException {
      List<String> hostedSPEntityIds = new ArrayList<>();

      for (String entityId : this.getAllHostedEntities(realm)) {
         if (this.getSPSSODescriptor(realm, entityId) != null) {
            hostedSPEntityIds.add(entityId);
         }
      }

      return hostedSPEntityIds;
   }

   public List<String> getAllHostedIdentityProviderEntities(String realm) throws SAML2MetaException {
      List<String> hostedIDPEntityIds = new ArrayList<>();

      for (String entityId : this.getAllHostedEntities(realm)) {
         if (this.getIDPSSODescriptor(realm, entityId) != null) {
            hostedIDPEntityIds.add(entityId);
         }
      }

      return hostedIDPEntityIds;
   }

   private List<String> getAllRemoteEntities(String realm) throws SAML2MetaException {
      List<String> remoteEntityIds = new ArrayList<>();
      String[] objs = new String[]{realm};

      try {
         Set<String> entityIds = configInst.getAllConfigurationNames(realm);
         if (entityIds != null && !entityIds.isEmpty()) {
            for (String entityId : entityIds) {
               EntityConfigElement config = this.getEntityConfig(realm, entityId);
               if (config == null || !XjcHelper.mapBooleanWrapperToPrimitiveWithFalseAsDefault(((EntityConfigType)config.getValue()).isHosted())) {
                  remoteEntityIds.add(entityId);
               }
            }
         }
      } catch (ConfigurationException e) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getAllRemoteEntities:", e);
         String[] data = new String[]{e.getMessage(), realm};
         LogUtil.error(Level.INFO, "CONFIG_ERROR_GET_ALL_REMOTE_ENTITIES", data, null);
         throw new SAML2MetaException(e);
      }

      LogUtil.access(Level.FINE, "GOT_ALL_REMOTE_ENTITIES", objs, null);
      return remoteEntityIds;
   }

   public List<String> getAllRemoteIdentityProviderEntities(String realm) throws SAML2MetaException {
      List<String> remoteIDPEntityIds = new ArrayList<>();

      for (String entityId : this.getAllRemoteEntities(realm)) {
         if (this.getIDPSSODescriptor(realm, entityId) != null) {
            remoteIDPEntityIds.add(entityId);
         }
      }

      return remoteIDPEntityIds;
   }

   public String getEntityByMetaAlias(String metaAlias) throws SAML2MetaException {
      String realm = SAML2MetaUtils.getRealmByMetaAlias(metaAlias);

      try {
         Set<String> entityIds = configInst.getAllConfigurationNames(realm);
         if (entityIds != null && !entityIds.isEmpty()) {
            for (String entityId : entityIds) {
               EntityConfigElement config = this.getEntityConfig(realm, entityId);
               if (config != null) {
                  for (JAXBElement<BaseConfigType> o : ((EntityConfigType)config.getValue()).getIDPSSOConfigOrSPSSOConfigOrAuthnAuthorityConfig()) {
                     BaseConfigType bConfig = (BaseConfigType)o.getValue();
                     String cMetaAlias = bConfig.getMetaAlias();
                     if (cMetaAlias != null && cMetaAlias.equals(metaAlias)) {
                        return entityId;
                     }
                  }
               }
            }

            return null;
         } else {
            return null;
         }
      } catch (ConfigurationException e) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getEntityByMetaAlias:", e);
         throw new SAML2MetaException(e);
      }
   }

   public List<String> getAllHostedServiceProviderMetaAliases(String realm) throws SAML2MetaException {
      List<String> metaAliases = new ArrayList<>();

      for (String entityId : this.getAllHostedServiceProviderEntities(realm)) {
         BaseConfigType spConfig;
         if ((spConfig = this.getSPSSOConfig(realm, entityId)) != null) {
            metaAliases.add(spConfig.getMetaAlias());
         }
      }

      return metaAliases;
   }

   public boolean isTrustedProvider(String realm, String entityId, String trustedEntityId) throws SAML2MetaException {
      boolean result = false;
      BaseConfigType spconfig = this.getSPSSOConfig(realm, entityId);
      if (spconfig != null) {
         result = this.isSameCircleOfTrust(spconfig, entityId, realm, trustedEntityId);
      }

      if (result) {
         return true;
      }

      BaseConfigType idpconfig = this.getIDPSSOConfig(realm, entityId);
      if (idpconfig != null) {
         return this.isSameCircleOfTrust(idpconfig, entityId, realm, trustedEntityId);
      }

      SAML2MetaUtils.DEBUG.warning("No configured SP or IdP found for entityId " + entityId);
      return false;
   }

   private boolean isSameCircleOfTrust(BaseConfigType config, String entityId, String realm, String trustedEntityId) {
      try {
         if (config != null) {
            Map<String, List<String>> attr = SAML2MetaUtils.getAttributes(config);
            List<String> cotList = attr.get("cotlist");
            if (cotList != null && !cotList.isEmpty()) {
               for (String a : cotList) {
                  if (cotm.isInCircleOfTrust(realm, a, "saml2", trustedEntityId)) {
                     return true;
                  }
               }

               SAML2MetaUtils.DEBUG.warning("Entity \"" + entityId + "\" is not in the same circle of trust as entity \"" + trustedEntityId + "\"");
            } else {
               SAML2MetaUtils.DEBUG.warning("No \"cotlist\" attribute configured for entity \"" + entityId + "\"");
            }
         }

         return false;
      } catch (Exception e) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager.isSameCircleOfTrust: Error while determining two entities are in the same COT: " + e.getMessage());
         return false;
      }
   }

   public Set<String> getAllEntities(String realm) throws SAML2MetaException {
      Set<String> ret = new HashSet<>();
      String[] objs = new String[]{realm};

      try {
         Set<String> entityIds = configInst.getAllConfigurationNames(realm);
         if (entityIds != null && !entityIds.isEmpty()) {
            ret.addAll(entityIds);
         }
      } catch (ConfigurationException e) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager.getAllEntities:", e);
         String[] data = new String[]{e.getMessage(), realm};
         LogUtil.error(Level.INFO, "CONFIG_ERROR_GET_ALL_ENTITIES", data, null);
         throw new SAML2MetaException(e);
      }

      LogUtil.access(Level.FINE, "GOT_ALL_ENTITIES", objs, null);
      return ret;
   }

   static {
      try {
         configInst = ConfigurationManager.getConfigurationInstance("SAML2");
      } catch (ConfigurationException ce) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager constructor:", ce);
      }

      if (configInst != null) {
         try {
            configInst.addListener(new SAML2MetaServiceListener());
         } catch (ConfigurationException ce) {
            SAML2MetaUtils.DEBUG.error("SAML2MetaManager.static: Unable to add ConfigurationListener for SAML2COT service.", ce);
         }
      }

      try {
         cotm = new CircleOfTrustManager();
      } catch (COTException se) {
         SAML2MetaUtils.DEBUG.error("SAML2MetaManager constructor:", se);
      }
   }
}
