local json = require('json')
local space_manager = require('space-explorer.space-manager')
local space_bundle = require('space-explorer.bundle')
local cartridge = require('cartridge')
local cartridge_pool = require('cartridge.pool')
local vars = require('cartridge.vars').new('space-explorer')
local utils = require('space-explorer.utils')
local ok, front = pcall(require, 'frontend-core')
if not ok then
    -- in v4.0.0 we have changed rock name
    -- from "front" to "frontend"
    front = require('front')
end

vars:new('http_exports', {})

local function server_hash(server)
    return server.uuid
end


local function json_response(data, status)
    local code = status
    if code == nil then
        code = 200
    end
    return {
        status = code,
        body = json.encode(data),
        headers = { ['content-type'] = 'application/json' }
    }
end

local function choose_server_by_hash(hash)
    local servers = cartridge.admin_get_servers()
    for _, server in ipairs(servers) do
        if server_hash(server) == hash then
            return cartridge_pool.connect(server.uri)
        end
    end
    return nil
end

local function space_list(req)
    local connectbox = choose_server_by_hash(req:stash('host_hash'))
    if connectbox == nil then
        return json_response({ code = 'error', message = 'not_connected'}, 500)
    end
    local res = space_manager.space_list(connectbox)
    return json_response({code = 'ok', data = res})
end

local function space_items(req)
    local connectbox = choose_server_by_hash(req:stash('host_hash'))
    if connectbox == nil then
        return json_response({ code = 'error', message = 'not_connected'}, 500)
    end
    local space_id = tonumber(req:stash('space_id'), 10)
    local conditions = {}
    if string.upper(req.method) == 'POST' then
        conditions = req:json()
    end

    local items = space_manager.space_items(connectbox, space_id, conditions)
    return json_response({ code = 'ok', data = items})
end

local function select_host_property(host)
    local props = {'uri', 'status', 'uuid', 'alias'}
    local res = {}
    for _, prop in ipairs(props) do
        res[prop] = host[prop]
    end
    res['hash'] = host['uuid']
    return res
end

local function host_list(_)
    local servers = cartridge.admin_get_servers()
    local res = utils.map(servers, select_host_property)
    return json_response({code = 'ok', data = res})
end

local function request_handler(cb)
    local processed = function (req)
        local ok, res = pcall(cb, req)
        if ok == true then
            return res
        else
            return json_response({code = 'error', message = res}, 400)
        end
    end
    return processed
end

local function init()
    local httpd = cartridge.service_get('httpd')

    if httpd == nil then
        return
    end

    local base_path = '/space-explorer/api/0.0'

    vars.http_exports = {
        ['GET#space-explorer-hosts'] = {
            method = 'GET',
            path = base_path .. '/hosts',
            func = request_handler(host_list),
        },
        ['GET#space-explorer-spaces'] = {
            method = 'GET',
            path = base_path .. '/hosts/:host_hash/spaces',
            func = request_handler(space_list),
        },
        ['GET#space-explorer-data'] = {
            method = 'GET',
            path = base_path .. '/hosts/:host_hash/spaces/:space_id/data',
            func = request_handler(space_items),
        },
        ['POST#space-explorer-data'] = {
            method = 'POST',
            path = base_path .. '/hosts/:host_hash/spaces/:space_id/data',
            func = request_handler(space_items),
        },
    }

    for name, r in pairs(vars.http_exports) do
        httpd:route({
            name = name,
            path = r.path,
            method = r.method,
        }, r.func)
    end
end

local function stop()
    local httpd = cartridge.service_get('httpd')

    if httpd == nil then
        return
    end

    for name, _ in pairs(vars.http_exports) do
        local n = assert(httpd.iroutes[name])
        httpd.iroutes[name] = nil
        table.remove(httpd.routes, n)

        -- Update httpd.iroutes numeration
        for n, r in ipairs(httpd.routes) do
            if r.name then
                httpd.iroutes[r.name] = n
            end
        end
    end

    vars.http_exports = nil
end

front.add('space-explorer', space_bundle)

return {
    role_name = 'space-explorer',
    init = init,
    stop = stop,
}
