local linters_path = vim.fn.stdpath('data') .. '/lint'
local null_ls = require('null-ls')

local potential_cspell_files = {
    '.cspell.json',
    'cspell.json',
    '.cSpell.json',
    'cSpell.json',
    'cspell.config.js',
    'cspell.config.cjs',
    'cspell.config.json',
    'cspell.config.yaml',
    'cspell.config.yml',
    'cspell.yaml',
    'cspell.yml',
}

local function extra_cspell_args()
    local root_dir = require('null-ls.utils').get_root()
    for _, name in ipairs(potential_cspell_files) do
        local target_file = root_dir .. '/' .. name
        if vim.fn.filereadable(target_file) == 1 then
            return { '--config', target_file }
        end
    end
    return { '--locale', 'en' }
end

local sources = {
    formatting = {
        clang_format = { filetypes = { 'c', 'cpp' } },
        cmake_format = { command = linters_path .. '/cmake_format/venv/bin/cmake-format' },
        'fish_indent',
        -- prettier = { command = linters_path .. '/prettier/node_modules/.bin/prettier' },
        -- 'qmlformat',
        shfmt = { command = linters_path .. '/shfmt/shfmt' },
        stylua = {
            condition = function(utils)
                return utils.root_has_file { 'stylua.toml', '.stylua.toml' }
            end,
        },
        -- stylelint = { command = linters_path .. '/stylelint/node_modules/.bin/stylelint' },
        -- codespell = { command = linters_path .. '/codespell/venv/bin/codespell' },
    },
    diagnostics = {
        -- 'yamllint',
        'shellcheck',
        -- codespell = { command = linters_path .. '/codespell/venv/bin/codespell' },
        cspell = {
            command = linters_path .. '/cspell/node_modules/.bin/cspell',
            extra_args = extra_cspell_args(),
            condition = function(utils)
                return utils.root_has_file(potential_cspell_files)
            end,
        },
        hadolint = {
            command = vim.fn.stdpath('config') .. '/scripts/hadolint',
            args = { '$FILENAME' },
        },
        -- 'luacheck',
        'cppcheck',
        -- gitlint = { command = linters_path .. '/gitlint/venv/bin/gitlint' },
        -- write_good = {
        --     filetypes = { 'markdown', 'org', 'asciidoc', 'org', 'asciidoc' },
        --     command = linters_path .. '/write_good/node_modules/.bin/write-good',
        -- },
        -- markdownlint = { command = linters_path .. '/markdownlint/node_modules/.bin/markdownlint' },
        -- proselint = {
        --     filetypes = { 'org', 'tex', 'markdown', 'asciidoc' },
        --     command = linters_path .. '/proselint/venv/bin/proselint',
        -- },
        -- 'qmllint',
        selene = {
            condition = function(utils)
                return utils.root_has_file('selene.toml')
            end,
        },
        -- vale = { command = linters_path .. '/vale/vale' },
        vint = { command = linters_path .. '/vint/venv/bin/vint' },
        -- stylelint = { command = linters_path .. '/stylelint/node_modules/.bin/stylelint' },
    },
    -- hover = {
    --     'dictionary' -- get word definition from dictionaryapi.dev
    -- },
    code_actions = {
        -- proselint = {
        --     filetypes = { 'org', 'tex', 'markdown', 'asciidoc' },
        --     command = linters_path .. '/proselint/venv/bin/proselint',
        -- },
        'shellcheck',
    },
}

local function gen_sources(tbl_sources)
    local sources_config = {}

    for kind, kind_sources in pairs(tbl_sources) do
        for index, config in pairs(kind_sources) do
            local source_config = nil

            if type(config) == 'string' then
                source_config = null_ls.builtins[kind][config]
            elseif type(config) == 'table' then
                source_config = null_ls.builtins[kind][index].with(config)
            end

            table.insert(sources_config, source_config)
        end
    end

    return sources_config
end

null_ls.setup {
    sources = gen_sources(sources),
    on_attach = require('user.modules.lsp.util').common_on_attach,
    update_on_insert = false,
}
