-- Renders the registry reference from Schemas.REGISTRIES so the page cannot
-- drift from the engine.  Run from the repo root:
--   luajit tools/gen_registry_docs.lua            -- in-repo, the default
--   luajit tools/gen_registry_docs.lua ../gen1recomp.wiki
--   POKEPORT_DOCS_DIR=../project.wiki luajit tools/gen_registry_docs.lua
-- The full doc pipeline moves into the modkit CLI later; this is the
-- Schemas -> markdown seed it will absorb.
package.path = "./?.lua;./?/init.lua;" .. package.path

local Schemas = require("src.mods.Schemas")

-- No argument writes INSIDE the repo (a default landing in a sibling dir
-- creates one on the first run); `modkit docs` reads this exact path
local DEFAULT_DIR = "docs/modding/reference"
local DEFAULT_FILE = "registries.md"
-- an explicit target is the GitHub wiki checkout, whose page names are flat
local WIKI_FILE = "Reference-Registries.md"

-- precedence: argv, env, in-repo default -- so a wiki checkout is one flag
-- away and a CI job can set it once for every generator on this convention
local outDir = (... or nil)
if outDir == nil or outDir == "" then outDir = os.getenv("POKEPORT_DOCS_DIR") end
local explicit = outDir ~= nil and outDir ~= ""
if not explicit then outDir = DEFAULT_DIR end
outDir = outDir:gsub("/+$", "")

local OUT = outDir .. "/" .. (explicit and WIKI_FILE or DEFAULT_FILE)

local names = {}
for name in pairs(Schemas.REGISTRIES) do names[#names + 1] = name end
table.sort(names)

local out = {}
local function line(fmt, ...)
  if select("#", ...) > 0 then
    out[#out + 1] = fmt:format(...)
  else
    out[#out + 1] = fmt
  end
end

line("<!-- Generated by tools/gen_registry_docs.lua from src/mods/Schemas.lua.")
line("     Do not edit by hand; regenerate after any schema change. -->")
line("")
line("# Registry reference")
line("")
line("One section per registry: merge semantics, the `Data` table the merge")
line("writes, and the value schema. Where Gold differs -- a different table, a")
line("different record, or no home at all -- the registry carries a Gen 2")
line("subsection built from the same catalog entry. Concepts and verbs:")
line("[Concepts: Registries](Concepts-Registries).")

-- the value schema of one spec, whichever generation's shape it carries.
-- Schemas.check reads keys/keyValue, then value, then fields in that order,
-- so this renders them in the same order or the page would describe a branch
-- that never runs.
local function renderSchema(spec)
  if spec.keys then
    line("")
    line("Id = a top-level key of the target table. Keys not listed here are")
    line("accepted and merged as-is.")
    line("")
    line("| key | type |")
    line("|---|---|")
    local keyNames = {}
    for keyName in pairs(spec.keys) do keyNames[#keyNames + 1] = keyName end
    table.sort(keyNames)
    for _, keyName in ipairs(keyNames) do
      line("| `%s` | %s |", keyName, spec.keys[keyName].desc)
    end
  elseif spec.fields then
    line("")
    line("| field | type | required |")
    line("|---|---|---|")
    local fieldNames = {}
    for fieldName in pairs(spec.fields) do fieldNames[#fieldNames + 1] = fieldName end
    table.sort(fieldNames)
    for _, fieldName in ipairs(fieldNames) do
      local ft = spec.fields[fieldName]
      line("| `%s` | %s | %s |", fieldName, ft.desc,
        ft.kind == "opt" and "no" or "yes")
    end
  elseif spec.keyValue then
    line("")
    line("Id = a top-level key of the target table; every key carries the same")
    line("shape.")
    line("")
    line("- value: %s", spec.keyValue.desc)
  elseif spec.value then
    line("- value: %s", spec.value.desc)
  end
end

-- prose a registry needs beyond its schema (resolution order, the guarantees
-- a value carries).  It lives on the spec rather than in the page because
-- this file is regenerated: hand-written paragraphs in the output are deleted
-- by the next run.
local function renderExample(example, notes)
  if example then
    line("")
    line("```lua")
    line("%s", example)
    line("```")
  end
  if notes then
    line("")
    line("%s", notes)
  end
end

for _, name in ipairs(names) do
  local spec = Schemas.REGISTRIES[name]
  line("")
  line("## %s", name)
  line("")
  line("- semantics: `%s`", spec.semantics)
  line("- target: %s", spec.target and ("`Data." .. spec.target .. "`") or "none")
  if spec.deprecated then
    line("- **deprecated** -- use %s", spec.deprecated.useInstead)
  end
  -- The mirror of the Gen 2 gating below.  Six registries exist because GOLD
  -- does (the phone book, the decorations, the radio dial), so they carry no
  -- Gen 1 target and "- target: none" on its own reads as a broken entry
  -- rather than as the deliberate one-generation registry it is.
  if Schemas.gatedFor(name, 1) then
    line("")
    line("Gen 2 only: Red, Blue and Yellow have no such system, so there is no")
    line("Gen 1 table to merge into and a write here on a Gen 1 boot is dropped")
    line("and reported. See the Gold subsection below for where it does land.")
  end
  renderSchema(spec)
  renderExample(spec.example, spec.notes)

  -- Gold.  The registry NAME and the verbs are shared across generations, so
  -- what a Gen 2 subsection says is only ever WHERE the merge lands and what
  -- a record there looks like -- or that there is nowhere to land, which is a
  -- write the loader drops and reports rather than a name a mod may not use.
  local gen2Target = Schemas.targetFor(name, spec, 2)
  local shaped = Schemas.hasGen2Shape(spec)
  if Schemas.gatedFor(name, 2) then
    line("")
    line("### On Gold (Gen 2)")
    line("")
    line("No Gen 2 home: Gold reimplements this system without reading the")
    line("registry, so a write here is dropped and reported on a Gold boot.")
    line("`docs/mod-api-gen2-compat.md` in the engine repo lists what is left")
    line("to do for each one.")
  elseif gen2Target ~= spec.target or shaped then
    local gen2 = Schemas.shapeFor(name, spec, 2)
    line("")
    line("### On Gold (Gen 2)")
    line("")
    line("- semantics: `%s`", gen2.semantics)
    line("- target: `Data.%s`", gen2Target)
    if shaped then
      line("")
      line("The record differs; the registry name, the verbs and the id space")
      line("do not.")
      renderSchema(gen2)
      renderExample(spec.gen2Example, spec.gen2Notes)
    else
      renderExample(spec.gen2Example, spec.gen2Notes)
    end
  end
end

line("")
line("## v1 aliases")
line("")
line("| alias | canonical |")
line("|---|---|")
local aliases = {}
for alias in pairs(Schemas.ALIASES) do aliases[#aliases + 1] = alias end
table.sort(aliases)
for _, alias in ipairs(aliases) do
  line("| `%s` | `%s` |", alias, Schemas.ALIASES[alias])
end

-- a wiki checkout may not have the directory yet; create it before the
-- open so pointing at a fresh clone is not a two-step
local file = io.open(OUT, "w")
if not file then
  os.execute('mkdir -p "' .. outDir:gsub('"', '\\"') .. '"')
  file = assert(io.open(OUT, "w"),
    "cannot write " .. OUT .. " (is the output directory reachable?)")
end
file:write(table.concat(out, "\n") .. "\n")
file:close()
print("wrote " .. OUT)
