// Public · Atlas — the spawn atlas / bestiary. Static shard CONTENT derived from
// the shard's own ServUO tree, not live shard state.
//
// Mounted at /api/v1/public/atlas by public/index.js. Two deliberate differences
// from the /public/shard routes next door (docs/link/v3.md §6):
//
//   • **Not under /shard.** Nothing here round-trips the sidecar, and the pages
//     stay fully populated while the shard is down. Mounting it under /shard
//     would imply a dependency it does not have.
//   • **siteMode-gated, like /posts and /wiki.** The shard routes are exempt
//     because shard status is wanted *during* maintenance; a bestiary is site
//     content and follows site content's rules.
//
// Every route also carries `requireFeature('atlas')` — 404 when an admin has
// disabled the feature, 403 when the caller sits below its configured audience.
// The default audience is `anonymous`, so these gates are inert until an admin
// changes something.

const express = require('express')
const { param, query } = require('express-validator')

const atlas = require('./atlas.controller')
const siteMode = require('../../../middleware/siteMode')
const validate = require('../../../middleware/validate')
const { requireFeature } = require('../../../utils/shardVisibility')

const atlasRouter = express.Router()

// Facet names come from the shard's own files and are never validated against a
// list — nothing in the codebase names a facet (§6.1 R2). Only the length is
// bounded, and the query matches exactly, so an unknown name returns an empty
// result rather than an error.
const facetParam = query('facet').optional({ values: 'falsy' }).isString().isLength({ max: 40 })

atlasRouter.get(
  '/creatures',
  requireFeature('atlas'),
  // #swagger.tags = ['Public · Atlas']
  // #swagger.summary = 'Search the bestiary (paginated)'
  // #swagger.description = 'Every creature the shard spawns, most numerous first. `total` is how many can be alive at once across all spawners; `points` is how many spawners mention it; `facets` maps facet name to that creature\'s share on it. Static content parsed from the shard\'s ServUO tree — unaffected by the shard being offline.'
  // #swagger.parameters['q'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Substring match on the creature name (max 60 chars).' }
  // #swagger.parameters['facet'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Limit to creatures spawning on this facet. Facet names come from the shard\'s own files; an unknown one returns an empty page.' }
  // #swagger.parameters['limit'] = { in: 'query', required: false, schema: { type: 'integer' }, description: 'Page size, 1..100 (default 50).' }
  // #swagger.parameters['offset'] = { in: 'query', required: false, schema: { type: 'integer' }, description: 'Rows to skip (default 0).' }
  /* #swagger.responses[200] = { description: 'A page of creatures plus the unpaginated total', content: { "application/json": { schema: { $ref: "#/components/schemas/AtlasCreaturePage" } } } } */
  /* #swagger.responses[403] = { description: 'The atlas feature is gated above this caller', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
  /* #swagger.responses[404] = { description: 'The atlas feature is disabled', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
  query('q').optional({ values: 'falsy' }).isString().isLength({ max: 60 }),
  facetParam,
  query('limit').optional().isInt({ min: 1, max: 100 }),
  query('offset').optional().isInt({ min: 0, max: 100000 }),
  validate,
  siteMode,
  atlas.getCreatures,
)
atlasRouter.get(
  '/creatures/:slug',
  requireFeature('atlas'),
  // #swagger.tags = ['Public · Atlas']
  // #swagger.summary = 'One creature: where it spawns, and what spawns with it'
  // #swagger.description = 'The answer the atlas exists to give. `places` is the aggregate — "lizardman → Shrines, Isamu-Jima, Yew" — resolved by point-in-rect against the shard\'s own region rectangles, falling back to the nearest landmark, else "Wilderness". `spawners` lists the individual spawn points (bounded; `spawnersTruncated` says when the list was cut), and `alsoHere` is what shares those spawners.'
  // #swagger.parameters['slug'] = { in: 'path', required: true, schema: { type: 'string' }, description: 'Creature slug, e.g. lizardman.' }
  // #swagger.parameters['facet'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Restrict places and spawners to one facet.' }
  // #swagger.parameters['points'] = { in: 'query', required: false, schema: { type: 'integer' }, description: 'Max spawners to return, 1..1000 (default 200).' }
  /* #swagger.responses[200] = { description: 'The creature', content: { "application/json": { schema: { $ref: "#/components/schemas/AtlasCreature" } } } } */
  /* #swagger.responses[404] = { description: 'No such creature in this atlas (or the feature is disabled)', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
  param('slug').isString().isLength({ min: 1, max: 120 }),
  facetParam,
  query('points').optional().isInt({ min: 1, max: 1000 }),
  validate,
  siteMode,
  atlas.getCreature,
)
atlasRouter.get(
  '/regions',
  requireFeature('atlas'),
  // #swagger.tags = ['Public · Atlas']
  // #swagger.summary = 'Named regions and their rectangles'
  // #swagger.description = 'Flattened out of the shard\'s nested Regions.xml. `priority` and the rectangles are what placed each spawn point, kept so the placement can be re-derived rather than taken on trust.'
  // #swagger.parameters['facet'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Limit to one facet.' }
  // #swagger.parameters['q'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Substring match on the region name.' }
  /* #swagger.responses[200] = { description: 'Regions, by facet then name', content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/AtlasRegion" } } } } } */
  facetParam,
  query('q').optional({ values: 'falsy' }).isString().isLength({ max: 60 }),
  validate,
  siteMode,
  atlas.getRegions,
)
atlasRouter.get(
  '/landmarks',
  requireFeature('atlas'),
  // #swagger.tags = ['Public · Atlas']
  // #swagger.summary = 'Points of interest (dungeon levels, town markers)'
  // #swagger.description = 'From the shard\'s Data/Locations files. `group` is the innermost enclosing parent ("Covetous"), which is the label worth showing over the individual marker ("Level 1").'
  // #swagger.parameters['facet'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Limit to one facet.' }
  // #swagger.parameters['q'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Substring match on the landmark name or its group.' }
  /* #swagger.responses[200] = { description: 'Landmarks, by facet then group', content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/AtlasLandmark" } } } } } */
  facetParam,
  query('q').optional({ values: 'falsy' }).isString().isLength({ max: 60 }),
  validate,
  siteMode,
  atlas.getLandmarks,
)
atlasRouter.get(
  '/champions',
  requireFeature('atlas'),
  // #swagger.tags = ['Public · Atlas']
  // #swagger.summary = 'Configured champion altars (the roster, not the live board)'
  // #swagger.description = 'Where the altars are and what each one summons — "there is an Unholy Terror altar in Deceit". `randomType` marks altars whose champion is drawn at activation. Do not conflate this with GET /public/shard/champs, which is the live sidecar-fed board ("it is on level 3 right now").'
  // #swagger.parameters['facet'] = { in: 'query', required: false, schema: { type: 'string' }, description: 'Limit to one facet.' }
  /* #swagger.responses[200] = { description: 'Altars, by facet then name', content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/AtlasChampion" } } } } } */
  facetParam,
  validate,
  siteMode,
  atlas.getChampions,
)
atlasRouter.get(
  '/meta',
  requireFeature('atlas'),
  // #swagger.tags = ['Public · Atlas']
  // #swagger.summary = 'What atlas is loaded: facets, counts, when it was imported'
  // #swagger.description = 'Drives the facet filter and the "parsed from the shard\'s own files on <date>" line. Reports the game world only — the ServUO path, the per-file hashes and any pending refresh are operator detail and live on the admin status route.'
  /* #swagger.responses[200] = { description: 'Atlas metadata', content: { "application/json": { schema: { $ref: "#/components/schemas/AtlasMeta" } } } } */
  siteMode,
  atlas.getMeta,
)

module.exports = atlasRouter
