const { execute, queryOne, query, transaction } = require('../database/postgres')
const crypto = require('crypto')
const NotificationService = require('../services/notificationService')

class RaffleResult {
  /**
   * Executa o sorteio de um raffle de forma segura e auditável.
   */
  static async draw(raffle_id, executedByIp = '127.0.0.1') {
    const txResult = await transaction(async (client) => {
      // 1. Lock do sorteio
      const raffleResult = await client.query(
        "SELECT id, titulo, end_at, status FROM raffles WHERE id = $1 AND status != 'finished' FOR UPDATE",
        [raffle_id]
      )
      
      const raffle = raffleResult.rows[0]
      if (!raffle) throw new Error('Sorteio não encontrado ou já finalizado')

      // 2. Busca todos os tickets vendidos, ordenados pela data e id (ordem determinística)
      const ticketsResult = await client.query(
        'SELECT id, numero FROM tickets WHERE raffle_id = $1 ORDER BY id ASC',
        [raffle_id]
      )

      const tickets = ticketsResult.rows
      if (tickets.length === 0) throw new Error('Nenhum ticket vendido para este sorteio')

      const totalSold = tickets.length
      const lastTicketId = tickets[tickets.length - 1].id

      // 3. Gerar SEED fixo e auditável (SHA256)
      // Baseado em: id do sorteio + end_at + total vendido + id do último ticket
      const endAtISO = raffle.end_at ? new Date(raffle.end_at).toISOString() : 'no-end-date'
      const seedString = `${raffle_id}-${endAtISO}-${totalSold}-${lastTicketId}`
      const seed = crypto.createHash('sha256').update(seedString).digest('hex')

      // 4. Transformar seed (hex) em um número inteiro grande usando BigInt para matemática justa
      const seedBigInt = BigInt('0x' + seed)
      const winnerIndex = Number(seedBigInt % BigInt(totalSold))
      
      const winnerTicket = tickets[winnerIndex]

      // 5. Salvar resultado e logs
      await client.query(
        `INSERT INTO raffle_results (raffle_id, winner_ticket_id, seed, algorithm_version)
         VALUES ($1, $2, $3, $4)`,
        [raffle_id, winnerTicket.id, seed, '1.0']
      )

      await client.query(
        `INSERT INTO raffle_draw_logs (raffle_id, seed, result_number, ip)
         VALUES ($1, $2, $3, $4)`,
        [raffle_id, seed, winnerTicket.numero, executedByIp]
      )

      // 6. Atualizar status do sorteio
      await client.query(
        "UPDATE raffles SET status = 'finished', ativo = 0 WHERE id = $1",
        [raffle_id]
      )

      return {
        winner_ticket_id:   winnerTicket.id,
        winner_number:      winnerTicket.numero,
        seed,
        winner_cpf:         winnerTicket.usuario_cpf,
        winner_nome:        winnerTicket.usuario_nome,
        winner_telefone:    winnerTicket.usuario_telefone,
        raffle_titulo:      raffle.titulo,
      }
    })

    // Notificação ao ganhador FORA da transação — garante que só dispara após COMMIT
    if (txResult.winner_cpf) {
      setImmediate(() => {
        NotificationService.notifyWinner(
          txResult.winner_cpf,
          txResult.winner_nome,
          txResult.winner_telefone,
          txResult.raffle_titulo || 'Sorteio #' + raffle_id,
          txResult.winner_number
        ).catch(console.error)
      })
    }

    return {
      winner_ticket_id: txResult.winner_ticket_id,
      winner_number:    txResult.winner_number,
      seed:             txResult.seed,
    }
  }

  static findByRaffleId(raffle_id) {
    return queryOne(`
      SELECT rr.*, t.numero as winner_number, t.usuario_nome, t.usuario_telefone, t.usuario_cpf
      FROM raffle_results rr
      JOIN tickets t ON t.id = rr.winner_ticket_id
      WHERE rr.raffle_id = $1
    `, [raffle_id])
  }
}

module.exports = RaffleResult
