local Settings = require("neoconf.settings")
local Util = require("neoconf.util")

local M = {}

---@class Schema
---@field _schema Settings
---@field file string
local Schema = {}
Schema.__index = function(table, key)
  return Schema[key] or table._schema:get(key)
end

---@return Schema
function M.new(schema)
  return setmetatable({ _schema = Settings.new(schema) }, Schema)
end

local function schema_key(key)
  return table.concat(
    vim.tbl_map(function(k)
      return "properties." .. k
    end, Settings.path(key)),
    "."
  )
end

function Schema:set(key, value)
  return self._schema:set(schema_key(key), value)
end

function Schema:get(key)
  return self._schema:get(schema_key(key))
end

function Schema:import(key, value)
  return self:set(key, M.to_schema(value))
end

function M.get()
  local schema = M.new()

  schema:import("neoconf", require("neoconf.config").defaults)

  require("neoconf.plugins").fire("on_schema", schema)
  return schema
end

-- try to create a simple schema from a given value
function M.to_schema(value)
  if value == nil then
    return { type = "null" }
  elseif type(value) == "boolean" then
    return { type = "boolean", default = value, description = "boolean" }
  elseif type(value) == "string" then
    return { type = "string", default = value, description = "string" }
  elseif type(value) == "number" then
    return { type = "number", default = value, description = "number" }
  end

  if Util.islist(value) then
    return { type = "array", default = value, description = "array" }
  end

  if type(value) == "table" then
    local obj = { type = "object", properties = {} }
    for k, v in pairs(value) do
      obj.properties[k] = M.to_schema(v)
    end
    return obj
  end
  return { type = "null" }
end

return M
