#!/usr/bin/env lua

local json = require "luci.jsonc"
local fs   = require "nixio.fs"
local sys = require "luci.sys"
local ut = require "luci.util"

local methods = {
	runCommand = {
		args = { cmd = "none", cmd_args = "none" },
		call = function(args)
			local errfile = "/tmp/command.err"
			local retfile = "/tmp/command.ret"
			local res = {}
			local exec_cmd = tostring(args.cmd) .. " " .. tostring(args.cmd_args) .. " 2>" .. errfile .. "; echo -n $?>" .. retfile

			local handle = io.popen(exec_cmd .. " 2>&1")
			res["stdout"] = handle:read("*a")
			handle:close()

			local stderr_file = io.open(errfile)
			res["stderr"] = stderr_file:read("*all")
			stderr_file:close()

			local ret_file = io.open(retfile)
			res["retcode"] = tonumber(ret_file:read("*all"))
			ret_file:close()

			return { result = res }
		end
	},
	getCommands = {
		call = function()
			local uci = require "luci.model.uci".cursor()
			local res = { }
			uci:foreach("luci", "command", function(s) res[#res+1] = s end)
			return { result = res}
		end
	}
}

local function parseInput()
        local parse = json.new()
        local done, err

        while true do
                local chunk = io.read(4096)
                if not chunk then
                        break
                elseif not done and not err then
                        done, err = parse:parse(chunk)
                end
        end

        if not done then
                print(json.stringify({ error = err or "Incomplete input" }))
                os.exit(1)
        end

        return parse:get()
end

local function validateArgs(func, uargs)
        local method = methods[func]
        if not method then
                print(json.stringify({ error = "Method not found" }))
                os.exit(1)
        end

        if type(uargs) ~= "table" then
                print(json.stringify({ error = "Invalid arguments" }))
                os.exit(1)
        end

        uargs.ubus_rpc_session = nil

        local k, v
        local margs = method.args or {}
        for k, v in pairs(uargs) do
                if margs[k] == nil or
                   (v ~= nil and type(v) ~= type(margs[k]))
                then
                        print(json.stringify({ error = "Invalid arguments" }))
                        os.exit(1)
                end
        end

        return method
end

if arg[1] == "list" then
        local _, method, rv = nil, nil, {}
        for _, method in pairs(methods) do rv[_] = method.args or {} end
        print((json.stringify(rv):gsub(":%[%]", ":{}")))
elseif arg[1] == "call" then
        local args = parseInput()
        local method = validateArgs(arg[2], args)
        local result, code = method.call(args)
        print((json.stringify(result):gsub("^%[%]$", "{}")))
        os.exit(code or 0)
end
