package com.airlock.iam.apipolicyservice.infrastructure.repository;

import com.airlock.iam.apipolicyservice.domain.model.ApiPolicyServiceApiKey;
import com.airlock.iam.apipolicyservice.domain.model.ApiPolicyServicePlanUsage;
import com.airlock.iam.apipolicyservice.domain.model.ApiPolicyServiceSubscription;
import com.airlock.iam.apipolicyservice.domain.model.ApiPolicyServiceTechClientView;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.infrastructure.db.DbTimestamps;
import com.airlock.iam.core.domain.model.jwt.JweToken;
import com.airlock.iam.core.domain.model.jwt.Plaintext;
import com.airlock.iam.core.domain.service.jwt.JwtHmacAndAesGcmEncryption;
import com.airlock.iam.core.misc.util.ticket.TicketEncodingException;
import com.airlock.iam.database.infrastructure.generated.schema.tables.ApiKeyTable;
import com.airlock.iam.database.infrastructure.generated.schema.tables.PlanUsageTable;
import com.airlock.iam.database.infrastructure.generated.schema.tables.PrincipalTable;
import com.airlock.iam.database.infrastructure.generated.schema.tables.SubscriptionTable;
import com.airlock.iam.techclient.domain.model.apikey.ApiKeySecret;
import com.airlock.iam.techclient.domain.model.apikey.ApiKeySecretFingerprint;
import com.airlock.iam.techclient.domain.model.apikey.ApiKeySecretValue;
import com.airlock.iam.techclient.domain.model.identity.TechClientIdentityFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.LocalDateTime;
import java.util.HashSet;
import org.apache.commons.lang3.StringUtils;
import org.jooq.Record;

final class PersistencyToApiPolicyServiceTechClientMapper {
   private final JwtHmacAndAesGcmEncryption macAndEncrypter;

   public ApiPolicyServiceTechClientView.ApiPolicyServiceTechClientViewBuilder mapToClientBuilderAndSubscription(Record record) {
      ApiPolicyServiceTechClientView.ApiPolicyServiceTechClientViewBuilder tcBuilder = ApiPolicyServiceTechClientView.builder()
         .id(TechClientIdentityFactory.techClientId((String)record.get(PrincipalTable.PRINCIPAL.ID)))
         .displayName((String)record.get(PrincipalTable.PRINCIPAL.DISPLAY_NAME))
         .label((String)record.get(PrincipalTable.PRINCIPAL.LABEL))
         .apiKey(this.toApiKey(record))
         .locked((Integer)record.get(PrincipalTable.PRINCIPAL.LOCKED) != 0);
      if (StringUtils.isNotEmpty((CharSequence)record.get(SubscriptionTable.SUBSCRIPTION.ID))) {
         tcBuilder.subscriptionBuilder(this.toSubscriptionBuilder(record));
      }

      return tcBuilder;
   }

   public ApiPolicyServicePlanUsage mapToPlanUsage(Record record) {
      return ApiPolicyServicePlanUsage.builder()
         .name((String)record.get(PlanUsageTable.PLAN_USAGE.NAME))
         .rateLimit((Integer)record.get(PlanUsageTable.PLAN_USAGE.RATE_LIMIT))
         .build();
   }

   private ApiPolicyServiceApiKey toApiKey(Record record) {
      Plaintext decryptedSecret = this.decrypt((String)record.get(ApiKeyTable.API_KEY.SECRET));
      String fingerprint = (String)record.get(ApiKeyTable.API_KEY.FINGERPRINT);
      return ApiPolicyServiceApiKey.builder()
         .locked((Integer)record.get(ApiKeyTable.API_KEY.LOCKED) != 0)
         .validFrom(DbTimestamps.toInstantFromNullable((LocalDateTime)record.get(ApiKeyTable.API_KEY.VALID_FROM)))
         .validTo(DbTimestamps.toInstantFromNullable((LocalDateTime)record.get(ApiKeyTable.API_KEY.VALID_TO)))
         .secret(
            ApiKeySecret.apiKeySecret(
               ApiKeySecretValue.apiKeySecretValueFromString(decryptedSecret.getValue()),
               ApiKeySecretFingerprint.apiKeySecretFingerprintFromString(fingerprint)
            )
         )
         .build();
   }

   private ApiPolicyServiceSubscription.ApiPolicyServiceSubscriptionBuilder toSubscriptionBuilder(Record record) {
      return ApiPolicyServiceSubscription.builder()
         .id(ApiPolicyServiceSubscription.subscriptionId((String)record.get(SubscriptionTable.SUBSCRIPTION.ID)))
         .plans(new HashSet<>());
   }

   private Plaintext decrypt(String value) {
      try {
         return this.macAndEncrypter.decryptAndCheckHmac(new JweToken(value));
      } catch (TicketEncodingException e) {
         throw new FailureException("failed to decrypt API key secret", e);
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public PersistencyToApiPolicyServiceTechClientMapper(JwtHmacAndAesGcmEncryption macAndEncrypter) {
      this.macAndEncrypter = macAndEncrypter;
   }
}
