package com.airlock.iam.common.infrastructure.mapper.event;

import com.airlock.iam.base.domain.model.event.UnknownEventSource;
import com.airlock.iam.base.misc.collection.IamMaps;
import com.airlock.iam.common.api.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.common.api.domain.model.event.AuthenticationDeviceAdded;
import com.airlock.iam.common.api.domain.model.event.AuthenticationDeviceDeleted;
import com.airlock.iam.common.api.domain.model.event.AuthenticationDeviceEvent;
import com.airlock.iam.common.api.domain.model.event.AuthenticationDeviceUsed;
import com.airlock.iam.common.api.domain.model.event.AuthenticationMethodChanged;
import com.airlock.iam.common.api.domain.model.event.ContextDataChanged;
import com.airlock.iam.common.api.domain.model.event.CrontoDeviceActivated;
import com.airlock.iam.common.api.domain.model.event.CrontoDeviceDeleted;
import com.airlock.iam.common.api.domain.model.event.CrontoLetterOrdered;
import com.airlock.iam.common.api.domain.model.event.DeviceTokenDeleted;
import com.airlock.iam.common.api.domain.model.event.DeviceTokenRegistered;
import com.airlock.iam.common.api.domain.model.event.EmailAddressAdded;
import com.airlock.iam.common.api.domain.model.event.EmailAddressChanged;
import com.airlock.iam.common.api.domain.model.event.EmailAddressDeleted;
import com.airlock.iam.common.api.domain.model.event.EventWithOldPhoneNumber;
import com.airlock.iam.common.api.domain.model.event.MtanTokenDeleted;
import com.airlock.iam.common.api.domain.model.event.MtanTokenPhoneNumberChanged;
import com.airlock.iam.common.api.domain.model.event.MtanTokenRegistered;
import com.airlock.iam.common.api.domain.model.event.OathOtpSecretAdded;
import com.airlock.iam.common.api.domain.model.event.OathOtpSecretViewed;
import com.airlock.iam.common.api.domain.model.event.PasswordChanged;
import com.airlock.iam.common.api.domain.model.event.PasswordLetterOrdered;
import com.airlock.iam.common.api.domain.model.event.UserCreated;
import com.airlock.iam.common.api.domain.model.event.UserDeleted;
import com.airlock.iam.common.api.domain.model.event.UserLocked;
import com.airlock.iam.common.api.domain.model.event.UserRolesChanged;
import com.airlock.iam.common.api.domain.model.event.UserUnlocked;
import com.airlock.iam.common.domain.model.role.RolesUtil;
import com.airlock.iam.common.infrastructure.event.EventPayloadData;
import com.airlock.iam.common.infrastructure.event.EventPayloadSource;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.mapstruct.Mapper;

@Mapper
public interface CommonDomainEventMapper {
   static Map<String, Object> mapAuthenticationDeviceEvent(AuthenticationDeviceEvent event) {
      return Map.of("authenticationMethod", event.authenticationMethod().getId());
   }

   default Map<String, Object> mapAuthenticationDeviceChangedEvent(AuthenticationDeviceEvent event) {
      return mapAuthenticationDeviceEvent(event);
   }

   default Map<String, Object> mapAuthenticationDeviceAdded(AuthenticationDeviceAdded event) {
      return mapAuthenticationDeviceEvent(event);
   }

   default Map<String, Object> mapAuthenticationDeviceDeleted(AuthenticationDeviceDeleted event) {
      return mapAuthenticationDeviceEvent(event);
   }

   default Map<String, Object> mapAuthenticationDeviceUsed(AuthenticationDeviceUsed event) {
      return mapAuthenticationDeviceEvent(event);
   }

   default Map<String, Object> mapEventWithOldPhoneNumber(EventWithOldPhoneNumber event) {
      return Map.of("oldPhoneNumber", event.oldPhoneNumber().getValue());
   }

   default EventPayloadData mapDeviceTokenDeleted(DeviceTokenDeleted event) {
      return new EventPayloadData(IamMaps.mergeMaps(mapAuthenticationDeviceEvent(event), Map.of("deviceTokenId", event.deviceTokenId().id())));
   }

   default EventPayloadData mapDeviceTokenRegistered(DeviceTokenRegistered event) {
      return new EventPayloadData(IamMaps.mergeMaps(mapAuthenticationDeviceEvent(event), Map.of("deviceTokenId", event.deviceTokenId().id())));
   }

   default EventPayloadData mapMtanTokenDeleted(MtanTokenDeleted event) {
      return new EventPayloadData(
         IamMaps.mergeMaps(mapAuthenticationDeviceEvent(event), this.mapEventWithOldPhoneNumber(event), Map.of("mtanNumberId", event.mtanNumberId().getId()))
      );
   }

   default EventPayloadData mapMtanTokenRegistered(MtanTokenRegistered event) {
      return new EventPayloadData(
         IamMaps.mergeMaps(
            mapAuthenticationDeviceEvent(event), Map.of("mtanNumberId", event.mtanNumberId().getId(), "newPhoneNumber", event.newPhoneNumber().getValue())
         )
      );
   }

   default EventPayloadData mapMtanTokenPhoneNumberChanged(MtanTokenPhoneNumberChanged event) {
      return new EventPayloadData(
         IamMaps.mergeMaps(
            this.mapAuthenticationDeviceChangedEvent(event),
            this.mapEventWithOldPhoneNumber(event),
            Map.of("mtanNumberId", event.mtanNumberId().getId(), "newPhoneNumber", event.newPhoneNumber().getValue())
         )
      );
   }

   default EventPayloadData mapUserLocked(UserLocked event) {
      return new EventPayloadData(Map.of("lockReason", event.lockReason().getResourceKey()));
   }

   default EventPayloadData mapUserCreated(UserCreated event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapUserDeleted(UserDeleted event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapUserUnlocked(UserUnlocked event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapUserRolesChanged(UserRolesChanged event) {
      return new EventPayloadData(
         Map.of("addedRoles", RolesUtil.rolesToStringList(event.addedRoles()), "removedRoles", RolesUtil.rolesToStringList(event.removedRoles()))
      );
   }

   default EventPayloadData mapEmailAddressDeleted(EmailAddressDeleted event) {
      return new EventPayloadData(Map.of("oldEmailAddress", event.oldEmailAddress().value()));
   }

   default EventPayloadData mapEmailAddressAdded(EmailAddressAdded event) {
      return new EventPayloadData(Map.of("newEmailAddress", event.newEmailAddress().value()));
   }

   default EventPayloadData mapEmailAddressChanged(EmailAddressChanged event) {
      return new EventPayloadData(Map.of("newEmailAddress", event.newEmailAddress().value(), "oldEmailAddress", event.oldEmailAddress().value()));
   }

   default EventPayloadData mapAuthenticationMethodChanged(AuthenticationMethodChanged event) {
      return new EventPayloadData(
         Map.of(
            "activeAuthenticationMethod",
            event.activeAuthenticationMethod().map(AuthenticationMethod::getId).orElse(""),
            "previousAuthenticationMethod",
            event.previousAuthenticationMethod().map(AuthenticationMethod::getId).orElse("")
         )
      );
   }

   default EventPayloadData mapCrontoDeviceDeleted(CrontoDeviceDeleted event) {
      return new EventPayloadData(IamMaps.mergeMaps(mapAuthenticationDeviceEvent(event), Map.of("crontoDeviceId", event.deviceId().getDisplayId())));
   }

   default EventPayloadData mapCrontoDeviceActivated(CrontoDeviceActivated event) {
      return new EventPayloadData(IamMaps.mergeMaps(mapAuthenticationDeviceEvent(event), Map.of("crontoDeviceId", event.deviceId().getDisplayId())));
   }

   default EventPayloadData mapCrontoLetterOrdered(CrontoLetterOrdered event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapContextDataChanged(ContextDataChanged event) {
      return new EventPayloadData(Map.of("contextDataChanges", this.mapContextDataChanges(event.changes())));
   }

   default EventPayloadData mapMapOathOtpSecretAdded(OathOtpSecretAdded event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapOathOtpSecretViewed(OathOtpSecretViewed event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapPasswordChanged(PasswordChanged event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default EventPayloadData mapPasswordLetterOrdered(PasswordLetterOrdered event) {
      return new EventPayloadData(Collections.emptyMap());
   }

   default List<Map<String, Map<String, Object>>> mapContextDataChanges(Set<ContextDataChanged.ContextDataChange> contextDataChanges) {
      return contextDataChanges.stream().map(contextDataChange -> Map.of(contextDataChange.name(), this.mapContextDataChange(contextDataChange))).toList();
   }

   default Map<String, Object> mapContextDataChange(ContextDataChanged.ContextDataChange contextDataChange) {
      Map<String, Object> mappedContextDataChange = new HashMap<>();
      if (contextDataChange.oldValue() != null) {
         mappedContextDataChange.put("oldValue", contextDataChange.oldValue());
      }

      if (contextDataChange.newValue() != null) {
         mappedContextDataChange.put("newValue", contextDataChange.newValue());
      }

      return mappedContextDataChange;
   }

   default EventPayloadSource mapUnknownEventSource(UnknownEventSource ignoredSource) {
      return new EventPayloadSource(Map.of("sourceType", "unknown"));
   }
}
