import type { RowDataPacket, ResultSetHeader } from 'mysql2'
import { db, DB } from './db'
import { checkSecurityToken } from './security-token'
import { executeSoapCommand } from './soap'

/**
 * Renombrar hermandad: el usuario elige una hermandad de la que sea Maestro
 * (guild_member.rank = 0) y le pone un nombre nuevo. Cuesta 1000 PD y requiere
 * el token de seguridad. El renombrado se aplica por SOAP (.guild rename).
 */

export const GUILD_RENAME_PD = 1000
/** Nombre de hermandad: A-Za-z y espacio, máx. 24 (regla del juego / diseño). */
const NAME_RE = /^[A-Za-z ]{1,24}$/

export interface GmGuild {
  guildid: number
  name: string
}

/** Hermandades de las que la cuenta tiene un personaje Maestro (rank 0). */
export async function getGuildMasterGuilds(accountId: number): Promise<GmGuild[]> {
  if (!accountId) return []
  try {
    const [rows] = await db(DB.characters).query<RowDataPacket[]>(
      `SELECT g.guildid, g.name
         FROM guild g
         JOIN guild_member gm ON g.guildid = gm.guildid
         JOIN characters c ON c.guid = gm.guid
        WHERE c.account = ? AND gm.rank = 0
        ORDER BY g.name`,
      [accountId],
    )
    return rows.map((r) => ({ guildid: Number(r.guildid), name: r.name }))
  } catch {
    return []
  }
}

export interface Result {
  success: boolean
  error?: string
  cost?: number
  newName?: string
}

export async function renameGuild(params: {
  accountId: number
  guildId: number
  newName: string
  token: string
}): Promise<Result> {
  const { accountId, guildId, token } = params
  const newName = (params.newName || '').trim()

  if (!accountId || !guildId || !newName || !token) return { success: false, error: 'missingFields' }
  if (!NAME_RE.test(newName)) return { success: false, error: 'invalidName' }
  if (!(await checkSecurityToken(accountId, token))) return { success: false, error: 'invalidToken' }

  // La hermandad debe ser una de las que la cuenta dirige (Maestro).
  const guilds = await getGuildMasterGuilds(accountId)
  const guild = guilds.find((g) => g.guildid === guildId)
  if (!guild) return { success: false, error: 'notGuildMaster' }
  const oldName = guild.name
  if (/["\r\n]/.test(oldName)) return { success: false, error: 'genericError' } // nombre antiguo no seguro para SOAP

  // El nuevo nombre no puede coincidir con otra hermandad existente.
  try {
    const [dup] = await db(DB.characters).query<RowDataPacket[]>(
      'SELECT guildid FROM guild WHERE name = ? AND guildid <> ? LIMIT 1',
      [newName, guildId],
    )
    if (dup[0]) return { success: false, error: 'nameTaken' }
  } catch {
    return { success: false, error: 'genericError' }
  }

  // Cobra 1000 PD de forma atómica (condicional sobre el saldo).
  let charged = false
  try {
    const [res] = await db(DB.default).query<ResultSetHeader>(
      'UPDATE home_api_points SET dp = dp - ? WHERE accountID = ? AND dp >= ?',
      [GUILD_RENAME_PD, accountId, GUILD_RENAME_PD],
    )
    charged = res.affectedRows === 1
  } catch {
    return { success: false, error: 'genericError' }
  }
  if (!charged) return { success: false, error: 'insufficientPoints' }

  // Aplica el renombrado en el servidor por SOAP (convención del proyecto:
  // respuesta no nula = aplicado, igual que soapFulfill en paid-services). Si el
  // worldserver está caído, devuelve los PD cobrados.
  const soap = await executeSoapCommand(`.guild rename "${oldName}" "${newName}"`)
  if (soap === null) {
    await db(DB.default)
      .query('UPDATE home_api_points SET dp = dp + ? WHERE accountID = ?', [GUILD_RENAME_PD, accountId])
      .catch(() => {})
    return { success: false, error: 'soapError' }
  }

  return { success: true, cost: GUILD_RENAME_PD, newName }
}
