local M = {}

local modem = peripheral.wrap("back")
local SERVER_CHANNEL = 43

local function sendRequestAndWaitForReply(messageTable, replyHandler, timeout)
    timeout = timeout or 5

    local replyChannel = math.random(1000, 9999)
    modem.open(replyChannel) 
    messageTable.replyChannel = replyChannel

    local jsonMessage = textutils.serialiseJSON(messageTable)
    modem.transmit(SERVER_CHANNEL, replyChannel, jsonMessage)

    local startTime = os.clock()
    local replyReceived = false
    while os.clock() - startTime < timeout do
        local event, side, channel, receivedReplyChannel, message, distance = os.pullEvent("modem_message")
        if event == "modem_message" and channel == replyChannel then
            local success, parsedReply = pcall(textutils.unserialiseJSON, message)
            if success and type(parsedReply) == "table" then
                replyHandler(parsedReply)
                replyReceived = true
                break 
            end
        end
    end
    modem.close(replyChannel) 

    return replyReceived
end

function M.getCredit(username, callback)
    local message = {
        type = "getCredit",
        user = username
    }

    return sendRequestAndWaitForReply(message, function(reply)
        if reply.type == "creditResponse" and reply.user == username then
            callback(reply.credit)
        else

            callback(nil)
        end
    end)
end

function M.addTransaction(username, amount, vendor, vendor_account, callback, timeout)

    if type(amount) ~= "number" or amount <= 0 then
        error("Amount must be a positive number.", 2)
    end
    if type(username) ~= "string" or #username == 0 then
        error("Username must be a non-empty string.", 2)
    end
    if type(vendor) ~= "string" or #vendor == 0 then
        error("Vendor must be a non-empty string.", 2)
    end
    if type(vendor_account) ~= "string" or #vendor_account == 0 then
        error("Vendor account must be a non-empty string.", 2)
    end
    if type(callback) ~= "function" then
        error("Callback must be a function.", 2)
    end

    if timeout and (type(timeout) ~= "number" or timeout <= 0) then
        error("Timeout must be a positive number if provided.", 2)
    end

    local message = {
        type = "addTransaction",
        user = username,
        amount = amount,
        vendor = vendor,
        vendor_account = vendor_account
    }

    return sendRequestAndWaitForReply(message, function(reply)

        if reply.type == "transactionCompleted" and reply.transaction_id then
            if reply.status == "success" then
                callback(true, reply.newCredit) 
            elseif reply.status == "failed" then

                callback(false, reply.reason) 
            elseif reply.status == "cancelled" then

                callback(false, "cancelled") 
            else

                callback(false, "unknown_status")
            end
        else

            callback(false, "unexpected_reply") 
        end
    end, timeout) 
end

return M