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

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.base.misc.json.JsonCoding;
import com.airlock.iam.saml2.domain.model.session.FlowFederationSession;
import com.airlock.iam.saml2.infrastructure.sp.logout.Saml2IdpInitiatedSloCache;
import com.sun.identity.saml2.common.SAML2Exception;
import com.sun.identity.saml2.profile.LogoutRequestDataContainer;
import com.sun.identity.saml2.protocol.LogoutRequest;
import com.sun.identity.saml2.protocol.impl.LogoutRequestImpl;
import java.util.Optional;

public final class Saml2IdpInitiatedSloCacheEntrySerializer implements Serializer<Saml2IdpInitiatedSloCache.Saml2IdpInitiatedSloCacheEntry> {
   private static final String SESSION_KEY = "session";
   private static final String DATA_KEY = "data";
   private static final String LOGOUT_REQUEST_KEY = "logoutReq";
   private static final String SP_ENTITY_ID_KEY = "spEntityId";
   private static final String REALM_KEY = "realm";
   private static final String LOCATION_KEY = "location";
   private static final String IDP_ENTITY_ID_KEY = "idpEntityId";
   private static final String BINDING_KEY = "binding";
   private static final String RELAY_STATE_KEY = "relayState";

   public String mapToStore(Saml2IdpInitiatedSloCache.Saml2IdpInitiatedSloCacheEntry value) {
      return JsonCoding.toJson(
         MapifiedValuesBuilder.mapifiedValues().put("session", value.federationSession().mapify()).put("data", mapifyData(value.logoutData())).build().asMap()
      );
   }

   public Saml2IdpInitiatedSloCache.Saml2IdpInitiatedSloCacheEntry mapFromStore(String serialized) {
      MapifiedValues values = new MapifiedValues(JsonCoding.fromJson(serialized));
      return new Saml2IdpInitiatedSloCache.Saml2IdpInitiatedSloCacheEntry(
         values.demapifyValue("session", FlowFederationSession::demapify), values.demapifyValue("data", Saml2IdpInitiatedSloCacheEntrySerializer::demapifyData)
      );
   }

   private static MapifiedValues mapifyData(LogoutRequestDataContainer data) {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("logoutReq", serializeLogoutRequest(data.getLogoutReq()))
         .put("spEntityId", data.getSpEntityID())
         .put("realm", data.getRealm())
         .put("location", data.getLocation())
         .put("idpEntityId", data.getIdpEntityID())
         .put("binding", data.getBinding())
         .putIfPresent("relayState", Optional.ofNullable(data.getRelayState()))
         .build();
   }

   private static String serializeLogoutRequest(LogoutRequest logoutRequest) {
      try {
         return logoutRequest.toXMLString(true, true);
      } catch (SAML2Exception e) {
         throw new FailureException(e);
      }
   }

   private static LogoutRequestDataContainer demapifyData(MapifiedValues values) {
      return new LogoutRequestDataContainer(
         values.simpleValue("logoutReq", Saml2IdpInitiatedSloCacheEntrySerializer::deserializeLogoutRequest),
         values.get("spEntityId"),
         values.get("realm"),
         values.get("location"),
         values.get("idpEntityId"),
         values.get("binding"),
         values.<String>getOptional("relayState").orElse(null)
      );
   }

   private static LogoutRequest deserializeLogoutRequest(String xmlString) {
      try {
         return new LogoutRequestImpl(xmlString);
      } catch (SAML2Exception e) {
         throw new FailureException(e);
      }
   }
}
