<commit_before>package org.http4k.core.cookie

import org.http4k.core.Parameters
import org.http4k.quoted
import org.http4k.unquoted
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter.ofPattern
import java.util.*
import java.util.Locale.US

data class Cookie(val name: String, val value: String,
                  val maxAge: Long? = null,
                  val expires: LocalDateTime? = null,
                  val domain: String? = null,
                  val path: String? = null,
                  val secure: Boolean = false,
                  val httpOnly: Boolean = false,
                  val sameSite: SameSite? = null) {

    fun domain(domain: String) = copy(domain = domain)
    fun maxAge(seconds: Long) = copy(maxAge = seconds)
    fun path(path: String) = copy(path = path)
    fun secure() = copy(secure = true)
    fun httpOnly() = copy(httpOnly = true)
    fun expires(date: LocalDateTime): Cookie = copy(expires = date)
    fun sameSite(sameSite: SameSite) = copy(sameSite = sameSite)

    override fun toString(): String = fullCookieString()

    private fun attributes(): String = mutableListOf<String>().apply {
        appendIfPresent(maxAge, "Max-Age=$maxAge")
        appendIfPresent(expires, "Expires=${expires?.let { ZonedDateTime.of(it, ZoneId.of("GMT")).format(RFC822) }}")
        appendIfPresent(domain, "Domain=$domain")
        appendIfPresent(path, "Path=$path")
        appendIfTrue(secure, "secure")
        appendIfTrue(httpOnly, "HttpOnly")
        appendIfPresent(sameSite, "SameSite=$sameSite")
    }.joinToString("; ")

    companion object {
        fun parse(cookieValue: String): Cookie? {
            val pair = cookieValue.split("=", limit = 2)
            return when {
                pair.size < 2 -> null
                else -> {
                    val valueAndAttributes = pair[1].split(";")
                    val attributes = valueAndAttributes.drop(1)
                        .map { it.trimStart() }
                        .map { attrString -> attrString.split("=").let { it[0] to it.getOrNull(1) } }
                    Cookie(pair[0], valueAndAttributes[0].unquoted(),
                        attributes.maxAge(), attributes.expires(), attributes.domain(),
                        attributes.path(), attributes.secure(), attributes.httpOnly(), attributes.sameSite())
                }
            }
        }

        private fun Parameters.maxAge(): Long? = find { it.first.equals("Max-Age", true) }?.second?.toLong()
        private fun Parameters.expires(): LocalDateTime? = find { it.first.equals("Expires", true) }?.second?.parseDate()
        private fun Parameters.domain(): String? = find { it.first.equals("Domain", true) }?.second
        private fun Parameters.path(): String? = find { it.first.equals("Path", true) }?.second
        private fun Parameters.secure(): Boolean = find { it.first.equals("secure", true) } != null
        private fun Parameters.httpOnly(): Boolean = find { it.first.equals("HttpOnly", true) } != null
        private fun Parameters.sameSite(): SameSite? = find { it.first.equals("SameSite", true) }?.second?.parseSameSite()

        private fun String.parseDate(): LocalDateTime? {
            println("got $this")
            for (supportedFormat in supportedFormats) {
                try {
                    return supportedFormat.parse(this).let { LocalDateTime.from(it) }
                } catch (e: Exception) {
                    println("error: $e")
                }
            }
            return null
        }

        private fun String.parseSameSite() = try {<commit_msg>Remove debug<commit_after>package org.http4k.core.cookie

import org.http4k.core.Parameters
import org.http4k.quoted
import org.http4k.unquoted
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter.ofPattern
import java.util.*
import java.util.Locale.US

data class Cookie(val name: String, val value: String,
                  val maxAge: Long? = null,
                  val expires: LocalDateTime? = null,
                  val domain: String? = null,
                  val path: String? = null,
                  val secure: Boolean = false,
                  val httpOnly: Boolean = false,
                  val sameSite: SameSite? = null) {

    fun domain(domain: String) = copy(domain = domain)
    fun maxAge(seconds: Long) = copy(maxAge = seconds)
    fun path(path: String) = copy(path = path)
    fun secure() = copy(secure = true)
    fun httpOnly() = copy(httpOnly = true)
    fun expires(date: LocalDateTime): Cookie = copy(expires = date)
    fun sameSite(sameSite: SameSite) = copy(sameSite = sameSite)

    override fun toString(): String = fullCookieString()

    private fun attributes(): String = mutableListOf<String>().apply {
        appendIfPresent(maxAge, "Max-Age=$maxAge")
        appendIfPresent(expires, "Expires=${expires?.let { ZonedDateTime.of(it, ZoneId.of("GMT")).format(RFC822) }}")
        appendIfPresent(domain, "Domain=$domain")
        appendIfPresent(path, "Path=$path")
        appendIfTrue(secure, "secure")
        appendIfTrue(httpOnly, "HttpOnly")
        appendIfPresent(sameSite, "SameSite=$sameSite")
    }.joinToString("; ")

    companion object {
        fun parse(cookieValue: String): Cookie? {
            val pair = cookieValue.split("=", limit = 2)
            return when {
                pair.size < 2 -> null
                else -> {
                    val valueAndAttributes = pair[1].split(";")
                    val attributes = valueAndAttributes.drop(1)
                        .map { it.trimStart() }
                        .map { attrString -> attrString.split("=").let { it[0] to it.getOrNull(1) } }
                    Cookie(pair[0], valueAndAttributes[0].unquoted(),
                        attributes.maxAge(), attributes.expires(), attributes.domain(),
                        attributes.path(), attributes.secure(), attributes.httpOnly(), attributes.sameSite())
                }
            }
        }

        private fun Parameters.maxAge(): Long? = find { it.first.equals("Max-Age", true) }?.second?.toLong()
        private fun Parameters.expires(): LocalDateTime? = find { it.first.equals("Expires", true) }?.second?.parseDate()
        private fun Parameters.domain(): String? = find { it.first.equals("Domain", true) }?.second
        private fun Parameters.path(): String? = find { it.first.equals("Path", true) }?.second
        private fun Parameters.secure(): Boolean = find { it.first.equals("secure", true) } != null
        private fun Parameters.httpOnly(): Boolean = find { it.first.equals("HttpOnly", true) } != null
        private fun Parameters.sameSite(): SameSite? = find { it.first.equals("SameSite", true) }?.second?.parseSameSite()

        private fun String.parseDate(): LocalDateTime? {
            for (supportedFormat in supportedFormats) {
                try {
                    return supportedFormat.parse(this).let { LocalDateTime.from(it) }
                } catch (e: Exception) {
                }
            }
            return null
        }

        private fun String.parseSameSite() = try {
{'fraction_non_alphanumeric': 0.12849469770004132, 'fraction_numerical': 0.0041316623054675665, 'mean_word_length': 2.3190127970749543, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3137936', 'n_tokens_mistral': 2018, 'n_tokens_neox': 1924, 'n_words': 473}