package com.airlock.iam.saml2.infrastructure.service;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.opensso.jaxb.entityconfig.BaseConfigType;
import com.airlock.iam.opensso.jaxb.metadata.IDPSSODescriptorType;
import com.airlock.iam.saml2.domain.model.Saml2MetaManager;
import com.airlock.iam.saml2.domain.model.idp.Saml2IdpEntityId;
import com.airlock.iam.saml2.domain.model.idp.Saml2IdpMetaAlias;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpEntityId;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpMetaAlias;
import com.sun.identity.saml2.meta.SAML2MetaException;
import com.sun.identity.saml2.meta.SAML2MetaManager;
import jakarta.inject.Inject;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;

public final class Saml2MetaManagerImpl implements Saml2MetaManager {
   private final SAML2MetaManager metaManager;

   @Inject
   Saml2MetaManagerImpl() {
      try {
         this.metaManager = new SAML2MetaManager();
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }

   @Override
   public Saml2IdpMetaAlias idpMetaAlias(Saml2IdpEntityId idpEntityId) {
      try {
         return Optional.ofNullable(new SAML2MetaManager().getIDPSSOConfig("/", idpEntityId.value()))
            .map(BaseConfigType::getMetaAlias)
            .filter(StringUtils::isNotBlank)
            .map(Saml2IdpMetaAlias::idpMetaAlias)
            .orElseThrow(() -> new FailureException("IDP MetaAlias must not be blank for IDP " + idpEntityId));
      } catch (SAML2MetaException e) {
         throw new FailureException("Error accessing SAML2 metadata for IDP " + idpEntityId, e);
      }
   }

   @Override
   public List<Saml2SpEntityId> getAllHostedServiceProviderEntities(String realm) {
      try {
         return this.metaManager.getAllHostedServiceProviderEntities(realm).stream().map(Saml2SpEntityId::spEntityId).collect(Collectors.toList());
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }

   @Override
   public List<Saml2SpMetaAlias> getAllHostedServiceProviderMetaAliases(String realm) {
      try {
         return this.metaManager.getAllHostedServiceProviderMetaAliases(realm).stream().map(Saml2SpMetaAlias::spMetaAlias).collect(Collectors.toList());
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }

   @Override
   public Optional<Saml2SpEntityId> getEntityByMetaAlias(Saml2SpMetaAlias spMetaAlias) {
      try {
         return Optional.ofNullable(this.metaManager.getEntityByMetaAlias(spMetaAlias.value())).map(Saml2SpEntityId::spEntityId);
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }

   @Override
   public Optional<IDPSSODescriptorType> getIDPSSODescriptor(String realm, Saml2IdpEntityId idpEntityId) {
      try {
         return Optional.ofNullable(this.metaManager.getIDPSSODescriptor(realm, idpEntityId.value()));
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }

   @Override
   public boolean isTrustedProvider(String realm, Saml2SpEntityId spEntityId, Saml2IdpEntityId idpEntityId) {
      try {
         return this.metaManager.isTrustedProvider(realm, spEntityId.value(), idpEntityId.value());
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }

   @Override
   public Saml2SpMetaAlias spMetaAlias(Saml2SpEntityId hostEntityId) {
      try {
         BaseConfigType spConfig = this.metaManager.getSPSSOConfig("/", hostEntityId.value());
         if (spConfig != null) {
            return Saml2SpMetaAlias.spMetaAlias(spConfig.getMetaAlias());
         } else {
            throw new FailureException("SP's entity config not found for entity ID %s.".formatted(hostEntityId));
         }
      } catch (SAML2MetaException e) {
         throw new FailureException(e);
      }
   }
}
