--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements.  See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License.  You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local require = require
local core = require("apisix.core")
local plugin = require("apisix.plugin")
local process = require("ngx.process")
local sw_tracer = require("skywalking.tracer")
local Span = require("skywalking.span")
local ngx = ngx
local math = math

local plugin_name = "skywalking"
local attr_schema = {
    type = "object",
    properties = {
        service_name = {
            type = "string",
            description = "service name for skywalking",
            default = "APISIX",
        },
        service_instance_name = {
            type = "string",
            description = "User Service Instance Name",
            default = "APISIX Instance Name",
        },
        endpoint_addr = {
            type = "string",
            default = "http://127.0.0.1:12800",
        },
        report_interval = {
            type = "integer",
        },
    },
}

local schema = {
    type = "object",
    properties = {
        sample_ratio = {
            type = "number",
            minimum = 0.00001,
            maximum = 1,
            default = 1
        }
    },
}


local _M = {
    version = 0.1,
    priority = 12010,
    name = plugin_name,
    schema = schema,
    attr_schema = attr_schema,
    run_policy = "prefer_route",
}


function _M.check_schema(conf)
    local check = {"endpoint_addr"}
    core.utils.check_https(check, conf, plugin_name)
    return core.schema.check(schema, conf)
end


function _M.rewrite(conf, ctx)
    core.log.debug("rewrite phase of skywalking plugin")
    ctx.skywalking_sample = false
    if conf.sample_ratio == 1 or math.random() < conf.sample_ratio then
        ctx.skywalking_sample = true
        sw_tracer:start("upstream service")
        core.log.info("tracer start")
        return
    end

    core.log.info("miss sampling, ignore")
end


function _M.delayed_body_filter(conf, ctx)
    if ctx.skywalking_sample and ngx.arg[2] then
        Span.setComponentId(ngx.ctx.exitSpan, 6002)
        Span.setComponentId(ngx.ctx.entrySpan, 6002)
        sw_tracer:finish()
        core.log.info("tracer finish")
    end
end


local started_skywalking_reporter = false
function _M.log(conf, ctx)
    if not ctx.skywalking_sample then
        return
    end

    if not started_skywalking_reporter then
        started_skywalking_reporter = true

        local sk_cli = require("skywalking.client")

        local plugin_info = _M.plugin_info
        if plugin_info.report_interval then
            sk_cli.backendTimerDelay = plugin_info.report_interval
        end
        sk_cli:startBackendTimer(plugin_info.endpoint_addr)
    end

    sw_tracer:prepareForReport()
    core.log.info("tracer prepare for report")
end


function _M.init()
    if process.type() ~= "worker" then
        return
    end
    local local_plugin_info = plugin.plugin_attr(plugin_name)
    local_plugin_info = local_plugin_info and core.table.clone(local_plugin_info) or {}
    local ok, err = core.schema.check(attr_schema, local_plugin_info)
    if not ok then
        core.log.error("failed to check the plugin_attr[", plugin_name, "]",
                       ": ", err)
        return
    end

    core.log.info("plugin attribute: ",
                  core.json.delay_encode(local_plugin_info))

    -- TODO: maybe need to fetch them from plugin-metadata
    if local_plugin_info.service_instance_name == "$hostname" then
        local_plugin_info.service_instance_name = core.utils.gethostname()
    end

    _M.plugin_info = local_plugin_info

    local metadata_shdict = ngx.shared.tracing_buffer

    metadata_shdict:set('serviceName', local_plugin_info.service_name)
    metadata_shdict:set('serviceInstanceName', local_plugin_info.service_instance_name)
end

return _M
