package com.airlock.iam.flow.shared.domain.model.mapification;

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.common.api.domain.model.device.DeviceId;
import com.airlock.iam.common.api.domain.model.device.DeviceTokenId;
import com.airlock.iam.common.api.domain.model.device.DeviceType;
import com.airlock.iam.common.api.domain.model.device.KnownDeviceTypes;
import com.airlock.iam.common.api.domain.model.device.usage.AuthenticationDeviceUsage;
import java.util.Arrays;
import java.util.Optional;

public class AuthenticationDeviceUsageMapifier {
   private static final String CLASS_KEY = "class";
   private static final String VALUE_KEY = "value";
   private static final String DEVICE_TYPE_KEY = "deviceType";
   private static final String DEVICE_ID_KEY = "deviceId";
   private static final String USED_AT_KEY = "usedAt";

   public static MapifiedValues mapifyAuthDeviceUsage(AuthenticationDeviceUsage deviceUsage) {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("deviceType", deviceUsage.deviceType().id())
         .put("deviceId", mapifyDeviceId(deviceUsage.deviceId()))
         .put("usedAt", MapificationUtils.mapifyInstant(deviceUsage.usedAt()))
         .build();
   }

   public static AuthenticationDeviceUsage demapifyAuthDeviceUsage(MapifiedValues values) {
      return new AuthenticationDeviceUsage(
         values.simpleValue("deviceType", AuthenticationDeviceUsageMapifier::demapifyDeviceType),
         values.demapifyValue("deviceId", AuthenticationDeviceUsageMapifier::demapifyDeviceId),
         MapificationUtils.demapifyInstant(values.getValues("usedAt"))
      );
   }

   private static MapifiedValues mapifyDeviceId(DeviceId deviceId) {
      return MapifiedValuesBuilder.mapifiedValues()
         .putIfPresent("class", Optional.of(deviceId).filter(v -> v instanceof DeviceTokenId).map(v -> v.getClass().getName()))
         .put("value", deviceId.id())
         .build();
   }

   private static DeviceId demapifyDeviceId(MapifiedValues values) {
      Optional<String> className = values.getOptional("class");
      String id = values.get("value");
      return className.isPresent() && className.get().equals(DeviceTokenId.class.getName()) ? new DeviceTokenId(id) : () -> id;
   }

   private static DeviceType demapifyDeviceType(String typeId) {
      return Arrays.stream(KnownDeviceTypes.values()).anyMatch(v -> v.id().equals(typeId)) ? KnownDeviceTypes.valueOf(typeId) : () -> typeId;
   }
}
