-- vote_submit.lua
-- Atomic vote submission with distributed locking
--
-- KEYS[1] = vote:count:{category}
-- KEYS[2] = vote:user:{user_id}:categories
-- KEYS[3] = vote:category:{category}:voters
-- KEYS[4] = leaderboard:{category}
-- KEYS[5] = sync:queue:votes
-- KEYS[6] = lock:vote:{user_id}:{category}
--
-- ARGV[1] = candidate_id
-- ARGV[2] = user_id
-- ARGV[3] = category
-- ARGV[4] = timestamp
-- ARGV[5] = local_id (client UUID)
-- ARGV[6] = lock_ttl_ms
-- ARGV[7] = max_categories

local vote_count_key = KEYS[1]
local user_categories_key = KEYS[2]
local category_voters_key = KEYS[3]
local leaderboard_key = KEYS[4]
local sync_queue_key = KEYS[5]
local lock_key = KEYS[6]

local candidate_id = ARGV[1]
local user_id = ARGV[2]
local category = ARGV[3]
local timestamp = ARGV[4]
local local_id = ARGV[5]
local lock_ttl = tonumber(ARGV[6])
local max_categories = tonumber(ARGV[7])

-- Step 1: Acquire distributed lock (prevent concurrent double-vote attempts)
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',
        message = 'Another vote operation in progress'
    })
end

-- Step 2: Check if user already voted in this category
local already_voted = redis.call('SISMEMBER', category_voters_key, user_id)
if already_voted == 1 then
    redis.call('DEL', lock_key)
    return cjson.encode({
        success = false,
        error = 'ALREADY_VOTED',
        message = 'User already voted in this category'
    })
end

-- Step 3: Check if user has exceeded max votes (7 categories)
local user_vote_count = redis.call('SCARD', user_categories_key)
if user_vote_count >= max_categories then
    redis.call('DEL', lock_key)
    return cjson.encode({
        success = false,
        error = 'MAX_VOTES_REACHED',
        message = 'User has voted in all categories'
    })
end

-- Step 4: Perform atomic vote operations
-- 4a: Increment vote count for candidate
local new_count = redis.call('HINCRBY', vote_count_key, candidate_id, 1)

-- 4b: Add category to user's voted categories
redis.call('SADD', user_categories_key, category)

-- 4c: Add user to category's voters
redis.call('SADD', category_voters_key, user_id)

-- 4d: Update leaderboard (sorted set)
redis.call('ZINCRBY', leaderboard_key, 1, candidate_id)

-- Step 5: Queue for MySQL sync
local vote_record = cjson.encode({
    user_id = user_id,
    category = category,
    candidate_id = candidate_id,
    timestamp = timestamp,
    local_id = local_id
})
redis.call('RPUSH', sync_queue_key, vote_record)

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

-- Return success with new count
return cjson.encode({
    success = true,
    candidate_id = candidate_id,
    new_count = new_count,
    user_total_votes = user_vote_count + 1
})
