-- factoryos.net.envelope — FNP/1 message envelopes. See BLUEPRINT §5.
local util = require("factoryos.util")

local M = {}
M.VERSION = 1

local function base(t, from)
  return {
    v = M.VERSION,
    id = util.uuid(),
    ts = util.now(),
    t = t,
    from = from,
    to = "*",
  }
end

function M.req(from, svc, op, params, opts)
  opts = opts or {}
  local e = base("req", from)
  e.svc, e.op, e.p, e.auth = svc, op, params, opts.auth
  e.to = opts.to or "*"
  return e
end

function M.resp(from, req, result)
  local e = base("resp", from)
  e.corr = req.id
  e.to = req.from
  e.p = result or { ok = true }
  return e
end

function M.event(from, svc, op, params, opts)
  opts = opts or {}
  local e = base("event", from)
  e.svc, e.op, e.p = svc, op, params
  e.to = opts.to or "*"
  return e
end

--- Node-internal messages: announce/hello carry node descriptors in p.
function M.sys(t, from, params)
  local e = base(t, from)
  e.p = params
  return e
end

--- Serialize + size check. Returns string or nil,err.
function M.encode(env)
  local ok, s = pcall(textutils.serialize, env)
  if not ok then return nil, "serialize: " .. tostring(s) end
  if #s > 8192 then return nil, "oversize: " .. #s .. "b" end
  return s
end

--- Parse + validate wire format. Returns env or nil.
function M.decode(raw)
  if type(raw) ~= "string" then return nil end
  local ok, env = pcall(textutils.unserialize, raw)
  if not ok or type(env) ~= "table" then return nil end
  if env.v ~= M.VERSION then return nil end
  if type(env.id) ~= "string" or type(env.from) ~= "string" then return nil end
  if env.t ~= "req" and env.t ~= "resp" and env.t ~= "event"
     and env.t ~= "announce" and env.t ~= "hello" then
    return nil
  end
  return env
end

return M
