import type { RowDataPacket } from 'mysql2' import { db, DB } from './db' export interface PostSocial { likes: number liked: boolean bookmarked: boolean note: string remindAt: number | null } /** Motivos de denuncia permitidos (clave estable; el texto se traduce en la UI). */ export const REPORT_REASONS = [ 'troll', 'threat', 'username', 'offtopic', 'inappropriate', 'spam', 'violence', 'child', 'illegal', ] as const export type ReportReason = (typeof REPORT_REASONS)[number] /** Likes/bookmarks de un conjunto de posts para el usuario actual. */ export async function getPostsSocial(postIds: number[], accountId: number): Promise> { const map = new Map() const ids = [...new Set(postIds.filter((n) => Number.isFinite(n) && n > 0))] for (const id of ids) map.set(id, { likes: 0, liked: false, bookmarked: false, note: '', remindAt: null }) if (ids.length === 0) return map const ph = ids.map(() => '?').join(',') try { const [likes] = await db(DB.web).query( `SELECT post_id, COUNT(*) AS c FROM forum_post_like WHERE post_id IN (${ph}) GROUP BY post_id`, ids, ) for (const r of likes) { const e = map.get(Number(r.post_id)) if (e) e.likes = Number(r.c) } if (accountId) { const [liked] = await db(DB.web).query( `SELECT post_id FROM forum_post_like WHERE account = ? AND post_id IN (${ph})`, [accountId, ...ids], ) for (const r of liked) { const e = map.get(Number(r.post_id)) if (e) e.liked = true } const [bm] = await db(DB.web).query( `SELECT post_id, note, remind_at FROM forum_post_bookmark WHERE account = ? AND post_id IN (${ph})`, [accountId, ...ids], ) for (const r of bm) { const e = map.get(Number(r.post_id)) if (e) { e.bookmarked = true e.note = String(r.note || '') e.remindAt = r.remind_at != null ? Number(r.remind_at) : null } } } } catch { /* tablas ausentes */ } return map } /** Alterna el me gusta (al volver a pulsar se retira). Devuelve estado + total. */ export async function togglePostLike(accountId: number, postId: number): Promise<{ liked: boolean; likes: number }> { if (!accountId || !postId) return { liked: false, likes: 0 } try { const [ex] = await db(DB.web).query( 'SELECT 1 FROM forum_post_like WHERE post_id = ? AND account = ? LIMIT 1', [postId, accountId], ) if (ex.length) { await db(DB.web).query('DELETE FROM forum_post_like WHERE post_id = ? AND account = ?', [postId, accountId]) } else { await db(DB.web).query('INSERT IGNORE INTO forum_post_like (post_id, account) VALUES (?, ?)', [postId, accountId]) } const [[c]] = (await db(DB.web).query( 'SELECT COUNT(*) AS c FROM forum_post_like WHERE post_id = ?', [postId], )) as unknown as [RowDataPacket[]] return { liked: !ex.length, likes: Number(c?.c ?? 0) } } catch { return { liked: false, likes: 0 } } } /** Alterna guardar en marcadores. */ export async function togglePostBookmark(accountId: number, postId: number): Promise<{ bookmarked: boolean }> { if (!accountId || !postId) return { bookmarked: false } try { const [ex] = await db(DB.web).query( 'SELECT 1 FROM forum_post_bookmark WHERE post_id = ? AND account = ? LIMIT 1', [postId, accountId], ) if (ex.length) { await db(DB.web).query('DELETE FROM forum_post_bookmark WHERE post_id = ? AND account = ?', [postId, accountId]) return { bookmarked: false } } await db(DB.web).query('INSERT IGNORE INTO forum_post_bookmark (post_id, account) VALUES (?, ?)', [postId, accountId]) return { bookmarked: true } } catch { return { bookmarked: false } } } /** Detalle del marcador (nota + recordatorio) de un post para el usuario. */ export async function getBookmarkDetail( accountId: number, postId: number, ): Promise<{ note: string; remindAt: number | null } | null> { if (!accountId || !postId) return null try { const [rows] = await db(DB.web).query( 'SELECT note, remind_at FROM forum_post_bookmark WHERE post_id = ? AND account = ?', [postId, accountId], ) if (!rows[0]) return null return { note: String(rows[0].note || ''), remindAt: rows[0].remind_at != null ? Number(rows[0].remind_at) : null } } catch { return null } } /** Crea/actualiza el marcador con nota y recordatorio (upsert). */ export async function updateBookmark( accountId: number, postId: number, note: string, remindAt: number | null, ): Promise { if (!accountId || !postId) return false try { await db(DB.web).query( `INSERT INTO forum_post_bookmark (post_id, account, note, remind_at) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE note = VALUES(note), remind_at = VALUES(remind_at)`, [postId, accountId, (note || '').slice(0, 255), remindAt && remindAt > 0 ? remindAt : null], ) return true } catch { return false } } /** Registra una denuncia de un post. */ export async function reportPost( accountId: number, postId: number, reason: string, detail: string, nowUnix: number, ): Promise { if (!accountId || !postId) return false if (!(REPORT_REASONS as readonly string[]).includes(reason)) return false try { await db(DB.web).query( 'INSERT INTO forum_post_report (post_id, account, reason, detail, created) VALUES (?, ?, ?, ?, ?)', [postId, accountId, reason, (detail || '').slice(0, 500), nowUnix], ) return true } catch { return false } }