-- revoke_vote.lua
-- Atomic vote revocation
--
-- KEYS[1] = vote:user:{userId}:tickets (Hash)
-- KEYS[2] = vote:user:{userId}:programs (Set)
-- KEYS[3] = vote:sync:queue (List)
-- KEYS[4] = vote:lock:{userId} (String)
--
-- ARGV[1] = userId
-- ARGV[2] = ticketType
-- ARGV[3] = timestamp
-- ARGV[4] = lockTtlMs

local user_tickets_key = KEYS[1]
local user_programs_key = KEYS[2]
local sync_queue_key = KEYS[3]
local lock_key = KEYS[4]

local user_id = ARGV[1]
local ticket_type = ARGV[2]
local timestamp = ARGV[3]
local lock_ttl = tonumber(ARGV[4])

-- Step 1: Acquire lock
local lock_acquired = redis.call('SET', lock_key, timestamp, 'NX', 'PX', lock_ttl)
if not lock_acquired then
    return cjson.encode({
        success = false,
        error = 'LOCK_FAILED'
    })
end

-- Step 2: Check if ticket was used
local program_id = redis.call('HGET', user_tickets_key, ticket_type)
if not program_id or program_id == false then
    redis.call('DEL', lock_key)
    return cjson.encode({
        success = false,
        error = 'TICKET_NOT_USED',
        message = 'This ticket has not been used'
    })
end

-- Step 3: Revoke the vote
local count_key = 'vote:count:' .. program_id .. ':' .. ticket_type
local leaderboard_key = 'vote:leaderboard:' .. ticket_type
local voters_key = 'vote:program:' .. program_id .. ':voters'

-- Decrement count
redis.call('DECR', count_key)
redis.call('ZINCRBY', leaderboard_key, -1, program_id)

-- Remove from sets
redis.call('SREM', voters_key, user_id)
redis.call('SREM', user_programs_key, program_id)
redis.call('HDEL', user_tickets_key, ticket_type)

-- Step 4: Queue for sync
local record = cjson.encode({
    action = 'revoke',
    user_id = user_id,
    program_id = program_id,
    ticket_type = ticket_type,
    timestamp = timestamp
})
redis.call('RPUSH', sync_queue_key, record)

-- Step 5: Release lock
redis.call('DEL', lock_key)

return cjson.encode({
    success = true,
    revoked_program = program_id,
    ticket_type = ticket_type
})
