--:Minify:--
local http = {}

local syscall = syscall

local function parseUrl(url)
    local proto, host, path =
        url:match(
            "^(https?://)([^/]+)(/.*)$"
        )

    if not proto then
        proto, host =
            url:match(
                "^(https?://)([^/]+)$"
            )

        path = "/"
    end

    if not proto then
        return nil, "EINVAL"
    end

    return host, path
end

local function buildRequest(req)
    local host, path = parseUrl(req.url)

    if not host then
        return nil, path
    end

    local method =
        req.method or "GET"

    local body =
        req.body or ""

    local headers =
        table.deepcopy(
            req.headers or {}
        )

    headers.Host =
        headers.Host or host

    headers.Connection =
        headers.Connection or "close"

    if body ~= "" then
        headers["Content-Length"] =
            tostring(#body)
    end

    local out = {
        method ..
        " " ..
        path ..
        " HTTP/1.1"
    }

    for k, v in pairs(headers) do
        out[#out + 1] =
            tostring(k) ..
            ": " ..
            tostring(v)
    end

    out[#out + 1] = ""
    out[#out + 1] = body

    return table.concat(out, "\r\n")
end

local function parseResponse(response)
    local headerBlock, body = response:match("^(.-)\r\n\r\n(.*)$")
    if not headerBlock then
        return nil, "invalid HTTP response"
    end

    local headers = {}

    -- Parse status line
    local statusLine, rest = headerBlock:match("^(.-)\r\n(.*)$")
    if not statusLine then
        statusLine = headerBlock
        rest = ""
    end

    local version, code, message = statusLine:match("^(HTTP/%d+%.%d+)%s+(%d+)%s+(.*)$")

    -- Parse headers
    for line in (rest .. "\r\n"):gmatch("(.-)\r\n") do
        local key, value = line:match("^([^:]+):%s*(.*)$")
        if key then
            headers[key:lower()] = value
        end
    end

    return {
        code=tonumber(code),
        message=message,
        headers=headers,
        body=body
    }
end

function http.request(req)
    local raw, err =
        buildRequest(req)

    if not raw then
        return nil, err
    end

    local fd =
        syscall.socket()

    if not fd then
        return nil, "ESOCKET"
    end

    local host, path = parseUrl(req.url)

    if not host then
        syscall.close(fd)
        return nil, path
    end

    local proto = req.url:match("^(https?://)")

    local base = proto .. host

    local ok, err = syscall.connect(fd, base)

    if not ok then
        syscall.close(fd)
        return nil, err
    end

    local ok2, err2 = syscall.write(fd, raw)

    if not ok2 then
        syscall.close(fd)
        return nil, err2
    end

    local out = {}

    while true do
        local chunk = syscall.read(fd, 4096)
        if chunk == "" then
            break
        end

        out[#out+1] = chunk
    end

    syscall.close(fd)
    return parseResponse(table.concat(out))
end

function http.get(url, headers)
    return http.request({
        url = url,
        method = "GET",
        headers = headers
    })
end

function http.post(url, body, headers)
    return http.request({
        url = url,
        method = "POST",
        body = body,
        headers = headers
    })
end

return http