package com.airlock.iam.admin.domain.model.token.airlock2fa;

import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FAAccountId;
import com.airlock.iam.airlock2fa.api.domain.model.Airlock2FADeviceId;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAccountStatus;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAUserAttributes;
import com.airlock.iam.factor.domain.model.airlock2fa.activation.Airlock2FAActivationLetter;
import com.airlock.iam.factor.domain.model.airlock2fa.hardware.Airlock2FAAdminHardwareDevice;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.NonNull;

public class Airlock2FAUserAccount {
   private final UserId userId;
   private final Airlock2FAAccountId accountId;
   private final Airlock2FAUserAttributes userAttributes;
   private final List<Airlock2FAAdminAppDevice> appDevices;
   private final List<Airlock2FAAdminHardwareDevice> hardwareDevices;
   private final List<Airlock2FAActivationLetter> activationLetters;

   public Airlock2FAUserAccount(
      @NonNull UserId userId,
      @NonNull Airlock2FAAccountId accountId,
      @NonNull Airlock2FAUserAttributes userAttributes,
      @NonNull List<Airlock2FAAdminAppDevice> appDevices,
      @NonNull List<Airlock2FAAdminHardwareDevice> hardwareDevices,
      @NonNull List<Airlock2FAActivationLetter> activationLetters
   ) {
      if (userId == null) {
         throw new NullPointerException("userId is marked non-null but is null");
      }

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

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

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

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

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

      this.userId = userId;
      this.accountId = accountId;
      this.userAttributes = userAttributes;
      this.appDevices = new ArrayList<>(appDevices);
      this.hardwareDevices = new ArrayList<>(hardwareDevices);
      this.activationLetters = new ArrayList<>(activationLetters);
   }

   public Set<Airlock2FADeviceId> getAllDeviceIds() {
      return Stream.concat(
            this.appDevices.stream().map(Airlock2FAAdminAppDevice::getDeviceId), this.hardwareDevices.stream().map(Airlock2FAAdminHardwareDevice::getDeviceId)
         )
         .collect(Collectors.toSet());
   }

   public List<Airlock2FAAdminAppDevice> getAppDevices() {
      return Collections.unmodifiableList(this.appDevices);
   }

   public List<Airlock2FAAdminHardwareDevice> getHardwareDevices() {
      return Collections.unmodifiableList(this.hardwareDevices);
   }

   public List<Airlock2FAActivationLetter> getActivationLetters() {
      return Collections.unmodifiableList(this.activationLetters);
   }

   public boolean isLocked() {
      return this.userAttributes.isLocked();
   }

   public void lock() {
      this.userAttributes.lock();
   }

   public void unlock() {
      this.userAttributes.unlock();
   }

   public boolean hasAppDeviceWithId(Airlock2FADeviceId deviceId) {
      return this.appDevices.stream().anyMatch(d -> d.hasId(deviceId));
   }

   public boolean hasHardwareDeviceWithId(Airlock2FADeviceId deviceId) {
      return this.hardwareDevices.stream().anyMatch(d -> d.hasId(deviceId));
   }

   public Optional<Airlock2FAAdminHardwareDevice> findHardwareDeviceWithId(Airlock2FADeviceId deviceId) {
      return this.hardwareDevices.stream().filter(device -> device.getDeviceId().equals(deviceId)).findFirst();
   }

   public void removeDeviceById(Airlock2FADeviceId airlock2FADeviceId) {
      this.appDevices.removeIf(d -> d.hasId(airlock2FADeviceId));
      this.hardwareDevices.removeIf(d -> d.hasId(airlock2FADeviceId));
   }

   public boolean hasStatus(Airlock2FAAccountStatus accountStatus) {
      return this.userAttributes.hasStatus(accountStatus);
   }

   public boolean hasSameStatus(Airlock2FAUserAccount account) {
      return account.userAttributes.getStatus().isEmpty()
         ? this.userAttributes.getStatus().isEmpty()
         : this.hasStatus((Airlock2FAAccountStatus)account.userAttributes.getStatus().get());
   }

   public Optional<Airlock2FAAccountStatus> getStatus() {
      return this.userAttributes.getStatus();
   }

   public boolean isPermanentlyDisabled() {
      return this.userAttributes.isPermanentlyDisabled();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public UserId getUserId() {
      return this.userId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2FAAccountId getAccountId() {
      return this.accountId;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Airlock2FAUserAttributes getUserAttributes() {
      return this.userAttributes;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class Airlock2FAUserAccountBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserId userId;
      @SuppressFBWarnings(justification = "generated code")
      private Airlock2FAAccountId accountId;
      @SuppressFBWarnings(justification = "generated code")
      private Airlock2FAUserAttributes userAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Airlock2FAAdminAppDevice> appDevices;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Airlock2FAAdminHardwareDevice> hardwareDevices;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<Airlock2FAActivationLetter> activationLetters;

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

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder userId(@NonNull UserId userId) {
         if (userId == null) {
            throw new NullPointerException("userId is marked non-null but is null");
         }

         this.userId = userId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder accountId(@NonNull Airlock2FAAccountId accountId) {
         if (accountId == null) {
            throw new NullPointerException("accountId is marked non-null but is null");
         }

         this.accountId = accountId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder userAttributes(@NonNull Airlock2FAUserAttributes userAttributes) {
         if (userAttributes == null) {
            throw new NullPointerException("userAttributes is marked non-null but is null");
         }

         this.userAttributes = userAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder appDevice(Airlock2FAAdminAppDevice appDevice) {
         if (this.appDevices == null) {
            this.appDevices = new ArrayList<>();
         }

         this.appDevices.add(appDevice);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder appDevices(Collection<? extends Airlock2FAAdminAppDevice> appDevices) {
         if (appDevices == null) {
            throw new NullPointerException("appDevices cannot be null");
         }

         if (this.appDevices == null) {
            this.appDevices = new ArrayList<>();
         }

         this.appDevices.addAll(appDevices);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder clearAppDevices() {
         if (this.appDevices != null) {
            this.appDevices.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder hardwareDevice(Airlock2FAAdminHardwareDevice hardwareDevice) {
         if (this.hardwareDevices == null) {
            this.hardwareDevices = new ArrayList<>();
         }

         this.hardwareDevices.add(hardwareDevice);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder hardwareDevices(Collection<? extends Airlock2FAAdminHardwareDevice> hardwareDevices) {
         if (hardwareDevices == null) {
            throw new NullPointerException("hardwareDevices cannot be null");
         }

         if (this.hardwareDevices == null) {
            this.hardwareDevices = new ArrayList<>();
         }

         this.hardwareDevices.addAll(hardwareDevices);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder clearHardwareDevices() {
         if (this.hardwareDevices != null) {
            this.hardwareDevices.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder activationLetter(Airlock2FAActivationLetter activationLetter) {
         if (this.activationLetters == null) {
            this.activationLetters = new ArrayList<>();
         }

         this.activationLetters.add(activationLetter);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder activationLetters(Collection<? extends Airlock2FAActivationLetter> activationLetters) {
         if (activationLetters == null) {
            throw new NullPointerException("activationLetters cannot be null");
         }

         if (this.activationLetters == null) {
            this.activationLetters = new ArrayList<>();
         }

         this.activationLetters.addAll(activationLetters);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount.Airlock2FAUserAccountBuilder clearActivationLetters() {
         if (this.activationLetters != null) {
            this.activationLetters.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Airlock2FAUserAccount build() {
         List appDevices = switch (this.appDevices == null ? 0 : this.appDevices.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.appDevices.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.appDevices));
         };

         List hardwareDevices = switch (this.hardwareDevices == null ? 0 : this.hardwareDevices.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.hardwareDevices.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.hardwareDevices));
         };

         return new Airlock2FAUserAccount(
            this.userId,
            this.accountId,
            this.userAttributes,
            appDevices,
            hardwareDevices,
            switch (this.activationLetters == null ? 0 : this.activationLetters.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.activationLetters.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.activationLetters));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "Airlock2FAUserAccount.Airlock2FAUserAccountBuilder(userId="
            + this.userId
            + ", accountId="
            + this.accountId
            + ", userAttributes="
            + this.userAttributes
            + ", appDevices="
            + this.appDevices
            + ", hardwareDevices="
            + this.hardwareDevices
            + ", activationLetters="
            + this.activationLetters
            + ")";
      }
   }
}
