local cassandra = require "lua_cassandra"

local logger = {}

package.preload["phones.logger"] = function()
	return logger
end

local function init (options)
	assert(type(options) == "table", "options must be a table")
	assert(options.cassandra, "options.cassandra is missing")
	assert(options.cassandra.host, "missing options.cassandra.host field")
	assert(options.cassandra.port, "missing options.cassandra.port field")

	for k,v in pairs(options.logger) do
		logger[k] = v
	end
	setmetatable(logger, getmetatable(options.logger))

	cassandra.seed(options.cassandra.host, options.cassandra.port)

	--model.SetDatasource(cassandra)

	return true
end


---
-- Esta funcion no reemplaza brackets. Entender y documentar.
--
local function replaceBrackets (format, source)

	logger.info("replaceBrackets: input", format, source)

	local result = ""
	local all = format

	while all ~= "" do
		local prefix, replace, newAll = all:match("([^{]*){([^}]+)}(.*)")
		logger.info("replaceBrackets", prefix, replace, newAll)
		if not prefix then
			result = result .. all
			break
		end
		local replaceFirst, replaceLast = replace:match("([0-9]+):([0-9]*)")
		if replaceLast == "" then
			replaceLast = -1
		end
		result = result .. prefix .. source:sub(tonumber(replaceFirst), tonumber(replaceLast))
		all = newAll
	end

	logger.info("replaceBrackets: output", result)
	return result
end


---
--
local function dialing_number (params)

	local range = params.range
	local country_code = params.countryCode
	local area_code = params.areaCode
	local number = params.number

	assert(range == "Local" or range == "SameCountry" or range == "Global",
		"range must be one of 'Local', 'SameCountry' or 'Global'")

	local connection = cassandra:CassandraConnection("Provisioning")

	-- Look for a dialing schema. From particular to general.
	local schemaKeys = {
		range..":"..country_code.."/"..area_code,
		range..":"..country_code,
		range
	}
	local schema
	for _, schemaKey in ipairs(schemaKeys) do
		local schemaCols = connection:GetSliceRange(schemaKey, "PhoneDialingSchemas", "", {
			slice_range = { start = "."..number, finish = "", reversed = true, count = 1 }
		})
		if schemaCols[1] then
			schema = schemaCols[1].value
			break
		end
	end
    connection:Close()
	if not schema or schema == "invalid" then
		return
	end

	-- replace each { rule } ocurrence according
	logger.Warning("schema", schema)
	local result = schema:gsub("{[^}]*}", function(toSub)
		logger.Warning(toSub)
		id, rangeFrom, rangeTo = string.match(toSub, "{([^:]*):([0-9]+):([0-9]+)}")
		if not id then
			id, rangeFrom = string.match(toSub, "{([^:]*):([0-9]+)}")
			if not id then
				id = string.match(toSub, "{([^:]*)}")
			end
		end

		local replacement =
			(id == "country") and country_code or
			(id == "area") and area_code or
			(id == "number") and number or
			nil
		if not replacement then return "" end
		if not rangeFrom then return replacement end
		if not rangeTo then return string.sub(replacement, tonumber(rangeFrom)) end
		return string.sub(replacement, tonumber(rangeFrom), tonumber(rangeTo))
	end)

	return result
end


---
-- TODO: Documentar esto, que es inhumano para el pobre desgraciado que tiene que entenderlo
--
local function validate_phone (params)

	local country_code = params.country_code
	local area_code = params.area_code
	local number = params.number

	logger.Info("Validating phone '%s' being called from '%s/%s'", number, country_code, area_code)

	local connection = cassandra:CassandraConnection("Provisioning")

	local countryCode, areaCode, clean
	local ref = ("%s-%s"):format(country_code, area_code)
	while not clean do
		logger.Debug("\tGoing to check %s with number %s", ref, number)
		local defSlice = connection:GetSliceRange(ref, "PhoneNumberNormalization", "", {
			slice_range = { start = "."..number, finish = "", reversed = true, count = 1 }
		})
		if not defSlice[1] or defSlice[1].value == "invalid" then
			logger.debug("no slice or invalid", json.encode(defSlice))
			break
		end

		logger.debug("Processing rule '%s'", defSlice[1].value)

		-- Aca obtuvo un accion a realizar. fw, valid u otra poronga
		local forwardDest, forwardCleanup = defSlice[1].value:match("fw|([^|]*)|(.+)")
		if forwardDest then
			logger.debug("Applying 'forward' rule. forwardDest: %s, forwardCleanup: %s", forwardDest, forwardCleanup)
			ref = replaceBrackets(forwardDest, number)
			number = replaceBrackets(forwardCleanup, number)
			logger.debug("Applied 'forward' rule. new ref: %s, new number: %s", ref, number)
		else
			local valid, length = defSlice[1].value:match("^valid|(.*)|([0-9]*)$")
			local lengthTo = length
			if not valid then
				valid, length, lengthTo = defSlice[1].value:match("^valid|(.*)|([0-9]*)-([0-9]*)$")
			end
			if valid then
				clean = replaceBrackets(valid, number)
				countryCode, areaCode, clean = clean:match("([^-]*)-([^-]*)-([^-]*)")
				if #clean < tonumber(length) or #clean > tonumber(lengthTo) then
					clean = nil
				end
			end
			break
		end
	end

    connection:Close()
	if not clean then
		logger.Info("Not clean, motherfuckers")
		return
	end

	logger.Info(countryCode, areaCode, clean)

	return {
		countryCode = countryCode,
		areaCode = areaCode,
		number = clean,
		dialLocal = dialing_number({
			range = "Local",
			countryCode = countryCode,
			areaCode = areaCode,
			number = clean
		}),
		dialSameCountry = dialing_number({
			range = "SameCountry",
			countryCode = countryCode,
			areaCode = areaCode,
			number = clean
		}),
		dialGlobal = dialing_number({
			range = "Global",
			countryCode = countryCode,
			areaCode = areaCode,
			number = clean
		})
	}
end



--
-- Exported functions
--
return {
	init = init,
	validate = validate_phone
}
