import type { RowDataPacket } from 'mysql2'
import { db, DB } from './db'

export interface ForumInfo {
  id: number
  category_id: number
  name: string
  description: string
  icon: string | null
  topic_count: number
  post_count: number
  last_topic_id: number | null
  last_topic_name: string | null
  last_topic_poster: string | null
}

export interface Category {
  id: number
  name: string
  forums: ForumInfo[]
}

export interface Topic {
  id: number
  name: string
  poster: string
  created: string | null
  sticky: boolean
  locked: boolean
  views: number
}

export interface Post {
  id: number
  poster: string
  poster_id: number | null
  text: string
  time: string | null
  edited: boolean
  deleted: boolean
}

export interface TopicFull {
  id: number
  name: string
  forum_id: number
  poster_id: number | null
  locked: boolean
  sticky: boolean
  deleted: boolean
}

export interface SearchResult {
  id: number
  name: string
  poster: string
  forum_id: number
  forum_name: string
  updated_at: string | null
}

/** Índice del foro: categorías con sus foros visibles (+ contadores y último tema). */
export async function getForumIndex(locale = 'es'): Promise<Category[]> {
  const en = locale === 'en'
  try {
    const [cats] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT id, name, name_en FROM forum_categories ORDER BY `order`, id',
    )
    const [forums] = await db(DB.web).query<RowDataPacket[]>(
      `SELECT f.id, f.category_id, f.name, f.name_en, f.description, f.description_en, f.icon,
              (SELECT COUNT(*) FROM forum_topics t WHERE t.forum_id = f.id AND t.deleted = 0) AS topic_count,
              (SELECT COUNT(*) FROM forum_posts p JOIN forum_topics t ON p.topic_id = t.id
                 WHERE t.forum_id = f.id AND p.deleted = 0 AND t.deleted = 0) AS post_count,
              (SELECT t2.id FROM forum_topics t2 WHERE t2.forum_id = f.id AND t2.deleted = 0
                 ORDER BY t2.updated_at DESC, t2.id DESC LIMIT 1) AS last_topic_id,
              (SELECT t3.name FROM forum_topics t3 WHERE t3.forum_id = f.id AND t3.deleted = 0
                 ORDER BY t3.updated_at DESC, t3.id DESC LIMIT 1) AS last_topic_name,
              (SELECT t4.poster FROM forum_topics t4 WHERE t4.forum_id = f.id AND t4.deleted = 0
                 ORDER BY t4.updated_at DESC, t4.id DESC LIMIT 1) AS last_topic_poster
       FROM forums f
       WHERE f.visibility = 1 AND f.deleted = 0
       ORDER BY f.\`order\`, f.id`,
    )
    const byCat = new Map<number, ForumInfo[]>()
    for (const f of forums) {
      const info = f as unknown as ForumInfo & { name_en?: string | null; description_en?: string | null }
      info.name = en && info.name_en ? info.name_en : info.name
      info.description = en && info.description_en ? info.description_en : info.description
      if (!byCat.has(info.category_id)) byCat.set(info.category_id, [])
      byCat.get(info.category_id)!.push(info)
    }
    return cats
      .map((c) => ({ id: c.id, name: en && c.name_en ? c.name_en : c.name, forums: byCat.get(c.id) ?? [] }))
      .filter((c) => c.forums.length > 0)
  } catch {
    return []
  }
}

/** Lista de foros visibles para el desplegable de «mover tema». */
export async function listForumsForMove(): Promise<{ id: number; name: string }[]> {
  try {
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT id, name FROM forums WHERE visibility = 1 AND deleted = 0 ORDER BY `order`, id',
    )
    return rows.map((r) => ({ id: r.id, name: r.name }))
  } catch {
    return []
  }
}

export async function getForum(id: number, locale = 'es'): Promise<{ name: string; description: string } | null> {
  const en = locale === 'en'
  try {
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT name, name_en, description, description_en FROM forums WHERE id = ? AND visibility = 1 AND deleted = 0',
      [id],
    )
    if (!rows[0]) return null
    return {
      name: en && rows[0].name_en ? rows[0].name_en : rows[0].name,
      description: en && rows[0].description_en ? rows[0].description_en : rows[0].description,
    }
  } catch {
    return null
  }
}

export async function countTopics(forumId: number): Promise<number> {
  try {
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT COUNT(*) AS n FROM forum_topics WHERE forum_id = ? AND deleted = 0',
      [forumId],
    )
    return Number(rows[0]?.n ?? 0)
  } catch {
    return 0
  }
}

export async function getTopics(forumId: number, offset = 0, limit = 20): Promise<Topic[]> {
  try {
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT id, name, poster, created, sticky, locked, views FROM forum_topics WHERE forum_id = ? AND deleted = 0 ORDER BY sticky DESC, updated_at DESC, id DESC LIMIT ? OFFSET ?',
      [forumId, limit, offset],
    )
    return rows.map((r) => ({
      id: r.id,
      name: r.name,
      poster: r.poster,
      created: r.created ? new Date(r.created).toISOString() : null,
      sticky: r.sticky === 1,
      locked: r.locked === 1,
      views: r.views ?? 0,
    }))
  } catch {
    return []
  }
}

export async function getTopic(id: number, includeDeleted = false): Promise<TopicFull | null> {
  try {
    const where = includeDeleted ? 'id = ?' : 'id = ? AND deleted = 0'
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      `SELECT id, name, forum_id, poster_id, locked, sticky, deleted FROM forum_topics WHERE ${where}`,
      [id],
    )
    const r = rows[0]
    return r
      ? {
          id: r.id,
          name: r.name,
          forum_id: r.forum_id,
          poster_id: r.poster_id ?? null,
          locked: r.locked === 1,
          sticky: r.sticky === 1,
          deleted: r.deleted === 1,
        }
      : null
  } catch {
    return null
  }
}

export async function countPosts(topicId: number, includeDeleted = false): Promise<number> {
  try {
    const del = includeDeleted ? '' : 'AND deleted = 0'
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      `SELECT COUNT(*) AS n FROM forum_posts WHERE topic_id = ? ${del}`,
      [topicId],
    )
    return Number(rows[0]?.n ?? 0)
  } catch {
    return 0
  }
}

export async function getPosts(topicId: number, offset = 0, limit = 15, includeDeleted = false): Promise<Post[]> {
  try {
    const del = includeDeleted ? '' : 'AND deleted = 0'
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      `SELECT id, poster, poster_id, text, time, created_at, updated_at, deleted FROM forum_posts WHERE topic_id = ? ${del} ORDER BY time ASC, id ASC LIMIT ? OFFSET ?`,
      [topicId, limit, offset],
    )
    return rows.map((r) => ({
      id: r.id,
      poster: r.poster,
      poster_id: r.poster_id ?? null,
      text: r.text,
      time: r.time ? new Date(r.time).toISOString() : null,
      // editado si updated_at es posterior (más de 2s) al alta del post
      edited: Boolean(
        r.updated_at && r.created_at && new Date(r.updated_at).getTime() - new Date(r.created_at).getTime() > 2000,
      ),
      deleted: r.deleted === 1,
    }))
  } catch {
    return []
  }
}

export interface ForumProfile {
  username: string
  postCount: number
  topicCount: number
  joindate: string | null
  gmlevel: number
  status: 'admin' | 'gm' | null
  recentTopics: { id: number; name: string; forum_id: number }[]
}

/** Perfil público de un usuario del foro (contadores + antigüedad + rango). */
export async function getForumProfile(username: string): Promise<ForumProfile> {
  const profile: ForumProfile = {
    username,
    postCount: 0,
    topicCount: 0,
    joindate: null,
    gmlevel: 0,
    status: null,
    recentTopics: [],
  }
  try {
    const [pc] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT COUNT(*) AS n FROM forum_posts WHERE poster = ? AND deleted = 0',
      [username],
    )
    profile.postCount = Number(pc[0]?.n ?? 0)
    const [tc] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT COUNT(*) AS n FROM forum_topics WHERE poster = ? AND deleted = 0',
      [username],
    )
    profile.topicCount = Number(tc[0]?.n ?? 0)
    const [rt] = await db(DB.web).query<RowDataPacket[]>(
      'SELECT id, name, forum_id FROM forum_topics WHERE poster = ? AND deleted = 0 ORDER BY updated_at DESC, id DESC LIMIT 5',
      [username],
    )
    profile.recentTopics = rt.map((r) => ({ id: r.id, name: r.name, forum_id: r.forum_id }))
  } catch {
    /* tablas de foro no pobladas */
  }
  try {
    const [acc] = await db(DB.auth).query<RowDataPacket[]>(
      'SELECT id, joindate FROM account WHERE username = ?',
      [username],
    )
    if (acc[0]) {
      profile.joindate = acc[0].joindate ? new Date(acc[0].joindate).toISOString() : null
      const [ga] = await db(DB.auth).query<RowDataPacket[]>(
        'SELECT MAX(SecurityLevel) AS lvl FROM account_access WHERE AccountID = ?',
        [acc[0].id],
      )
      profile.gmlevel = ga[0]?.lvl != null ? Number(ga[0].lvl) : 0
    }
  } catch {
    /* account_access puede no existir */
  }
  if (profile.gmlevel >= 16) profile.status = 'admin'
  else if (profile.gmlevel >= 1) profile.status = 'gm'
  return profile
}

/** Nº de temas que coinciden con la búsqueda (foros visibles). */
export async function countSearchTopics(query: string): Promise<number> {
  const like = `%${query}%`
  try {
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      `SELECT COUNT(DISTINCT t.id) AS n
       FROM forum_topics t
       JOIN forums f ON f.id = t.forum_id AND f.visibility = 1 AND f.deleted = 0
       LEFT JOIN forum_posts p ON p.topic_id = t.id AND p.deleted = 0
       WHERE t.deleted = 0 AND (t.name LIKE ? OR p.text LIKE ?)`,
      [like, like],
    )
    return Number(rows[0]?.n ?? 0)
  } catch {
    return 0
  }
}

/** Busca temas por título o contenido de sus posts (solo foros visibles). */
export async function searchTopics(query: string, offset = 0, limit = 20): Promise<SearchResult[]> {
  const like = `%${query}%`
  try {
    const [rows] = await db(DB.web).query<RowDataPacket[]>(
      `SELECT DISTINCT t.id, t.name, t.poster, t.forum_id, t.updated_at, f.name AS forum_name
       FROM forum_topics t
       JOIN forums f ON f.id = t.forum_id AND f.visibility = 1 AND f.deleted = 0
       LEFT JOIN forum_posts p ON p.topic_id = t.id AND p.deleted = 0
       WHERE t.deleted = 0 AND (t.name LIKE ? OR p.text LIKE ?)
       ORDER BY t.updated_at DESC
       LIMIT ? OFFSET ?`,
      [like, like, limit, offset],
    )
    return rows.map((r) => ({
      id: r.id,
      name: r.name,
      poster: r.poster,
      forum_id: r.forum_id,
      forum_name: r.forum_name,
      updated_at: r.updated_at ? new Date(r.updated_at).toISOString() : null,
    }))
  } catch {
    return []
  }
}
