package com.airlock.iam.saml2.domain.model.sp.step;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.common.api.domain.model.user.ProvidedUsername;
import com.airlock.iam.flow.shared.api.domain.model.session.UserSessionScopedFlowAttribute;
import com.airlock.iam.saml2.domain.model.Saml2RelayState;
import com.airlock.iam.saml2.domain.model.sp.Saml2Attributes;
import com.airlock.iam.saml2.domain.model.sp.Saml2InResponseTo;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpAssertionAttributes;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpEntityId;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpSsoAssertionData;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpSsoContinuationId;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpSsoMetadata;

public record Saml2SpSsoSessionAttribute(Saml2SpSsoContinuationId continuationId, Saml2SpSsoAssertionData assertionData)
   implements UserSessionScopedFlowAttribute<Saml2SpSsoSessionAttribute> {
   private static final String CONTINUATION_ID_KEY = "continuationId";
   private static final String IN_RESPONSE_TO_KEY = "inResponseTo";
   private static final String RELAY_STATE_KEY = "relayState";
   private static final String META_DATA_ENTITY_ID_KEY = "metaDataEntityId";
   private static final String ATTRIBUTES_ENTITY_ID_KEY = "attributesEntityId";
   private static final String PROVIDED_USER_ID_KEY = "providedUserId";
   private static final String ATTRIBUTES_KEY = "attributesId";

   public Saml2SpSsoSessionAttribute mergeWith(Saml2SpSsoSessionAttribute newAttribute) {
      throw new FailureException("Cannot change SSO session.");
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("continuationId", this.continuationId.value())
         .putIfPresent("inResponseTo", this.assertionData.ssoMetadata().inResponseTo().map(Saml2InResponseTo::value))
         .putIfPresent("relayState", this.assertionData.ssoMetadata().relayState().map(Saml2RelayState::value))
         .put("metaDataEntityId", this.assertionData.ssoMetadata().spEntityId().value())
         .put("attributesEntityId", this.assertionData.assertionAttributes().spEntityId().value())
         .put("providedUserId", this.assertionData.assertionAttributes().providedUserIdFromNameID().getValue())
         .put("attributesId", this.assertionData.assertionAttributes().attributes().getMap())
         .build();
   }

   public Saml2SpSsoSessionAttribute copyAttribute() {
      return new Saml2SpSsoSessionAttribute(this.continuationId, this.assertionData);
   }

   public static Saml2SpSsoSessionAttribute demapify(MapifiedValues values) {
      return new Saml2SpSsoSessionAttribute(
         values.simpleValue("continuationId", Saml2SpSsoContinuationId::parseSsoContinuationId),
         new Saml2SpSsoAssertionData(
            new Saml2SpSsoMetadata(
               values.<String>getOptional("inResponseTo").map(Saml2InResponseTo::new),
               values.<String>getOptional("relayState").map(Saml2RelayState::relayState),
               values.simpleValue("metaDataEntityId", Saml2SpEntityId::spEntityId)
            ),
            new Saml2SpAssertionAttributes(
               values.simpleValue("attributesEntityId", Saml2SpEntityId::spEntityId),
               values.simpleValue("providedUserId", ProvidedUsername::providedUsernameOf),
               values.simpleValue("attributesId", Saml2Attributes::new)
            )
         )
      );
   }
}
