package com.airlock.iam.oauth2.domain.model.oidc;

import com.airlock.iam.base.api.domain.model.mapification.Mapifiable;
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.collection.Pair;
import com.airlock.iam.flow.shared.api.domain.model.session.UserSessionScopedFlowAttribute;
import com.airlock.iam.oauth2.api.domain.model.AuthorizationServerId;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.domain.model.session.OAuth2SessionId;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

public record OidcUserAgentSessions(OidcUserAgentSessionId userAgentSessionId, List<OidcUserAgentSessions.OidcUserAgentSession> sessions)
   implements UserSessionScopedFlowAttribute<OidcUserAgentSessions> {
   private static final String USER_AGENT_ID_KEY = "userAgentSessionId";
   private static final String SESSIONS_KEY = "sessions";

   public OidcUserAgentSessions() {
      this(OidcUserAgentSessionId.generateRandomOidcUserAgentId(), Collections.emptyList());
   }

   public OidcUserAgentSessions withAddedSession(OidcUserAgentSessions.OidcUserAgentSession session) {
      return new OidcUserAgentSessions(this.userAgentSessionId, Stream.concat(this.sessions.stream(), Stream.of(session)).toList());
   }

   public List<Pair<AuthorizationServerId, OAuth2SessionId>> authorizationServerSessions() {
      return this.sessions.stream().map(session -> Pair.pair(session.asId(), session.sessionId())).toList();
   }

   public OidcUserAgentSessions copyAttribute() {
      return new OidcUserAgentSessions(this.userAgentSessionId, List.copyOf(this.sessions));
   }

   public OidcUserAgentSessions mergeWith(OidcUserAgentSessions newAttribute) {
      return newAttribute;
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("userAgentSessionId", this.userAgentSessionId.id())
         .putIfNotEmpty("sessions", this.sessions.stream().map(OidcUserAgentSessions.OidcUserAgentSession::mapify).toList())
         .build();
   }

   public static OidcUserAgentSessions demapify(MapifiedValues values) {
      return new OidcUserAgentSessions(
         new OidcUserAgentSessionId(values.get("userAgentSessionId")), values.demapifyList("sessions", OidcUserAgentSessions.OidcUserAgentSession::demapify)
      );
   }

   public record OidcUserAgentSession(AuthorizationServerId asId, OAuth2ClientId clientId, OAuth2SessionId sessionId, String sub) implements Mapifiable {
      private static final String AS_ID_KEY = "asId";
      private static final String CLIENT_ID_KEY = "clientId";
      private static final String SESSION_ID_KEY = "sessionId";
      private static final String SUB_KEY = "sub";

      public static OidcUserAgentSessions.OidcUserAgentSession demapify(MapifiedValues values) {
         return new OidcUserAgentSessions.OidcUserAgentSession(
            AuthorizationServerId.authorizationServerId(values.get("asId")),
            OAuth2ClientId.oAuth2ClientId(values.get("clientId")),
            OAuth2SessionId.oAuth2SessionId(values.get("sessionId")),
            values.get("sub")
         );
      }

      @Override
      public MapifiedValues mapify() {
         return MapifiedValuesBuilder.mapifiedValues()
            .put("asId", this.asId.id())
            .put("clientId", this.clientId.id())
            .put("sessionId", this.sessionId.getId())
            .put("sub", this.sub)
            .build();
      }
   }
}
