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

export interface Noticia {
  id: number
  titulo: string
  fecha: string | null
  contenido: string
  enlace: string | null
}

export interface ServerStatus {
  server_name: string | null
  address: string | null
  expansion: string
  online_characters: number
  status: 'Online' | 'Offline'
  world_status: 'Online' | 'Offline'
}

// Puerto del worldserver (AzerothCore) para el estado del reino.
const WORLD_PORT = 8085

/** Etiqueta de expansión por build del cliente (incluye WoW Classic; el nuestro = 3.4.3 = 54261). */
export function expansionFromGamebuild(gamebuild: number): string {
  if (gamebuild === 12340) return 'WotLK'
  if (gamebuild >= 44000 && gamebuild <= 54261) return 'WotLK Classic'
  if (gamebuild === 15595) return 'Cataclysm'
  if (gamebuild >= 54262 && gamebuild <= 60000) return 'Cataclysm Classic'
  return 'Unknown'
}

/**
 * Noticias localizadas. Con `locale='en'` usa `titulo_en`/`contenido_en` si
 * existen; si están vacías, cae a la versión en español (título y contenido de
 * forma independiente). El texto original (titulo/contenido) siempre es ES.
 */
export async function getNoticias(locale = 'es', limit = 50): Promise<Noticia[]> {
  const [rows] = await db(DB.default).query<RowDataPacket[]>(
    'SELECT id, titulo, titulo_en, contenido, contenido_en, fecha_publicacion, enlace FROM noticia ORDER BY fecha_publicacion DESC LIMIT ?',
    [limit],
  )
  const en = locale === 'en'
  return rows.map((r) => ({
    id: r.id,
    titulo: en && r.titulo_en ? r.titulo_en : r.titulo,
    fecha: r.fecha_publicacion ? new Date(r.fecha_publicacion).toISOString() : null,
    contenido: en && r.contenido_en ? r.contenido_en : r.contenido,
    enlace: r.enlace || null,
  }))
}

async function getOnlineCount(): Promise<number> {
  try {
    const [rows] = await db(DB.characters).query<RowDataPacket[]>(
      'SELECT COUNT(*) AS n FROM characters WHERE online = 1',
    )
    return Number(rows[0]?.n ?? 0)
  } catch {
    // La BD de personajes (AzerothCore) puede no estar poblada todavía.
    return 0
  }
}

function checkServerStatus(host: string, port: number, timeout = 1000): Promise<boolean> {
  return new Promise((resolve) => {
    if (!host || host === 'N/A') return resolve(false)
    const socket = new net.Socket()
    const done = (ok: boolean) => {
      socket.destroy()
      resolve(ok)
    }
    socket.setTimeout(timeout)
    socket.once('connect', () => done(true))
    socket.once('timeout', () => done(false))
    socket.once('error', () => done(false))
    socket.connect(port, host)
  })
}

export async function getServerStatus(): Promise<ServerStatus | null> {
  const [rows] = await db(DB.default).query<RowDataPacket[]>(
    'SELECT name, address, port, gamebuild FROM serverselection LIMIT 1',
  )
  const s = rows[0]
  if (!s) return null
  const [online, up, worldUp] = await Promise.all([
    getOnlineCount(),
    checkServerStatus(s.address, s.port),
    checkServerStatus(s.address, WORLD_PORT),
  ])
  return {
    server_name: s.name,
    address: s.address,
    expansion: expansionFromGamebuild(s.gamebuild),
    online_characters: online,
    status: up ? 'Online' : 'Offline',
    world_status: worldUp ? 'Online' : 'Offline',
  }
}
