const { query } = require('../../utils/db')

// Full page row + joined category fields.
const PAGE_COLS =
  'p.id, p.slug, p.title, p.body, p.excerpt, p.category_id, p.published, p.sort_order, ' +
  'p.updated_by, p.created_at, p.updated_at, p.published_at, ' +
  'c.slug AS category_slug, c.title AS category_title'

// List rows omit the body (lighter payload for indexes/tables).
const SUMMARY_COLS =
  'p.id, p.slug, p.title, p.excerpt, p.category_id, p.published, p.sort_order, ' +
  'p.updated_at, p.published_at, c.slug AS category_slug, c.title AS category_title'

const FROM = 'FROM wiki_pages p LEFT JOIN wiki_categories c ON c.id = p.category_id'
const ORDER = 'ORDER BY p.sort_order ASC, p.title ASC'

// Shared summary query builder with optional category / tag / status filters.
function buildSummaryQuery({ categoryId = null, tagId = null, published = null }) {
  const joins = []
  const where = []
  const params = []
  if (tagId != null) {
    joins.push('JOIN wiki_page_tags pt ON pt.page_id = p.id AND pt.tag_id = ?')
    params.push(tagId)
  }
  if (categoryId != null) {
    where.push('p.category_id = ?')
    params.push(categoryId)
  }
  if (published != null) {
    where.push('p.published = ?')
    params.push(published ? 1 : 0)
  }
  const clause = where.length ? `WHERE ${where.join(' AND ')}` : ''
  return {
    sql: `SELECT ${SUMMARY_COLS} ${FROM} ${joins.join(' ')} ${clause} ${ORDER}`,
    params,
  }
}

// Published summaries (public). Optional category / tag filters.
async function listPublishedSummaries({ categoryId = null, tagId = null } = {}) {
  const { sql, params } = buildSummaryQuery({ categoryId, tagId, published: true })
  return query(sql, params)
}

// All summaries (admin), with optional category / tag / status filters.
async function listAllSummaries({ categoryId = null, tagId = null, published = null } = {}) {
  const { sql, params } = buildSummaryQuery({ categoryId, tagId, published })
  return query(sql, params)
}

async function findBySlug(slug) {
  const rows = await query(`SELECT ${PAGE_COLS} ${FROM} WHERE p.slug = ? LIMIT 1`, [slug])
  return rows[0] || null
}

async function findPublishedBySlug(slug) {
  const rows = await query(
    `SELECT ${PAGE_COLS} ${FROM} WHERE p.slug = ? AND p.published = 1 LIMIT 1`,
    [slug],
  )
  return rows[0] || null
}

// Full-text search over title + body, ordered by relevance.
async function searchSummaries(q, { publishedOnly = true } = {}) {
  const pub = publishedOnly ? 'AND p.published = 1' : ''
  return query(
    `SELECT ${SUMMARY_COLS} ${FROM}
     WHERE MATCH(p.title, p.body) AGAINST (? IN NATURAL LANGUAGE MODE) ${pub}
     ORDER BY MATCH(p.title, p.body) AGAINST (?) DESC, p.title ASC`,
    [q, q],
  )
}

// ── Page writes ────────────────────────────────────────────────────────
async function insert({
  slug,
  title,
  body = null,
  excerpt = null,
  categoryId = null,
  published = true,
  sortOrder = 0,
  updatedBy = null,
}) {
  const res = await query(
    'INSERT INTO wiki_pages (slug, title, body, excerpt, category_id, published, sort_order, published_at, updated_by) ' +
      'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
    [
      slug,
      title,
      body,
      excerpt,
      categoryId,
      published ? 1 : 0,
      sortOrder,
      published ? new Date() : null,
      updatedBy,
    ],
  )
  return res.insertId
}

// Dynamic update — only the provided columns are written.
async function updateBySlug(slug, fields) {
  const cols = []
  const params = []
  for (const [key, val] of Object.entries(fields)) {
    cols.push(`${key} = ?`)
    params.push(val)
  }
  if (cols.length === 0) return
  params.push(slug)
  await query(`UPDATE wiki_pages SET ${cols.join(', ')} WHERE slug = ?`, params)
}

async function deleteBySlug(slug) {
  return query('DELETE FROM wiki_pages WHERE slug = ?', [slug])
}

// ── Categories ─────────────────────────────────────────────────────────
const CAT_COLS = 'id, slug, title, description, sort_order, created_at, updated_at'

// Categories with page counts (total + published) for index/admin views.
async function listCategories() {
  return query(
    `SELECT c.id, c.slug, c.title, c.description, c.sort_order, c.created_at, c.updated_at,
       (SELECT COUNT(*) FROM wiki_pages p WHERE p.category_id = c.id) AS page_count,
       (SELECT COUNT(*) FROM wiki_pages p WHERE p.category_id = c.id AND p.published = 1) AS published_count
     FROM wiki_categories c
     ORDER BY c.sort_order ASC, c.title ASC`,
  )
}

async function findCategoryBySlug(slug) {
  const rows = await query(`SELECT ${CAT_COLS} FROM wiki_categories WHERE slug = ? LIMIT 1`, [slug])
  return rows[0] || null
}

async function findCategoryById(id) {
  const rows = await query(`SELECT ${CAT_COLS} FROM wiki_categories WHERE id = ? LIMIT 1`, [id])
  return rows[0] || null
}

async function insertCategory({ slug, title, description = null, sortOrder = 0 }) {
  const res = await query(
    'INSERT INTO wiki_categories (slug, title, description, sort_order) VALUES (?, ?, ?, ?)',
    [slug, title, description, sortOrder],
  )
  return res.insertId
}

async function updateCategory(id, fields) {
  const cols = []
  const params = []
  for (const [key, val] of Object.entries(fields)) {
    cols.push(`${key} = ?`)
    params.push(val)
  }
  if (cols.length === 0) return
  params.push(id)
  await query(`UPDATE wiki_categories SET ${cols.join(', ')} WHERE id = ?`, params)
}

// Detach pages first (works even on upgraded DBs that lack the FK), then delete.
async function deleteCategory(id) {
  await query('UPDATE wiki_pages SET category_id = NULL WHERE category_id = ?', [id])
  return query('DELETE FROM wiki_categories WHERE id = ?', [id])
}

// ── Tags ───────────────────────────────────────────────────────────────
async function listTags() {
  return query(
    `SELECT t.id, t.slug, t.label,
       (SELECT COUNT(*) FROM wiki_page_tags pt
          JOIN wiki_pages p ON p.id = pt.page_id
          WHERE pt.tag_id = t.id AND p.published = 1) AS published_count
     FROM wiki_tags t ORDER BY t.label ASC`,
  )
}

async function findTagBySlug(slug) {
  const rows = await query('SELECT id, slug, label FROM wiki_tags WHERE slug = ? LIMIT 1', [slug])
  return rows[0] || null
}

async function getTagsForPage(pageId) {
  return query(
    'SELECT t.slug, t.label FROM wiki_tags t ' +
      'JOIN wiki_page_tags pt ON pt.tag_id = t.id WHERE pt.page_id = ? ORDER BY t.label ASC',
    [pageId],
  )
}

async function upsertTag(slug, label) {
  await query('INSERT INTO wiki_tags (slug, label) VALUES (?, ?) ON DUPLICATE KEY UPDATE label = VALUES(label)', [
    slug,
    label,
  ])
  const rows = await query('SELECT id FROM wiki_tags WHERE slug = ? LIMIT 1', [slug])
  return rows[0].id
}

async function setPageTags(pageId, tagIds) {
  await query('DELETE FROM wiki_page_tags WHERE page_id = ?', [pageId])
  for (const tagId of tagIds) {
    await query('INSERT IGNORE INTO wiki_page_tags (page_id, tag_id) VALUES (?, ?)', [pageId, tagId])
  }
}

// Drop tags no longer attached to any page (keeps the tag list tidy).
async function deleteOrphanTags() {
  return query('DELETE FROM wiki_tags WHERE id NOT IN (SELECT tag_id FROM wiki_page_tags)')
}

// ── Internal links / backlinks ─────────────────────────────────────────
async function clearLinks(pageId) {
  return query('DELETE FROM wiki_links WHERE source_page_id = ?', [pageId])
}

async function insertLink(pageId, targetSlug) {
  return query('INSERT INTO wiki_links (source_page_id, target_slug) VALUES (?, ?)', [pageId, targetSlug])
}

// Pages that link TO targetSlug (excludes the page linking to itself).
async function getBacklinks(targetSlug, { publishedOnly = true } = {}) {
  const pub = publishedOnly ? 'AND p.published = 1' : ''
  return query(
    `SELECT DISTINCT p.slug, p.title FROM wiki_links l
       JOIN wiki_pages p ON p.id = l.source_page_id
     WHERE l.target_slug = ? AND p.slug <> ? ${pub}
     ORDER BY p.title ASC`,
    [targetSlug, targetSlug],
  )
}

// Of the given slugs, which actually exist (for red-link detection).
async function getExistingSlugs(slugs) {
  if (!slugs || slugs.length === 0) return new Set()
  const placeholders = slugs.map(() => '?').join(',')
  const rows = await query(`SELECT slug FROM wiki_pages WHERE slug IN (${placeholders})`, slugs)
  return new Set(rows.map((r) => r.slug))
}

// ── Revisions ──────────────────────────────────────────────────────────
async function insertRevision({ pageId, title, body, excerpt, categoryId, editorId, changeNote }) {
  return query(
    'INSERT INTO wiki_revisions (page_id, title, body, excerpt, category_id, editor_id, change_note) ' +
      'VALUES (?, ?, ?, ?, ?, ?, ?)',
    [pageId, title, body || null, excerpt || null, categoryId ?? null, editorId ?? null, changeNote || null],
  )
}

async function listRevisions(pageId) {
  return query(
    `SELECT r.id, r.change_note, r.created_at, r.editor_id, u.username AS editor
     FROM wiki_revisions r LEFT JOIN users u ON u.id = r.editor_id
     WHERE r.page_id = ? ORDER BY r.id DESC`,
    [pageId],
  )
}

async function findRevision(id) {
  const rows = await query('SELECT * FROM wiki_revisions WHERE id = ? LIMIT 1', [id])
  return rows[0] || null
}

// ── Seeding (idempotent) ───────────────────────────────────────────────
async function seedDefault(slug, title, body) {
  await query('INSERT IGNORE INTO wiki_pages (slug, title, body) VALUES (?, ?, ?)', [
    slug,
    title,
    body || null,
  ])
}

async function seedDefaultCategory(slug, title, description, sortOrder = 0) {
  await query(
    'INSERT IGNORE INTO wiki_categories (slug, title, description, sort_order) VALUES (?, ?, ?, ?)',
    [slug, title, description || null, sortOrder],
  )
}

// Assign a seeded page to a category by slug, only if not already categorized —
// migrates pre-upgrade pages without clobbering manual changes.
async function assignCategoryBySlug(pageSlug, categorySlug) {
  await query(
    'UPDATE wiki_pages SET category_id = (SELECT id FROM wiki_categories WHERE slug = ?) ' +
      'WHERE slug = ? AND category_id IS NULL',
    [categorySlug, pageSlug],
  )
}

module.exports = {
  listPublishedSummaries,
  listAllSummaries,
  findBySlug,
  findPublishedBySlug,
  searchSummaries,
  insert,
  updateBySlug,
  deleteBySlug,
  listCategories,
  findCategoryBySlug,
  findCategoryById,
  insertCategory,
  updateCategory,
  deleteCategory,
  listTags,
  findTagBySlug,
  getTagsForPage,
  upsertTag,
  setPageTags,
  deleteOrphanTags,
  clearLinks,
  insertLink,
  getBacklinks,
  getExistingSlugs,
  insertRevision,
  listRevisions,
  findRevision,
  seedDefault,
  seedDefaultCategory,
  assignCategoryBySlug,
}
