const posts = require('../../../model/posts/posts.model')
const wiki = require('../../../model/wiki/wiki.model')
const settings = require('../../../model/settings/settings.model')
const pages = require('../../../model/pages/pages.model')
const mailer = require('../../../utils/mailer')
const version = require('../../../config/version')
const { getUserFromRequest } = require('../../../utils/auth')
const token = require('../../../auth/token')

const log = require('../../../utils/logger')('public')

// Staff (non-player) roles may see draft pages on the public route; everyone else
// gets a 404 for a draft, indistinguishable from a missing page.
const STAFF_ROLES = ['admin', 'editor', 'moderator']
function isStaff(req) {
  const user = getUserFromRequest(req)
  return Boolean(user && STAFF_ROLES.includes(user.role))
}

async function getSettings(req, res) {
  try {
    return res.json(await settings.getPublic())
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getStatus(req, res) {
  try {
    return res.json({
      mode: (await settings.get('site_mode')) || 'live',
      status_message: (await settings.get('status_message')) || '',
      // Identity + version, so a client's first-run probe recognizes a Runic
      // Gateway backend and can run its version-mismatch guard in this one call.
      version,
    })
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// Lightweight, DB-free identity/version endpoint — the canonical target for a
// client's first-run recognition and its periodic version-mismatch guard, without
// touching settings or the database. Also serves as a cheap liveness check.
function getVersion(req, res) {
  return res.json(version)
}

async function getPosts(req, res) {
  const { category } = req.params
  if (!posts.isValidUrlCategory(category)) {
    return res.status(404).json({ message: 'Unknown category' })
  }
  try {
    return res.json(await posts.listPublished(category))
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getPost(req, res) {
  const { category, idOrSlug } = req.params
  if (!posts.isValidUrlCategory(category)) {
    return res.status(404).json({ message: 'Unknown category' })
  }
  try {
    const post = await posts.getPublished(category, idOrSlug)
    if (!post) return res.status(404).json({ message: 'Not found' })
    return res.json(post)
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getWikiCategories(req, res) {
  try {
    return res.json(await wiki.listCategories())
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getWikiTags(req, res) {
  try {
    return res.json(await wiki.listTags())
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getWikiList(req, res) {
  try {
    // Full-text search takes precedence over category/tag filters.
    const q = (req.query.q || '').trim()
    if (q) return res.json(await wiki.search(q, { publishedOnly: true }))

    const filters = {}
    if (req.query.category) {
      const category = await wiki.getCategoryBySlug(req.query.category)
      if (!category) return res.json([]) // unknown category → no pages
      filters.categoryId = category.id
    }
    if (req.query.tag) {
      const tag = await wiki.getTagBySlug(req.query.tag)
      if (!tag) return res.json([]) // unknown tag → no pages
      filters.tagId = tag.id
    }
    return res.json(await wiki.listPublished(filters))
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getWikiPage(req, res) {
  try {
    // Public sees published pages only; drafts 404 like any missing page.
    const page = await wiki.getPublishedBySlug(req.params.slug)
    if (!page) return res.status(404).json({ message: 'Not found' })
    return res.json(page)
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function getPage(req, res) {
  try {
    // Staff see drafts (live preview); the public sees published pages only.
    const page = await pages.getBySlug(req.params.slug, { includeUnpublished: isStaff(req) })
    if (!page) return res.status(404).json({ message: 'Not found' })
    return res.json(page)
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// Token-gated draft preview: renders the page's current block state regardless of
// status, for anyone holding the (short-lived, unguessable) link.
async function getPagePreview(req, res) {
  try {
    const id = Number(req.params.id)
    const decoded = token.verifyPagePreview(req.params.token)
    if (!decoded || decoded.pageId !== id) {
      return res.status(404).json({ message: 'Preview not found or expired' })
    }
    const page = await pages.getById(id)
    if (!page) return res.status(404).json({ message: 'Not found' })
    return res.json(page)
  } catch (err) {
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

async function contact(req, res) {
  const { name, email, message } = req.body
  try {
    const result = await mailer.sendContactMessage({ name, email, message })
    return res.json(result)
  } catch (err) {
    log.error('contact send failed', err)
    return res.status(502).json({ message: 'Could not send message right now.' })
  }
}

module.exports = {
  getSettings,
  getStatus,
  getVersion,
  getPosts,
  getPost,
  getWikiCategories,
  getWikiTags,
  getWikiList,
  getWikiPage,
  getPage,
  getPagePreview,
  contact,
}
