package dp.kotul.starter.filter

import org.slf4j.LoggerFactory
import org.slf4j.MDC
import org.springframework.stereotype.Component
import javax.servlet.DispatcherType
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.HttpServletRequest

private const val LOGGING_CORRELATION_ID = "correlation-id"
private const val LOGGING_SESSION_ID = "session-id"

@Component
class CorrelationHeaderAndLoggingFilter: Filter {
    private val log = LoggerFactory.getLogger(CorrelationHeaderAndLoggingFilter::class.java)

    override fun doFilter(servletRequest: ServletRequest, servletResponse: ServletResponse, filterChain: FilterChain) {
        val httpServletRequest = servletRequest as HttpServletRequest

        RequestCorrelation.setId(null)

        MDC.put(LOGGING_SESSION_ID, getSessionIdHash(httpServletRequest))

        if (!currentRequestIsAsyncDispatcher(httpServletRequest)) {
            val currentCorrId = httpServletRequest.getHeader(RequestCorrelation.CORRELATION_ID_HEADER)
            if (currentCorrId != null) {
                RequestCorrelation.setId(currentCorrId)
                MDC.put(LOGGING_CORRELATION_ID, RequestCorrelation.getId())
                log.info("Found correlationId in header: {}", currentCorrId)
            }
        }

        MDC.put(LOGGING_CORRELATION_ID, RequestCorrelation.getId())

        filterChain.doFilter(httpServletRequest, servletResponse)
    }

    private fun currentRequestIsAsyncDispatcher(httpServletRequest: HttpServletRequest): Boolean {
        return httpServletRequest.dispatcherType == DispatcherType.ASYNC
    }

    private fun getSessionIdHash(httpServletRequest: HttpServletRequest): String? {
        val session = httpServletRequest.getSession(false) ?: return null
        return session.id.hashCode().toString()
    }

    companion object {
        fun setCorrelationId(correlationId: String) {
            RequestCorrelation.setId(correlationId)
            MDC.put(LOGGING_CORRELATION_ID, correlationId)
        }
    }
}
