package com.airlock.iam.common.domain.service.sms;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.common.api.domain.model.exception.SmsGatewayUnavailableException;
import com.airlock.iam.common.domain.model.sms.Sms;
import com.airlock.iam.common.domain.model.sms.SmsMessageId;
import com.airlock.iam.common.domain.model.sms.SmsValidator;
import com.airlock.iam.core.misc.sms.MessageIdNotFound;
import com.airlock.iam.core.misc.sms.SmsGateway;
import com.airlock.iam.core.misc.sms.SmsGatewayException;
import com.airlock.iam.log.api.signing.SigningLogger;
import com.airlock.iam.log.audit.AuditLogger;
import com.google.common.base.Preconditions;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import java.util.Optional;
import lombok.NonNull;
import org.apache.commons.lang3.StringUtils;

public class SmsService {
   private final SmsService.SmsServiceSettings settings;
   private final SigningLogger signingLogger;

   @Inject
   public SmsService(SmsService.SmsServiceSettings settings, SigningLogger signingLogger) {
      this.settings = settings;
      this.signingLogger = signingLogger;
   }

   public boolean isValid(Sms sms) {
      Preconditions.checkNotNull(sms);
      if (sms.hasOriginator() && this.hasConfiguredOriginator() && !sms.getOriginator().equals(this.settings.getConfiguredOriginator())) {
         this.signingLogger.log("An originator is already preconfigured and must not be present in the SMS to send");
         return false;
      } else {
         sms = this.withConfiguredOriginator(sms);
         return this.settings.getSmsValidator().isValid(sms);
      }
   }

   public SmsMessageId send(Sms sms) throws ViolationException, SmsGatewayUnavailableException {
      Preconditions.checkNotNull(sms);
      if (!this.isValid(sms)) {
         throw new ViolationException("The SMS to be sent violates the service's contract.");
      }

      sms = this.withConfiguredOriginator(sms);

      try {
         String messageId = this.settings.getSmsGateway().sendSms(sms.getOriginator(), sms.getPhoneNumber(), sms.getMessage(), sms.isFlashMessage());
         return SmsMessageId.builder().id(Optional.ofNullable(messageId)).build();
      } catch (SmsGatewayException e) {
         throw new FailureException(
            "Error while sending SMS with gateway " + this.settings.getSmsGateway().getClass().getSimpleName() + ": " + e.getMessage(), e
         );
      }
   }

   public SmsGateway.DeliveryStatus getDeliveryStatus(SmsMessageId id) throws FailureException {
      Preconditions.checkNotNull(id);
      if (!id.exists()) {
         return SmsGateway.DeliveryStatus.UNKNOWN;
      }

      try {
         return !this.settings.getSmsGateway().deliveryStatusSupported()
            ? SmsGateway.DeliveryStatus.UNKNOWN
            : this.settings.getSmsGateway().getDeliveryStatus(id.getId());
      } catch (MessageIdNotFound e) {
         AuditLogger.log(this.getClass(), "Message ID not found in response for message '" + id.getId() + "'");
         return SmsGateway.DeliveryStatus.UNKNOWN;
      } catch (SmsGatewayException e) {
         throw new FailureException("Error while retrieving delivery status for message with ID '" + id.getId() + "'", e);
      }
   }

   private Sms withConfiguredOriginator(Sms sms) {
      return this.hasConfiguredOriginator() ? sms.toBuilder().originator(this.settings.getConfiguredOriginator()).build() : sms;
   }

   private boolean hasConfiguredOriginator() {
      return StringUtils.isNotBlank(this.settings.getConfiguredOriginator());
   }

   @SuppressFBWarnings(justification = "generated code")
   public static SmsService.SmsServiceBuilder builder() {
      return new SmsService.SmsServiceBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class SmsServiceBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private SmsService.SmsServiceSettings settings;
      @SuppressFBWarnings(justification = "generated code")
      private SigningLogger signingLogger;

      @SuppressFBWarnings(justification = "generated code")
      SmsServiceBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public SmsService.SmsServiceBuilder settings(SmsService.SmsServiceSettings settings) {
         this.settings = settings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SmsService.SmsServiceBuilder signingLogger(SigningLogger signingLogger) {
         this.signingLogger = signingLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SmsService build() {
         return new SmsService(this.settings, this.signingLogger);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "SmsService.SmsServiceBuilder(settings=" + this.settings + ", signingLogger=" + this.signingLogger + ")";
      }
   }

   public static final class SmsServiceSettings {
      @NonNull
      private final SmsGateway smsGateway;
      @NonNull
      private final SmsValidator smsValidator;
      @NonNull
      private final String configuredOriginator;

      @SuppressFBWarnings(justification = "generated code")
      SmsServiceSettings(@NonNull SmsGateway smsGateway, @NonNull SmsValidator smsValidator, @NonNull String configuredOriginator) {
         if (smsGateway == null) {
            throw new NullPointerException("smsGateway is marked non-null but is null");
         }

         if (smsValidator == null) {
            throw new NullPointerException("smsValidator is marked non-null but is null");
         }

         if (configuredOriginator == null) {
            throw new NullPointerException("configuredOriginator is marked non-null but is null");
         }

         this.smsGateway = smsGateway;
         this.smsValidator = smsValidator;
         this.configuredOriginator = configuredOriginator;
      }

      @SuppressFBWarnings(justification = "generated code")
      public static SmsService.SmsServiceSettings.SmsServiceSettingsBuilder builder() {
         return new SmsService.SmsServiceSettings.SmsServiceSettingsBuilder();
      }

      @NonNull
      @SuppressFBWarnings(justification = "generated code")
      public SmsGateway getSmsGateway() {
         return this.smsGateway;
      }

      @NonNull
      @SuppressFBWarnings(justification = "generated code")
      public SmsValidator getSmsValidator() {
         return this.smsValidator;
      }

      @NonNull
      @SuppressFBWarnings(justification = "generated code")
      public String getConfiguredOriginator() {
         return this.configuredOriginator;
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public boolean equals(Object o) {
         if (o == this) {
            return true;
         } else if (!(o instanceof SmsService.SmsServiceSettings other)) {
            return false;
         } else {
            Object this$smsGateway = this.smsGateway;
            Object other$smsGateway = other.smsGateway;
            if (this$smsGateway == null ? other$smsGateway == null : this$smsGateway.equals(other$smsGateway)) {
               Object this$smsValidator = this.smsValidator;
               Object other$smsValidator = other.smsValidator;
               if (this$smsValidator == null ? other$smsValidator == null : this$smsValidator.equals(other$smsValidator)) {
                  Object this$configuredOriginator = this.configuredOriginator;
                  Object other$configuredOriginator = other.configuredOriginator;
                  return this$configuredOriginator == null ? other$configuredOriginator == null : this$configuredOriginator.equals(other$configuredOriginator);
               } else {
                  return false;
               }
            } else {
               return false;
            }
         }
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public int hashCode() {
         int PRIME = 59;
         int result = 1;
         Object $smsGateway = this.smsGateway;
         result = result * 59 + ($smsGateway == null ? 43 : $smsGateway.hashCode());
         Object $smsValidator = this.smsValidator;
         result = result * 59 + ($smsValidator == null ? 43 : $smsValidator.hashCode());
         Object $configuredOriginator = this.configuredOriginator;
         return result * 59 + ($configuredOriginator == null ? 43 : $configuredOriginator.hashCode());
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "SmsService.SmsServiceSettings(smsGateway="
            + this.getSmsGateway()
            + ", smsValidator="
            + this.getSmsValidator()
            + ", configuredOriginator="
            + this.getConfiguredOriginator()
            + ")";
      }

      @SuppressFBWarnings(justification = "generated code")
      public static class SmsServiceSettingsBuilder {
         @SuppressFBWarnings(justification = "generated code")
         private SmsGateway smsGateway;
         @SuppressFBWarnings(justification = "generated code")
         private SmsValidator smsValidator;
         @SuppressFBWarnings(justification = "generated code")
         private String configuredOriginator;

         @SuppressFBWarnings(justification = "generated code")
         SmsServiceSettingsBuilder() {
         }

         @SuppressFBWarnings(justification = "generated code")
         public SmsService.SmsServiceSettings.SmsServiceSettingsBuilder smsGateway(@NonNull SmsGateway smsGateway) {
            if (smsGateway == null) {
               throw new NullPointerException("smsGateway is marked non-null but is null");
            }

            this.smsGateway = smsGateway;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public SmsService.SmsServiceSettings.SmsServiceSettingsBuilder smsValidator(@NonNull SmsValidator smsValidator) {
            if (smsValidator == null) {
               throw new NullPointerException("smsValidator is marked non-null but is null");
            }

            this.smsValidator = smsValidator;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public SmsService.SmsServiceSettings.SmsServiceSettingsBuilder configuredOriginator(@NonNull String configuredOriginator) {
            if (configuredOriginator == null) {
               throw new NullPointerException("configuredOriginator is marked non-null but is null");
            }

            this.configuredOriginator = configuredOriginator;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public SmsService.SmsServiceSettings build() {
            return new SmsService.SmsServiceSettings(this.smsGateway, this.smsValidator, this.configuredOriginator);
         }

         @SuppressFBWarnings(justification = "generated code")
         @Override
         public String toString() {
            return "SmsService.SmsServiceSettings.SmsServiceSettingsBuilder(smsGateway="
               + this.smsGateway
               + ", smsValidator="
               + this.smsValidator
               + ", configuredOriginator="
               + this.configuredOriginator
               + ")";
         }
      }
   }
}
