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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.common.api.domain.model.cookie.SameSitePolicy;
import com.airlock.iam.common.domain.model.session.SessionNonce;
import com.airlock.iam.common.domain.model.state.SessionStateId;
import com.airlock.iam.common.infrastructure.util.CookieUtil;
import com.airlock.iam.core.misc.util.ServletUtils;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Optional;
import java.util.regex.Pattern;

public final class IamSessionCookieHelper {
   public static final String IAM_SESSION_COOKIE_NAME = "iam-session-id";
   private static final ELogger LOG = ELoggerFactory.getLogger(IamSessionCookieHelper.class);
   private static final String SEPARATOR = "-";
   private static final int LENGTH = 18 + "-".length() + 64;

   public static Optional<IamSessionCookieHelper.IamSessionCookie> readIamSessionCookie(HttpServletRequest request) {
      return Optional.ofNullable(ServletUtils.getCookie(request, "iam-session-id")).<String>map(Cookie::getValue).flatMap(IamSessionCookieHelper::parseCookie);
   }

   public static void writeIamSessionCookie(
      IamSessionCookieHelper.IamSessionCookie sessionCookie, HttpServletRequest request, HttpServletResponse response, SameSitePolicy sameSitePolicy
   ) {
      if (response.isCommitted()) {
         throw new FailureException("Unable to write session cookie: response already committed.");
      }

      SessionStateId sessionStateId = sessionCookie.sessionStateId();
      SessionNonce nonce = sessionCookie.nonce();
      Cookie cookie = new Cookie("iam-session-id", sessionStateId.id() + "-" + nonce.nonce());
      cookie.setPath(request.getContextPath());
      cookie.setHttpOnly(true);
      cookie.setSecure(request.isSecure());
      CookieUtil.setCookie(response, cookie, sameSitePolicy);
      LOG.debug("Setting session ID cookie");
   }

   public static void deleteIamSessionCookie(HttpServletRequest request, HttpServletResponse response) {
      ServletUtils.deleteCookie(request, response, "iam-session-id");
   }

   private static Optional<IamSessionCookieHelper.IamSessionCookie> parseCookie(String cookieValue) {
      if (cookieValue.length() != LENGTH) {
         LOG.debug("Session ID cookie has the wrong length, ignoring.");
         return Optional.empty();
      }

      String[] tokens = cookieValue.split(Pattern.quote("-"), 3);
      if (tokens.length != 2) {
         LOG.debug("Invalid session ID cookie not containing exactly one separator, ignoring.");
         return Optional.empty();
      }

      if (SessionStateId.isValidStateRepositoryKey(tokens[0]) && SessionNonce.isValidNonce(tokens[1])) {
         return Optional.of(new IamSessionCookieHelper.IamSessionCookie(new SessionStateId(tokens[0]), new SessionNonce(tokens[1])));
      }

      LOG.debug("Invalid session ID cookie, ignoring.");
      return Optional.empty();
   }

   private IamSessionCookieHelper() {
   }

   public record IamSessionCookie(SessionStateId sessionStateId, SessionNonce nonce) {
   }
}
