package com.airlock.iam.common.domain.model.otp;

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.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.base.misc.time.TimeProvider;
import com.airlock.iam.common.api.application.dependency.injection.LegacyPluginInjectorFactory;
import com.airlock.iam.common.api.domain.model.otp.OneTimePassword;
import com.airlock.iam.common.api.domain.model.otp.Otp;

public final class OtpMapifier {
   private static final String TYPE_KEY = "type";
   private static final String EXPIRING_TYPE = "expiring";
   private static final String NON_EXPIRING_TYPE = "nonExpiring";
   private static final String SIMULATED_TYPE = "simulated";
   private static final String VALUE_KEY = "value";
   private static final String EXPIRES_KEY = "expires";
   private static final String VALIDITY_KEY = "validity";
   private static final String INVALID_OTP_KEY = "invalidOtp";

   public static MapifiedValues mapifyOtp(OneTimePassword otp) {
      if (otp instanceof ExpiringOneTimePassword expiring) {
         return mapifyExpiringOtp(expiring);
      } else if (otp instanceof NonExpiringOneTimePassword nonExpiring) {
         return mapifyNonExpiringOtp(nonExpiring);
      } else if (otp instanceof SimulatedExpiringOneTimePassword simulated) {
         return mapifySimulatedOtp(simulated);
      } else {
         throw new FailureException("Unknown OTP implementation: " + otp.getClass().getName());
      }
   }

   private static MapifiedValues mapifyExpiringOtp(ExpiringOneTimePassword expiring) {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("type", "expiring")
         .put("value", expiring.value().rawValue())
         .put("expires", MapificationUtils.mapifyInstant(expiring.getExpiresAt()))
         .put("validity", MapificationUtils.mapifyDuration(expiring.getValidity()))
         .build();
   }

   private static MapifiedValues mapifyNonExpiringOtp(NonExpiringOneTimePassword nonExpiring) {
      return MapifiedValuesBuilder.mapifiedValues().put("type", "nonExpiring").put("value", nonExpiring.value().rawValue()).build();
   }

   private static MapifiedValues mapifySimulatedOtp(SimulatedExpiringOneTimePassword simulated) {
      return MapifiedValuesBuilder.mapifiedValues().put("type", "simulated").put("invalidOtp", mapifyOtp(simulated.getInvalidOneTimePassword())).build();
   }

   public static OneTimePassword demapifyOtp(MapifiedValues values) {
      return switch ((String)values.get("type")) {
         case "expiring" -> demapifyExpiringOtp(values);
         case "nonExpiring" -> demapifyNonExpiringOtp(values);
         case "simulated" -> demapifySimulatedOtp(values);
         default -> throw new FailureException("Unknown OTP type: " + values.get("type"));
      };
   }

   private static ExpiringOneTimePassword demapifyExpiringOtp(MapifiedValues values) {
      return new ExpiringOneTimePassword(
         Otp.otp(values.get("value")),
         MapificationUtils.demapifyInstant(values.getValues("expires")),
         MapificationUtils.demapifyDuration(values.getValues("validity")),
         LegacyPluginInjectorFactory.getInjector().getInstance(TimeProvider.class)
      );
   }

   private static NonExpiringOneTimePassword demapifyNonExpiringOtp(MapifiedValues values) {
      return new NonExpiringOneTimePassword(Otp.otp(values.get("value")));
   }

   private static SimulatedExpiringOneTimePassword demapifySimulatedOtp(MapifiedValues values) {
      return new SimulatedExpiringOneTimePassword(demapifyOtp(values.getValues("invalidOtp")));
   }
}
