import io.github.daviddenton.k2.Filter
import io.github.daviddenton.k2.Service
import io.github.daviddenton.k2.Service.Companion.mk
import io.github.daviddenton.k2.Svc
import io.github.daviddenton.k2.contract.*
import io.github.daviddenton.k2.contract.ContentType.Companion.APPLICATION_FORM_URLENCODED
import io.github.daviddenton.k2.contract.ContentType.Companion.APPLICATION_XML
import io.github.daviddenton.k2.formats.Argo
import io.github.daviddenton.k2.formats.Argo.Json
import io.github.daviddenton.k2.formats.Argo.Json.asJson
import io.github.daviddenton.k2.formats.Argo.Json.obj
import io.github.daviddenton.k2.formats.Argo.Response
import io.github.daviddenton.k2.http.Method
import io.github.daviddenton.k2.http.Method.Get
import io.github.daviddenton.k2.http.Request
import io.github.daviddenton.k2.http.Root
import io.github.daviddenton.k2.module.render.RouteModule
import java.time.LocalDate

data class Bob(val value: String)
data class DateWrapper(val value: LocalDate)

fun main(args: Array<String>) {
    fun servicesAndFilters() {
        println("filters")
        val asInt = Filter.mk { req: String, svc: Service<Int, Int> -> svc(req.toInt()).toString() }
        val tripleResult = Filter.mk { req: Int, svc: Service<Int, Int> -> svc(req) * 3 }

        println((asInt.andThen(tripleResult).andThen(Service.const(123)))("321"))
    }

    fun routes() {
        println("routes")
        val bob = Path.of(Parameter.string().map(::Bob, Bob::value).single("bob"))
        val json = Path.of(Argo.parameter.single("bob"))
        val query = Query.required(Argo.parameter.single("bob"))

        fun svc(string: Bob): Svc = Service.const(Response(200).build())

        val serverRoute = Route("bob")
            .producing(APPLICATION_FORM_URLENCODED)
            .consuming(APPLICATION_XML)
            .body(Body.of(Body.string(ContentType.TEXT_HTML)))
            .taking(query) / "bob" / bob at Get bind ::svc
        val clientRoute = Route("bob2") / "bob" / bob at Get bindClient mk { r: Request -> Response(200).withContent(Json.array(Json.string(r.uri))).build() }

        val a = RouteModule(Root)
            .withRoute(serverRoute).toService()

        println(a(Request(Get, "/asd")))

    }

    fun parameters() {
        println("parameters")
        val date: NamedParameter<DateWrapper, DateWrapper> = Parameter.localDate().map(::DateWrapper).single("date")
        val dates: NamedParameter<DateWrapper, Iterable<DateWrapper>> = Parameter.localDate().map(::DateWrapper).multi("dates")

        val dateWOpt: DateWrapper? = Query.optional(date).from(Request(Get, "http://location/index?date=2012-10-02", ""))
        val dateWListOpt: Iterable<DateWrapper>? = Query.optional(dates).from(Request(Get, "http://location/index?dates=2012-10-02", ""))
        val dateW: DateWrapper = Query.required(date).from(Request(Get, "http://location/index?date=2012-10-02", ""))
        val dateWList: Iterable<DateWrapper> = Query.required(dates).from(Request(Get, "http://location/index?dates=2012-10-02", ""))

        println(Query.optional(date).extract(Request(Get, "http://location/index", "")))
        println(Query.optional(dates).extract(Request(Get, "http://location/index", "")))
        println(Query.required(date).extract(Request(Get, "http://location/index?date=2012-10-02", "")))
        println(Query.required(dates).extract(Request(Get, "http://location/index?dates=2012-10-02", "")))
    }

    fun bodies() {
        println("bodies")
        val field1 = FormField.required(Parameter.string().map(::Bob, Bob::value).multi("bob"))
        val field2 = FormField.required(Parameter.boolean().single("sue"))
        val form = Body.of(Form.mk(field1, field2)).from(Request(Method.Post, "http://location/index", "bob=hello&bob=hello2&sue=true"))
        println("${field1.named.name}=${form[field1]}")
        println("${field2.named.name}=${form[field2]}")

        Form(field1.of(listOf<Bob>()), field2.of(true))
        println(Body.of(Form.mk(field1, field2)).extract(Request(Method.Post, "http://location/index", "bob=hello&bob=hello2")))
        val body = Body.of(Argo.body.map({ Bob(it.getStringValue("name")) }) { obj("name" to it.value.asJson()) })
        println(body.from(Request(Get, "http://location/index?bob=foo&bob=fo2", """{"name":"bobby"}""")))
    }

    fun formats() {
        println("formats")
        println(obj("name" to Json.string("hello")))
    }

    servicesAndFilters()
    formats()
    routes()
    parameters()
    bodies()

}