local skynet = require("skynet")
local httpc = require "http.httpc"
local json = require "json"
local conf = require("tracing.conf")
local assert = assert

------------------------------------------------

local zipkin_reporter_methods = {}
local zipkin_reporter_mt = {
    __index = zipkin_reporter_methods,
}

local function new()
    assert(conf.http_host)
    assert(conf.http_uri)

    local reporter = setmetatable({
        http_host = conf.http_host,
        http_uri = conf.http_uri,
        pending_spans = {},
        pending_spans_n = 0,
    }, zipkin_reporter_mt)
    reporter:init()
    return reporter
end

function zipkin_reporter_methods:init()
    skynet.timeout(conf.flush_interval * 100, handler(self, self.flush))
end

function zipkin_reporter_methods:add_span(span)
    local i = self.pending_spans_n + 1
    self.pending_spans[i] = span
    self.pending_spans_n = i

    -- gLog.d("span", i, json.encode(span))
    -- Log.d("add_span", i, json.encode(self.pending_spans))

    if self.pending_spans_n >= conf.flush_max_span then
        self:flush()
    end
end

function zipkin_reporter_methods:timerHander()
    self.flush()
    skynet.timeout(conf.flush_interval * 100, handler(self, self.flush))
end

local header = {
    ["content-type"] = "application/json",
}

function zipkin_reporter_methods:flush()
    if self.pending_spans_n == 0 then
        return true
    end

    if self.http_host == nil then
        return true
    end

    local pending_spans = json.encode(self.pending_spans)
    if not pending_spans then
        -- Log.e("pending_spans encode failed", self.pending_spans)
        return false
    end

    self.pending_spans = {}
    self.pending_spans_n = 0

    local recvheader = {}
    local status, body = httpc.request("POST", self.http_host, self.http_uri, recvheader, header, pending_spans)
    -- Log.d("zipkin_reporter_methods:flush", pending_spans, status, body)
    if status < 200 or status >= 300 then
        return false, "invalid response code: " .. status .. " body: " .. body
    end

    return true
end

return {
    new = new,
}
