-- factoryos.services.telemetry — metric aggregation + latest-value cache.
-- Metric shape: { n=name, v=value, u=unit, q=quality, ts=ms }
-- quality ∈ "measured" | "derived" | "estimated" | "unavailable"

local util  = require("factoryos.util")
local store = require("factoryos.core.store")
local sched = require("factoryos.core.sched")

local M = {}

function M.attach(node, opts)
  opts = opts or {}
  local historyLen = opts.historyLen or 120
  local persistEvery = opts.persistEvery or 120

  local T = {
    latest  = {},   -- nodeId -> metricName -> metric
    history = {},   -- nodeId -> metricName -> {metric,...} ring
  }

  local function ingest(nodeId, metrics)
    if type(metrics) ~= "table" then return end
    local L = T.latest[nodeId] or {}
    local H = T.history[nodeId] or {}
    T.latest[nodeId], T.history[nodeId] = L, H
    for _, m in ipairs(metrics) do
      if type(m) == "table" and m.n then
        m.ts = m.ts or util.now()
        L[m.n] = m
        local ring = H[m.n] or {}
        ring[#ring + 1] = m
        if #ring > historyLen then table.remove(ring, 1) end
        H[m.n] = ring
      end
    end
    node:_emit_local("telemetry.updated", { node = nodeId, metrics = metrics })
  end

  node:service("core.telemetry", {
    ops = {
      push = function(env) ingest(env.from, env.p and env.p.metrics) end,

      -- p = { node?, name? } → filtered latest metrics
      query = function(_, p)
        p = p or {}
        local out = {}
        for nid, metrics in pairs(T.latest) do
          if not p.node or p.node == nid then
            for name, m in pairs(metrics) do
              if not p.name or p.name == name then
                out[#out + 1] = m; out[#out].node = nid
              end
            end
          end
        end
        return { ok = true, metrics = out }
      end,

      -- p = { node, name } → ring history
      history = function(_, p)
        local h = p and T.history[p.node] and T.history[p.node][p.name]
        return { ok = true, metrics = h or {} }
      end,
    },
  })

  node:spawn(function()
    while true do
      sched.sleep(persistEvery)
      store.save("telemetry", T.latest)
    end
  end, "telemetry.persist")

  return T
end

return M
