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

/** Ratio de conversión: cada 1 unidad de moneda = 100 PD. */
export const PD_PER_UNIT = 100
/** Límites de compra por transacción (en unidades de moneda). */
export const DP_MIN_AMOUNT = 1
export const DP_MAX_AMOUNT = 200

/** Acredita PD (donate points) a una cuenta de juego en `api_points` (upsert). */
export async function creditDPoints(accountId: number, points: number): Promise<boolean> {
  if (!accountId || !(points > 0)) return false
  try {
    const [rows] = await db(DB.default).query<RowDataPacket[]>(
      'SELECT id FROM api_points WHERE accountID = ?',
      [accountId],
    )
    if (rows[0]) {
      await db(DB.default).query('UPDATE api_points SET dp = dp + ? WHERE accountID = ?', [points, accountId])
    } else {
      await db(DB.default).query('INSERT INTO api_points (accountID, vp, dp) VALUES (?, 0, ?)', [accountId, points])
    }
    return true
  } catch {
    return false
  }
}

/** Saldo de PD (donate points) de una cuenta de juego. 0 si no tiene fila. */
export async function getDPointsBalance(accountId: number): Promise<number> {
  if (!accountId) return 0
  try {
    const [rows] = await db(DB.default).query<RowDataPacket[]>(
      'SELECT dp FROM api_points WHERE accountID = ?',
      [accountId],
    )
    return rows[0] ? Number(rows[0].dp) : 0
  } catch {
    return 0
  }
}

/** Saldo de PV (vote points, ganados al votar) de una cuenta. 0 si no tiene fila. */
export async function getVPointsBalance(accountId: number): Promise<number> {
  if (!accountId) return 0
  try {
    const [rows] = await db(DB.default).query<RowDataPacket[]>(
      'SELECT vp FROM api_points WHERE accountID = ?',
      [accountId],
    )
    return rows[0] ? Number(rows[0].vp) : 0
  } catch {
    return 0
  }
}

/** Acredita PV (vote points) a una cuenta (upsert). Se usa para reembolsos. */
export async function creditVPoints(accountId: number, points: number): Promise<boolean> {
  if (!accountId || !(points > 0)) return false
  try {
    await db(DB.default).query(
      'INSERT INTO api_points (accountID, vp, dp) VALUES (?, ?, 0) ON DUPLICATE KEY UPDATE vp = vp + ?',
      [accountId, points, points],
    )
    return true
  } catch {
    return false
  }
}

/**
 * Descuenta `points` PV del saldo de una cuenta de forma atómica (bloqueo de
 * fila). Devuelve `insufficientFunds` si no hay saldo suficiente. Si la acción
 * posterior falla, reembolsa con `creditVPoints`.
 */
export async function spendVPoints(
  accountId: number,
  points: number,
): Promise<{ success: boolean; error?: string }> {
  if (!accountId || !Number.isInteger(points) || points <= 0) {
    return { success: false, error: 'invalidRequest' }
  }
  const conn = await db(DB.default).getConnection()
  try {
    await conn.beginTransaction()
    const [src] = await conn.query<RowDataPacket[]>(
      'SELECT vp FROM api_points WHERE accountID = ? FOR UPDATE',
      [accountId],
    )
    const balance = src[0] ? Number(src[0].vp) : 0
    if (balance < points) {
      await conn.rollback()
      return { success: false, error: 'insufficientFunds' }
    }
    await conn.query('UPDATE api_points SET vp = vp - ? WHERE accountID = ?', [points, accountId])
    await conn.commit()
    return { success: true }
  } catch {
    try {
      await conn.rollback()
    } catch {
      /* ignore */
    }
    return { success: false, error: 'genericError' }
  } finally {
    conn.release()
  }
}

/**
 * Transfiere `points` PD desde una cuenta de origen a una de destino de forma
 * atómica (transacción con bloqueo de fila del origen). Es irreversible.
 */
export async function transferDPoints(
  fromAccountId: number,
  toAccountId: number,
  points: number,
): Promise<{ success: boolean; error?: string }> {
  if (!fromAccountId || !toAccountId || !Number.isInteger(points) || points <= 0) {
    return { success: false, error: 'invalidRequest' }
  }
  if (fromAccountId === toAccountId) return { success: false, error: 'sameAccount' }

  const conn = await db(DB.default).getConnection()
  try {
    await conn.beginTransaction()
    // Bloquea la fila del origen para evitar dobles gastos concurrentes.
    const [src] = await conn.query<RowDataPacket[]>(
      'SELECT dp FROM api_points WHERE accountID = ? FOR UPDATE',
      [fromAccountId],
    )
    const balance = src[0] ? Number(src[0].dp) : 0
    if (balance < points) {
      await conn.rollback()
      return { success: false, error: 'insufficientFunds' }
    }
    await conn.query('UPDATE api_points SET dp = dp - ? WHERE accountID = ?', [points, fromAccountId])
    await conn.query(
      'INSERT INTO api_points (accountID, vp, dp) VALUES (?, 0, ?) ON DUPLICATE KEY UPDATE dp = dp + ?',
      [toAccountId, points, points],
    )
    await conn.commit()
    return { success: true }
  } catch {
    try {
      await conn.rollback()
    } catch {
      /* ignore */
    }
    return { success: false, error: 'genericError' }
  } finally {
    conn.release()
  }
}

/**
 * Descuenta `points` PD del saldo de una cuenta de forma atómica (bloqueo de
 * fila para evitar dobles gastos concurrentes). Se usa para pagar servicios con
 * el saldo PD. Devuelve `insufficientFunds` si no hay saldo suficiente.
 * Si la acción posterior falla, reembolsa con `creditDPoints`.
 */
export async function spendDPoints(
  accountId: number,
  points: number,
): Promise<{ success: boolean; error?: string }> {
  if (!accountId || !Number.isInteger(points) || points <= 0) {
    return { success: false, error: 'invalidRequest' }
  }
  const conn = await db(DB.default).getConnection()
  try {
    await conn.beginTransaction()
    const [src] = await conn.query<RowDataPacket[]>(
      'SELECT dp FROM api_points WHERE accountID = ? FOR UPDATE',
      [accountId],
    )
    const balance = src[0] ? Number(src[0].dp) : 0
    if (balance < points) {
      await conn.rollback()
      return { success: false, error: 'insufficientFunds' }
    }
    await conn.query('UPDATE api_points SET dp = dp - ? WHERE accountID = ?', [points, accountId])
    await conn.commit()
    return { success: true }
  } catch {
    try {
      await conn.rollback()
    } catch {
      /* ignore */
    }
    return { success: false, error: 'genericError' }
  } finally {
    conn.release()
  }
}
