package com.airlock.iam.common.infrastructure.sso;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.common.application.configuration.sso.InMemoryAcceptedSsoTicketRepositoryConfig;
import com.airlock.iam.common.domain.service.sso.AcceptedSsoTicketRepository;
import com.airlock.iam.database.infrastructure.TenantId;
import com.google.common.annotations.VisibleForTesting;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@LogicForConfig(InMemoryAcceptedSsoTicketRepositoryConfig.class)
public final class InMemoryAcceptedSsoTicketRepository implements AcceptedSsoTicketRepository {
   private static Instant latestCleanup = Instant.EPOCH;
   private static final Map<TenantId, AcceptedSsoTickets> ACCEPTED_TICKETS = new ConcurrentHashMap<>();
   private final Provider<TenantId> tenantId;

   @Inject
   public InMemoryAcceptedSsoTicketRepository(Provider<TenantId> tenantId) {
      this.tenantId = tenantId;
   }

   @Override
   public boolean containsAcceptedTicket(String ssoTicketId) {
      return this.ticketsOfCurrentTenant().containsAcceptedTicket(ssoTicketId);
   }

   @Override
   public void storeAcceptedTicket(String ssoTicketId, Instant expirationDate) {
      this.ticketsOfCurrentTenant().storeAcceptedTicket(ssoTicketId, expirationDate);
      if (isCleanupDue()) {
         cleanup();
      }
   }

   @VisibleForTesting
   static void clear() {
      ACCEPTED_TICKETS.clear();
   }

   private static void cleanup() {
      latestCleanup = Instant.now();

      for (AcceptedSsoTickets ticketsOfTenant : ACCEPTED_TICKETS.values()) {
         ticketsOfTenant.cleanup();
      }
   }

   private AcceptedSsoTickets ticketsOfCurrentTenant() {
      return ticketsOfTenant((TenantId)this.tenantId.get());
   }

   private static AcceptedSsoTickets ticketsOfTenant(TenantId tenantId) {
      ACCEPTED_TICKETS.putIfAbsent(tenantId, new AcceptedSsoTickets());
      return ACCEPTED_TICKETS.get(tenantId);
   }

   private static boolean isCleanupDue() {
      return latestCleanup.plusSeconds(10L).isBefore(Instant.now());
   }

   @VisibleForTesting
   static void setLastestCleanup(Instant instant) {
      latestCleanup = instant;
   }
}
