package com.airlock.iam.login.application.service.kerberos;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import jakarta.annotation.Nullable;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Optional;
import javax.security.auth.Subject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import org.ietf.jgss.GSSContext;

public class LegacyKerberosServiceImpl implements LegacyKerberosService {
   private static final ELogger DEFAULT_LOGGER = ELoggerFactory.getLogger(LegacyKerberosServiceImpl.class);
   @VisibleForTesting
   static final byte[] NTLM_MESSAGE_HEADER = "NTLMSSP\u0000".getBytes(StandardCharsets.US_ASCII);
   private final KerberosService kerberosService;

   @VisibleForTesting
   public LegacyKerberosServiceImpl(
      Subject subject, KerberosValidateActionFactory validateActionFactory, ELogger logger, @Nullable ConfigLoggingInfo configLoggingInfo
   ) {
      this.kerberosService = new KerberosServiceImpl(subject, validateActionFactory, Optional.ofNullable(configLoggingInfo), logger);
   }

   public LegacyKerberosServiceImpl(Subject subject, KerberosValidateActionFactory validateActionFactory, ConfigLoggingInfo configLoggingInfo) {
      this(subject, validateActionFactory, DEFAULT_LOGGER, configLoggingInfo);
   }

   public LegacyKerberosServiceImpl(Subject subject, KerberosValidateActionFactory validateActionFactory) {
      this(subject, validateActionFactory, DEFAULT_LOGGER, null);
   }

   @Override
   public boolean hasRequestValidSpnegoHeader(HttpServletRequest request) {
      return request.getHeader("Authorization") != null && request.getHeader("Authorization").startsWith("Negotiate ");
   }

   @Override
   public byte[] extractSpnegoDataFromRequest(HttpServletRequest request) {
      Preconditions.checkArgument(this.hasRequestValidSpnegoHeader(request));
      String authHeader = request.getHeader("Authorization");
      return Base64.decodeBase64(authHeader.substring("Negotiate ".length()));
   }

   @Override
   public boolean isSpnegoDataOfTypeNTLM(byte[] data) {
      Preconditions.checkNotNull(data);
      return Arrays.equals(NTLM_MESSAGE_HEADER, ArrayUtils.subarray(data, 0, NTLM_MESSAGE_HEADER.length));
   }

   @Override
   public KerberosValidationResult validateSpnegoData(byte[] spnegoData, Optional<GSSContext> gssContext) {
      Preconditions.checkNotNull(spnegoData);
      return this.kerberosService.validateSpnegoData(spnegoData, gssContext);
   }

   @Override
   public void sendInitialAuthenticateResponse(HttpServletResponse response) {
      Preconditions.checkNotNull(response);

      try {
         response.addHeader("WWW-Authenticate", "Negotiate");
         response.sendError(401, "please authorize");
      } catch (IOException e) {
         throw new FailureException("IO Exception", e);
      }
   }

   @Override
   public void sendSubsequentAuthenticateResponse(HttpServletResponse response, byte[] spnegoResponseData) {
      Preconditions.checkNotNull(response);
      Preconditions.checkNotNull(spnegoResponseData);

      try {
         response.addHeader("WWW-Authenticate", "Negotiate " + Base64.encodeBase64String(spnegoResponseData));
         response.sendError(401, "please authorize");
      } catch (IOException e) {
         throw new FailureException("IO exception", e);
      }
   }
}
