package ru.micord.ervu.security.webbpm.jwt.helper;

import java.net.IDN;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import static org.springframework.web.context.request.RequestAttributes.REFERENCE_REQUEST;
import static ru.micord.ervu.security.SecurityConstants.AUTH_MARKER;
import static ru.micord.ervu.security.SecurityConstants.AUTH_TOKEN;
import static ru.micord.ervu.security.SecurityConstants.PRNS_UUID;

public final class SecurityHelper {
  @Value("${cookie.path:#{null}}")
  private String accessCookiePath;
  @Value("${cookie.domain:#{null}}")
  private String accessCookieDomain;
  @Value("${cookie.secure:false}")
  private boolean accessCookieSecure;
  @Value("${cookie.same.site:Lax}")
  private String accessCookieSameSite;

  @PostConstruct
  private void init() {

    if (accessCookieDomain != null) {
      accessCookieDomain = IDN.toASCII(accessCookieDomain);
    }
  }

  public void clearAccessCookies(HttpServletResponse response) {
    clearCookie(response, AUTH_TOKEN, accessCookiePath);

    ResponseCookie emptyAuthMarker = createCookie(AUTH_MARKER, null, "/")
        .maxAge(0)
        .secure(false)
        .httpOnly(false)
        .build();
    addResponseCookie(response, emptyAuthMarker);
    clearCookie(response, PRNS_UUID, accessCookiePath);
  }

  public void addResponseCookie(HttpServletResponse response, ResponseCookie cookie) {
    response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
  }

  public void addAccessCookies(HttpServletResponse response, String cookieValue, int expiry) {
    ResponseCookie authTokenCookie = createCookie(AUTH_TOKEN, cookieValue, accessCookiePath)
        .maxAge(expiry)
        .build();
    addResponseCookie(response, authTokenCookie);

    ResponseCookie authMarker = createCookie(AUTH_MARKER, "true", "/")
        .maxAge(expiry)
        .secure(false)
        .httpOnly(false)
        .build();
    addResponseCookie(response, authMarker);
  }

  public ResponseCookie.ResponseCookieBuilder createCookie(String name, String value, String path) {
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

    if (requestAttributes == null) {
      throw new IllegalStateException("Must be called only in request context");
    }
    HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(
        REFERENCE_REQUEST);

    if (request == null) {
      throw new IllegalStateException("Must be called only in request context");
    }
    String cookieValue = value != null ? URLEncoder.encode(value, StandardCharsets.UTF_8) : "";
    return ResponseCookie.from(name, cookieValue)
        .path(path != null ? path : request.getContextPath())
        .httpOnly(true)
        .domain(accessCookieDomain)
        .secure(accessCookieSecure)
        .sameSite(accessCookieSameSite);
  }

  public void clearCookie(HttpServletResponse response, String name, String path) {
    ResponseCookie emptyCookie = createCookie(name, null, path)
        .maxAge(0).build();
    addResponseCookie(response, emptyCookie);
  }

  public ResponseCookie.ResponseCookieBuilder createAccessCookie(String name, String value) {
    return createCookie(name, value, accessCookiePath);
  }

  public void clearAccessCookie(HttpServletResponse response, String name) {
    clearCookie(response, name, accessCookiePath);
  }
}
