local cjson = require "cjson.safe"
local http = require "resty.http"
local kong_meta = require "kong.meta"
local x509 = require "resty.openssl.x509"
local x509_store = require "resty.openssl.x509.store"

local O11yDedicatedRoute = {PRIORITY = 900, VERSION = kong_meta.version}
local AUTH_SERVICE_URL = "http://regionalservice.observability.svc.cluster.local:8081"
local gcp_tls_store

local function load_gcp_tls_store(path)
    if gcp_tls_store then
        return gcp_tls_store
    end
    local file, err = io.open(path, "rb")
    if not file then
        return nil, err
    end
    local pem = file:read("*a")
    file:close()

    local certificate, certificate_err = x509.new(pem, "PEM")
    if not certificate then
        return nil, certificate_err
    end
    local store, store_err = x509_store.new()
    if not store then
        return nil, store_err
    end
    local _, add_err = store:add(certificate)
    if add_err then
        return nil, add_err
    end
    gcp_tls_store = store
    return store
end

local function forward_gcp_read(conf, path)
    -- Keep browser reads on the primary origin while data remains in its GCP
    -- Regional runtime. The remote Kong repeats the existing local ID lookup.
    local region_id = kong.request.get_header("X-Region-Id")
    if not conf.router_service_url or not region_id or
        not region_id:match("^gcp%-") then
        return false
    end

    local headers = {
        ["X-Region-Id"] = region_id,
        ["X-Org-Id"] = kong.request.get_header("X-Org-Id"),
        ["X-Biz-Type"] = kong.request.get_header("X-Biz-Type"),
        ["X-Project-Id"] = kong.request.get_header("X-Project-Id"),
        ["X-Application-Id"] = kong.request.get_header("X-Application-Id"),
    }
    local client = http.new()
    client:set_timeout(2000)
    local response, err = client:request_uri(conf.router_service_url, {
        method = "GET",
        headers = headers,
    })
    if not response then
        kong.log.err("GCP O11Y route lookup failed: ", err)
        return kong.response.exit(502, {message = "GCP O11Y route lookup failed"})
    end
    if response.status < 200 or response.status >= 300 then
        return kong.response.exit(response.status, {message = "GCP O11Y route lookup rejected"})
    end

    local body = cjson.decode(response.body)
    local address = body and body.data and body.data.dnsAddress
    local scheme, authority = string.match(address or "", "^(https?)://([^/]+)")
    local host, port_text = string.match(authority or "", "^([^:]+):?(%d*)$")
    if scheme ~= "https" or not host then
        return kong.response.exit(502, {message = "invalid GCP O11Y route lookup response"})
    end

    local store, store_err = load_gcp_tls_store(conf.tls_ca_path)
    if not store then
        kong.log.err("load GCP O11Y TLS trust failed: ", store_err)
        return kong.response.exit(502, {message = "GCP O11Y TLS trust is unavailable"})
    end
    local ok, verify_err = kong.service.set_tls_verify_store(store)
    if not ok then
        kong.log.err("set GCP O11Y TLS trust failed: ", verify_err)
        return kong.response.exit(502, {message = "GCP O11Y TLS verification setup failed"})
    end
    ok, verify_err = kong.service.set_tls_verify(true)
    if not ok then
        kong.log.err("enable GCP O11Y TLS verification failed: ", verify_err)
        return kong.response.exit(502, {message = "GCP O11Y TLS verification setup failed"})
    end
    kong.service.set_target(host, tonumber(port_text) or 443)
    kong.service.request.set_scheme(scheme)
    kong.service.request.set_header("Host", host)
    kong.service.request.set_path(path)
    return true
end

function O11yDedicatedRoute:access(conf)
    local path = kong.request.get_path()
    local o11y_id, suffix = path:match("^/([0-9a-fA-F%-]+)/metrics/(api/v1/.+)$")
    local target = "select"
    if not o11y_id then
        o11y_id, suffix = path:match("^/([0-9a-fA-F%-]+)/(api/v1/write)$")
        target = "insert"
    end
    if not o11y_id or #o11y_id ~= 36 then
        return kong.response.exit(404, {message = "unknown O11Y route"})
    end
    if target == "select" and forward_gcp_read(conf, path) then
        return
    end

    local auth_path = "/" .. o11y_id .. "/metrics/" .. suffix
    local client = http.new()
    client:set_timeout(2000)
    local response, err = client:request_uri(
        AUTH_SERVICE_URL .. "/api/v1/auth/?path=" ..
            ngx.escape_uri(auth_path) .. "&useO11yID=true",
        {method = "GET"}
    )
    if not response then
        kong.log.err("O11Y route lookup failed: ", err)
        return kong.response.exit(502, {message = "O11Y route lookup failed"})
    end
    if response.status < 200 or response.status >= 300 then
        return kong.response.exit(response.status, {message = "O11Y route lookup rejected"})
    end

    local body = cjson.decode(response.body)
    local route = body and body.data
    if not route or route.vmAccountID == nil or route.vmProjectID == nil then
        return kong.response.exit(502, {message = "invalid O11Y route lookup response"})
    end

    kong.service.request.set_path(
        "/" .. target .. "/" .. route.vmAccountID .. ":" .. route.vmProjectID ..
            "/prometheus/" .. suffix
    )
end

return O11yDedicatedRoute
