// Admin CMS pages controller. Thin HTTP layer over pages.model — it translates
// the model's PageError (status + code) into responses and records audit-log
// entries for the lifecycle events the spec calls out (create / publish /
// unpublish / delete, protect on, and the password-gated unprotect).

const pages = require('../../../model/pages/pages.model')
const users = require('../../../model/users/users.model')
const activity = require('../../../model/activity/activity.model')
const token = require('../../../auth/token')
const logger = require('../../../utils/logger')('pages')

// Map a thrown error to a response. Known PageErrors carry a status + code (and
// sometimes a block-error list); anything else is an unexpected 500.
function fail(res, err) {
  if (err && err.name === 'PageError') {
    const body = { message: err.message, code: err.code }
    if (err.errors) body.details = err.errors
    return res.status(err.status).json(body)
  }
  logger.error('unexpected pages error', { error: err.message })
  return res.status(500).json({ message: 'Internal error' })
}

async function listPages(req, res) {
  return res.json(await pages.list())
}

async function getPage(req, res) {
  const page = await pages.getById(Number(req.params.id))
  if (!page) return res.status(404).json({ message: 'Page not found', code: 'not_found' })
  return res.json(page)
}

async function createPage(req, res) {
  try {
    const page = await pages.create(req.body, req.user.id)
    await activity.log({ req, action: 'page.create', detail: { id: page.id, slug: page.slug } })
    if (page.status === 'published') {
      await activity.log({ req, action: 'page.publish', detail: { id: page.id, slug: page.slug } })
    }
    return res.status(201).json(page)
  } catch (err) {
    return fail(res, err)
  }
}

async function updatePage(req, res) {
  try {
    const id = Number(req.params.id)
    const before = await pages.getRawById(id)
    if (!before) return res.status(404).json({ message: 'Page not found', code: 'not_found' })

    const page = await pages.update(id, req.body)
    await activity.log({ req, action: 'page.update', detail: { id, slug: page.slug } })

    // Emit dedicated audit events for the transitions the spec singles out.
    if (before.status !== page.status) {
      const action = page.status === 'published' ? 'page.publish' : 'page.unpublish'
      await activity.log({ req, action, detail: { id, slug: page.slug } })
    }
    if (!before.protected && page.settings.protected) {
      await activity.log({ req, action: 'page.protect', detail: { id, slug: page.slug } })
    }
    return res.json(page)
  } catch (err) {
    return fail(res, err)
  }
}

async function deletePage(req, res) {
  try {
    const id = Number(req.params.id)
    const result = await pages.remove(id)
    await activity.log({ req, action: 'page.delete', detail: { id } })
    return res.json(result)
  } catch (err) {
    return fail(res, err)
  }
}

// Step-up auth: verify the CURRENT admin's password against their own hash
// (independent of JWT validity) before flipping protected → false. On failure:
// no mutation, standard 401, and the entered password is never logged anywhere.
async function unprotectPage(req, res) {
  try {
    const id = Number(req.params.id)
    const password = req.body?.password
    if (typeof password !== 'string' || password === '') {
      return res.status(400).json({ message: 'Password is required', code: 'password_required' })
    }
    const user = await users.getRawById(req.user.id)
    const ok = await users.validatePassword(user, password)
    if (!ok) {
      logger.warn('failed page unprotect (bad password)', { pageId: id, userId: req.user.id })
      return res.status(401).json({ message: 'Password is incorrect', code: 'bad_password' })
    }
    const page = await pages.unprotect(id)
    await activity.log({ req, action: 'page.unprotect', detail: { id, slug: page.slug } })
    return res.json(page)
  } catch (err) {
    return fail(res, err)
  }
}

// Mint a 1h preview token for the page's current (possibly unpublished) state.
// Returns the token plus the ready-to-use public preview path.
async function createPreview(req, res) {
  try {
    const id = Number(req.params.id)
    const page = await pages.getById(id)
    if (!page) return res.status(404).json({ message: 'Page not found', code: 'not_found' })
    const t = token.signPagePreview(id)
    return res.json({
      token: t,
      expiresInSeconds: 3600,
      path: `/api/v1/public/pages/${id}/preview/${t}`,
    })
  } catch (err) {
    return fail(res, err)
  }
}

module.exports = {
  listPages,
  getPage,
  createPage,
  updatePage,
  deletePage,
  unprotectPage,
  createPreview,
}
