#!/usr/bin/env tarantool

local fio = require('fio')
local instance_config = require('internal.config.instance_config')
local json = require('json')

local validators = {}

local function make_allowed_values(allowed_values)
    local allowed = '[]any{\n'
    for i, value in ipairs(allowed_values) do
        if type(value) == "number" or type(value) == "boolean" then
            allowed = allowed .. tostring(value)
        elseif type(value) == "string" then
            allowed = allowed .. '"' .. value .. '"'
        else
            error('unsupported allowed type: ' .. type(value))
        end
        allowed = allowed .. ',\n'
    end
    return allowed .. '}'
end

local function make_allowed(base, allowed_values)
    if allowed_values == nil then
        return base
    end
    local allowed = make_allowed_values(allowed_values)
    return 'MakeAllowedValidator(\n' .. base .. ',\n' .. allowed .. ')'
end

validators['integer'] = function(schema)
    return make_allowed('IntegerValidator{}', schema.allowed_values)
end

validators['number'] = function(schema)
    return make_allowed('NumberValidator{}', schema.allowed_values)
end

validators['string'] = function(schema)
    return make_allowed('StringValidator{}', schema.allowed_values)
end

validators['boolean'] = function(schema)
    return make_allowed('BooleanValidator{}', schema.allowed_values)
end

validators['any'] = function(schema)
    return 'AnyValidator{}'
end

validators['number, string'] = function(schema)
    local base = 'MakeSequenceValidator(NumberValidator{}, StringValidator{})'
    return make_allowed(base, schema.allowed_values)
end

validators['string, number'] = validators['number, string']

validators['array'] = function(schema)
    local validator = validators[schema.items.type]
    if validator == nil then
        error('unsupported array type: ' .. schema.items.type)
    end

    return 'MakeArrayValidator(\n' .. validator(schema.items) .. ')'
end

validators['record'] = function(schema)
    local ret = 'MakeRecordValidator(map[string]Validator{\n'

    for field, schema in pairs(schema.fields) do
        local validator = validators[schema.type]
        if validator == nil then
            error('unsupported schema type: ' .. schema.type)
        end
        ret = ret .. '"' .. field .. '": ' .. validator(schema) .. ',\n'
    end
    return ret .. '})'
end

validators['map'] = function(schema)
    local key_validator = validators[schema.key.type]
    if key_validator == nil then
        error('unsupported key type: ' .. schema.key.type)
    end
    local value_validator = validators[schema.value.type]
    if value_validator == nil then
        error('unsupported value type: ' .. schema.value.type)
    end
    local key = key_validator(schema.key)
    local value = value_validator(schema.value)

    return 'MakeMapValidator(\n' .. key .. ',\n' .. value .. ')'
end

-- Get all target paths.
local configs = {}
for _, c in instance_config:pairs() do
    table.insert(configs, c)
end

-- Sort in the alphabet order to avoid mixing on a next generation.
table.sort(configs, function(left, right)
    local pathleft = table.concat(left.path, "_")
    local pathright = table.concat(right.path, "_")
    return pathleft < pathright
end)

-- The file generation.
local file = fio.open("paths.go", {'O_WRONLY', 'O_CREAT', 'O_TRUNC'})
file:write([[// Code generated by paths_generate.lua; DO NOT EDIT.
//
// To update the file:
// 1. Install a latest Tarantool version.
// 2. Run: go generate ./...

package cluster

var ConfigEnvPaths = [][]string{
]])

for _, config in ipairs(configs) do
    file:write('[]string{')
    for i, v in ipairs(config.path) do
       file:write('"' .. v .. '"')
       if i < #config.path then
           file:write(", ")
       end
    end
    file:write('},\n')
end

file:write([[}

var TarantoolSchema = []SchemaPath{
]])

for _, config in ipairs(configs) do
    file:write('SchemaPath{\nPath: []string{')
    for i, v in ipairs(config.path) do
        file:write('"' .. v .. '"')
        if i < #config.path then
            file:write(", ")
        end
    end
    file:write('},\n')
    file:write('Validator: ')

    schema = config.schema
    if validators[schema.type] ~= nil then
        file:write(validators[schema.type](schema))
    else
        error("unknown path type: " .. json.encode(config))
    end

    file:write(',\n},\n')
end

file:write('}\n')
file:close()
