local M = {}

function M.colorizer()
    require('colorizer').setup({ '*' }, {
        RGB = true, -- #RGB hex codes
        RRGGBB = true, -- #RRGGBB hex codes
        names = true, -- "Name" codes like Blue
        RRGGBBAA = true, -- #RRGGBBAA hex codes
        rgb_fn = true, -- CSS rgb() and rgba() functions
        hsl_fn = true, -- CSS hsl() and hsla() functions
        css = true, -- Enable all CSS features: rgb_fn, hsl_fn, names, RGB, RRGGBB
        css_fn = true, -- Enable all CSS *functions*: rgb_fn, hsl_fn
        mode = 'background', -- Set the display mode.
    })
end

-- function M.org_bullets()
--     require('org-bullets').setup {
--         symbols = { '', '', '', '' }
--     }
-- end

function M.todo_comments()
    require('todo-comments').setup {
        signs = false,
        -- sign_priority = 8,
        keywords = {
            FIX = {
                icon = ' ', -- icon used for the sign, and in search results
                color = 'error', -- can be a hex color, or a named color (see below)
                alt = { 'FIXME', 'BUG', 'FIXIT', 'ISSUE' }, -- a set of other keywords that all map to this FIX keywords
                -- signs = false, -- configure signs for some keywords individually
            },
            TODO = { icon = ' ', color = 'info' },
            HACK = { icon = ' ', color = 'warning' },
            WARN = { icon = ' ', color = 'warning', alt = { 'WARNING', 'XXX' } },
            PERF = { icon = ' ', alt = { 'OPTIM', 'PERFORMANCE', 'OPTIMIZE' } },
            NOTE = { icon = ' ', color = 'hint', alt = { 'INFO' } },
        },
        merge_keywords = true,
        highlight = {
            before = '', -- 'fg' or 'bg' or empty
            keyword = 'fg', -- 'fg', 'bg', 'wide' or empty. (wide is the same as bg, but will also highlight surrounding characters)
            after = '', -- 'fg' or 'bg' or empty
            pattern = [[.*<(KEYWORDS)\s*:]], -- pattern or table of patterns, used for highlightng (vim regex)
            comments_only = true, -- uses treesitter to match keywords in comments only
            max_line_len = 400, -- ignore lines longer than this
            exclude = { 'org' }, -- list of file types to exclude highlighting
        },
        colors = {
            error = { 'DiagnosticError', 'TSDanger', '#bf616a', '#e06c75' },
            warning = { 'DiagnosticWarn', 'TSWarning', '#ebcb8b', '#e5c07b' },
            info = { 'DiagnosticInfo', 'TSNote', '#81a1c1', '#61afef' },
            hint = { 'DiagnosticHint', '#88c0d0', '#56b6c2' },
            default = { 'Normal', '#d8dee9', '#abb2bf' },
        },
        search = {
            command = 'rg',
            args = {
                '--hidden',
                '--color=never',
                '--no-heading',
                '--with-filename',
                '--line-number',
                '--column',
            },
            -- regex that will be used to match keywords.
            -- don't replace the (KEYWORDS) placeholder
            pattern = [[\b(KEYWORDS):]], -- ripgrep regex
            -- pattern = [[\b(KEYWORDS)\b]] -- match without the extra colon. You'll likely get false positives
        },
    }
end

function M.orgmode()
    local c = require('user.themes.' .. vim.g.colors_name .. '.colors')
    require('orgmode').setup {
        -- General settings
        org_agenda_files = { '~/Documents/Org/agenda/*' },
        org_default_notes_file = '~/Documents/Org/notes.org',
        org_todo_keywords = {
            'TODO',
            'WAIT',
            'HOLD',
            'IDEA',
            'DONE',
            'KILL',
            'CANCELED',
            'OKAY',
            'YES',
            'NO',
        },
        org_todo_keyword_faces = {
            TODO = ':foreground ' .. c.green .. ' :weight bold',
            WAIT = ':foreground ' .. c.yellow .. ' :weight bold',
            HOLD = ':foreground ' .. c.yellow .. ' :weight bold',
            IDEA = ':foreground ' .. c.green .. ' :weight bold :slant italic',
            DONE = ':foreground ' .. c.purple .. ' :weight bold',
            OKAY = ':foreground ' .. c.purple .. ' :weight bold :slant italic',
            KILL = ':foreground ' .. c.red .. ' :weight bold',
            CANCELED = ':foreground ' .. c.red .. ' :weight bold',
            YES = ':foreground ' .. c.green .. ' :weight bold :underline on',
            NO = ':foreground ' .. c.red .. ' :weight bold :underline on',
        },
        org_hide_emphasis_markers = true,
        org_hide_leading_stars = true,
        org_highlight_latex_and_related = 'entities',

        -- Agenda settings
        org_deadline_warning_days = 7,
        org_agenda_span = 'week',
        org_agenda_start_on_weekday = 7, -- Start a week in Sunday
        org_agenda_min_height = 15,

        -- Tags settings
        org_use_tag_inheritance = false,

        -- Notifications
        notifications = {
            enabled = true,
            cron_enabled = false,
            repeater_reminder_time = false,
            deadline_warning_reminder_time = false,
            reminder_time = { 0, 5, 10 },
            deadline_reminder = true,
            scheduled_reminder = true,
            notifier = function(tasks)
                local result = {}
                for _, task in ipairs(tasks) do
                    require('orgmode.utils').concat(result, {
                        string.format('# %s (%s)', task.category, task.humanized_duration),
                        string.format('%s %s %s', string.rep('*', task.level), task.todo, task.title),
                        string.format('%s: <%s>', task.type, task.time:to_string()),
                    })
                end

                if not vim.tbl_isempty(result) then
                    require('orgmode.notifications.notification_popup'):new {
                        content = result,
                        border = vim.g.border_style,
                    }
                end
            end,
        },
    }

    require('orgmode').setup_ts_grammar()
end

function M.treesitter()
    require('nvim-treesitter.configs').setup {
        ensure_installed = 'all',
        highlight = {
            enable = true,
            additional_vim_regex_highlighting = { 'org' },
        },
        incremental_selection = {
            enable = true,
            keymaps = {
                init_selection = 'gti',
                node_incremental = 'gtn',
                scope_incremental = 'gts',
                node_decremental = 'gtm',
            },
        },
        indent = { enable = false },
        refactor = {
            highlight_definitions = {
                enable = true,
                -- Set to false if you have an `updatetime` of ~100.
                clear_on_cursor_move = false,
            },
            highlight_current_scope = { enable = false },
            smart_rename = {
                enable = true,
                keymaps = { smart_rename = 'gtr' },
            },
            navigation = {
                enable = true,
                keymaps = {
                    goto_definition = 'gtd',
                    list_definitions = 'gtD',
                    list_definitions_toc = 'gto',
                    goto_next_usage = 'gtk',
                    goto_previous_usage = 'gtj',
                },
            },
        },
        autotag = {
            enable = true,
            filetypes = {
                'html',
                'javascript',
                'javascriptreact',
                'typescript',
                'typescriptreact',
                'svelte',
                'vue',
            },
        },
        textobjects = {
            select = {
                enable = true,
                lookahead = true,
                keymaps = {
                    ['af'] = '@function.outer',
                    ['if'] = '@function.inner',
                    ['ac'] = '@class.outer',
                    ['ic'] = '@class.inner',
                },
            },
            move = {
                enable = true,
                set_jumps = true, -- whether to set jumps in the jumplist
                goto_next_start = {
                    [']['] = '@function.outer',
                    [']m'] = '@class.outer',
                },
                goto_next_end = {
                    [']]'] = '@function.outer',
                    [']M'] = '@class.outer',
                },
                goto_previous_start = {
                    ['[['] = '@function.outer',
                    ['[m'] = '@class.outer',
                },
                goto_previous_end = {
                    ['[]'] = '@function.outer',
                    ['[M'] = '@class.outer',
                },
            },
        },
        matchup = {
            enable = true,
            disable_virtual_text = true,
            include_match_words = true,
        },
        rainbow = {
            enable = true,
            extended_mode = true,
            max_file_lines = 1000,
        },
        playground = {
            enable = true,
            disable = {},
            updatetime = 25,
            persist_queries = false,
            keybindings = {
                toggle_query_editor = 'o',
                toggle_hl_groups = 'i',
                toggle_injected_languages = 't',
                toggle_anonymous_nodes = 'a',
                toggle_language_display = 'I',
                focus_language = 'f',
                unfocus_language = 'F',
                update = 'R',
                goto_node = '<cr>',
                show_help = '?',
            },
        },
        query_linter = {
            enable = true,
            use_virtual_text = true,
            lint_events = { 'BufWritePre', 'CursorHold' },
        },
        context_commentstring = {
            enable = true,
            enable_autocmd = false,
        },
    }

    -- tree-sitter based folding
    vim.opt.foldmethod = 'expr'
    vim.opt.foldexpr = 'nvim_treesitter#foldexpr()'

    require('user.keymap.plugmap').treesitter()
end

function M.indent_blankline()
    require('indent_blankline').setup {
        char = '│',
        -- space_char_blankline = '·',
        show_first_indent_level = true,
        filetype_exclude = {
            'startify',
            'dashboard',
            'alpha',
            'log',
            'gitcommit',
            'packer',
            'vimwiki',
            'markdown',
            'org',
            'json',
            'txt',
            'help',
            'NvimTree',
            'git',
            'TelescopePrompt',
            'undotree',
            'dotooagenda',
            'fugitive',
            '', -- for all buffers without a filetype
        },
        buftype_exclude = { 'terminal', 'nofile' },
        show_trailing_blankline_indent = false,
        -- show_end_of_line = true,
        show_current_context = true,
        context_patterns = {
            'class',
            'function',
            'method',
            'block',
            'list_literal',
            'selector',
            '^if',
            '^table',
            'if_statement',
            'while',
            'for',
        },
        use_treesitter = true,
    }
end

function M.iswap()
    require('iswap').setup {
        -- The keys that will be used as a selection, in order
        -- ('asdfghjklqwertyuiopzxcvbnm' by default)
        -- keys = 'qwertyuiop',

        -- Grey out the rest of the text when making a selection
        -- (enabled by default)
        -- grey = 'disable',

        -- Highlight group for the sniping value (asdf etc.)
        -- default 'Search'
        -- hl_snipe = 'ErrorMsg',

        -- Highlight group for the visual selection of terms
        -- default 'Visual'
        -- hl_selection = 'WarningMsg',

        -- Highlight group for the greyed background
        -- default 'Comment'
        hl_grey = 'LineNr',

        -- Automatically swap with only two arguments
        -- default nil
        autoswap = true,
    }
end

function M.matchup()
    vim.g.matchup_matchparen_offscreen = { method = 'popup' }
end

function M.twilight()
    require('twilight').setup {
        dimming = {
            alpha = 0.25, -- amount of dimming
            -- we try to get the foreground from the highlight groups or fallback color
            color = { 'Normal', '#ffffff' },
            inactive = false, -- when true, other windows will be fully dimmed (unless they contain the same buffer)
        },
        context = 10,
        treesitter = true,
        expand = { -- for treesitter, we we always try to expand to the top-most ancestor with these types
            'function',
            'method',
            'table',
            'if_statement',
        },
        exclude = { 'alpha', 'packer', 'NvimTree', 'qf' },
    }
end

function M.zenmode()
    require('zen-mode').setup {
        window = {
            options = {
                signcolumn = 'no',
                number = false,
                relativenumber = false,
                cursorline = false,
                cursorcolumn = false,
                foldcolumn = '0',
            },
        },
        -- on_open = function()
        --     vim.api.nvim_command('TSContextDisable')
        --     vim.api.nvim_command('IndentBlanklineDisable')
        -- end,
        -- on_close = function()
        --     vim.api.nvim_command('TSContextEnable')
        --     vim.api.nvim_command('IndentBlanklineEnable')
        -- end
    }
end

function M.better_escape()
    require('better_escape').setup {
        mapping = { 'jk', 'kj' },
        timeout = vim.opt.timeoutlen:get(),
        clear_empty_lines = false,
        keys = '<Esc>',
    }
end

-- TODO: f,t,F,T motions + repeat
function M.hop()
    require('hop').setup {
        keys = 'etovxqpdygfblzhckisuran',
        jump_on_sole_occurrence = false,
    }
end

function M.dial()
    local augend = require('dial.augend')

    require('dial.config').augends:register_group {
        default = {
            augend.integer.alias.decimal_int,
            augend.integer.alias.hex,
            augend.date.alias['%Y/%m/%d'],
            augend.date.alias['%Y-%m-%d'],
            augend.date.alias['%m/%d'],
            augend.date.alias['%H:%M:%S'],
            augend.date.alias['%H:%M'],
            augend.semver.alias.semver,
            augend.constant.alias.ja_weekday_full,
            augend.constant.alias.bool,
            augend.constant.new {
                elements = { 'True', 'False' },
                word = true,
                cyclic = true,
            },
            augend.hexcolor.new {
                case = 'lower',
            },
        },
    }

    require('user.keymap.plugmap').dial()
end

function M.table_mode()
    local opts = {
        table_mode_disable_mappings = 1,
        table_mode_disable_tableize_mappings = 1,
        table_mode_map_prefix = '<leader>et',
        table_mode_toggle_map = '',
    }

    for key, val in pairs(opts) do
        vim.g[key] = val
    end
end

function M.comment()
    require('Comment').setup {
        padding = true, -- add a ' ' between comment and the line
        sticky = true, -- whether the cursor should stay at its position
        ignore = '^$', -- ignore empty lines
        mappings = {
            basic = true, -- 'gcc', 'gcb', 'gc[count]{motion}', 'gb[count]{motion}'
            extra = true, -- 'gco', 'gcO', 'gcA'
            extended = false, -- 'g>', 'g>[count]{motion}', 'g<[count]{motion}'
        },
        toggle = { -- toggle mapping in NORMAL + VISUAL mode
            line = 'gcc',
            block = 'gbc',
        },
        opleader = { -- operator-pending mapping in NORMAL + VISUAL mode
            line = 'gc',
            block = 'gb',
        },
        pre_hook = function(ctx)
            require('ts_context_commentstring.internal').update_commentstring()
        end,
        post_hook = nil,
    }
end

function M.range_highlight()
    require('range-highlight').setup()
end

function M.neogen()
    require('neogen').setup {
        enabled = true,
        input_after_comment = true,
    }
end

function M.project()
    require('project_nvim').setup {
        manual_mode = false,
        detection_methods = { 'lsp', 'pattern' },
        patterns = {
            '.git',
            '_darcs',
            '.hg',
            '.bzr',
            '.svn',
            '>Code',
            'Makefile',
            'package.json',
            '*.pro',
        },
        show_hidden = true,
        silent_chdir = true,
        datapath = vim.fn.stdpath('data'),
    }
end

function M.gitlinker()
    require('gitlinker').setup {
        opts = {
            remote = nil, -- force the use of a specific remote
            -- adds current line nr in the url for normal mode
            add_current_line_on_normal_mode = true,
            -- callback for what to do with the url
            action_callback = require('gitlinker.actions').copy_to_clipboard,
            -- print the url after performing the action
            print_url = true,
        },
        callbacks = {
            ['git.folliehiyuki.com'] = require('gitlinker.hosts').get_gitea_type_url,
            ['git.disroot.org'] = require('gitlinker.hosts').get_gitea_type_url,
            ['gitlab.alpinelinux.org'] = require('gitlinker.hosts').get_gitlab_type_url,
        },
        -- default mapping to call url generation with action_callback
        mappings = '<leader>gy',
    }

    require('user.keymap.plugmap').gitlinker()
end

function M.neoscroll()
    require('neoscroll').setup {
        -- All these keys will be mapped to their corresponding default scrolling animation
        mappings = { '<C-u>', '<C-d>', '<C-b>', '<C-f>', '<C-y>', '<C-e>', 'zt', 'zz', 'zb' },
        hide_cursor = false, -- Hide cursor while scrolling
        stop_eof = true, -- Stop at <EOF> when scrolling downwards
        use_local_scrolloff = false, -- Use the local scope of scrolloff instead of the global scope
        respect_scrolloff = false, -- Stop scrolling when the cursor reaches the scrolloff margin of the file
        cursor_scrolls_alone = true, -- The cursor will keep on scrolling even if the window cannot scroll further
        easing_function = nil, -- Default easing function
        pre_hook = nil, -- Function to run before the scrolling animation starts
        post_hook = nil, -- Function to run after the scrolling animation ends
        performance_mode = false, -- Disable "Performance Mode" on all buffers.
    }
end

function M.gitsigns()
    require('gitsigns').setup {
        signs = {
            add = { hl = 'GitSignsAdd', text = '+', numhl = 'GitSignsAddNr' },
            change = { hl = 'GitSignsChange', text = '~', numhl = 'GitSignsChangeNr' },
            delete = { hl = 'GitSignsDelete', text = '_', numhl = 'GitSignsDeleteNr' },
            topdelete = { hl = 'GitSignsDelete', text = '‾', numhl = 'GitSignsDeleteNr' },
            changedelete = { hl = 'GitSignsChange', text = '-', numhl = 'GitSignsChangeNr' },
        },
        signcolumn = true,
        numhl = false,
        linehl = false,
        word_diff = false,
        watch_gitdir = {
            interval = 1000,
            follow_files = true,
        },
        attach_to_untracked = true,
        current_line_blame = false,
        current_line_blame_opts = {
            delay = 1000,
            virt_text = true,
            virt_text_pos = 'right_align',
            ignore_whitespace = false,
        },
        current_line_blame_formatter_opts = {
            relative_time = false,
        },
        sign_priority = 6,
        update_debounce = 100,
        status_formatter = nil, -- Use default
        max_file_length = 40000,
        preview_config = {
            -- Options passed to nvim_open_win
            border = vim.g.border_style,
            style = 'minimal',
            relative = 'cursor',
            row = 0,
            col = 1,
        },
        yadm = { enable = false },
        diff_opts = {
            algorithm = 'myers', -- others: 'minimal', 'patience', 'histogram'
            internal = true, -- If luajit is present
        },
        on_attach = function(bufnr)
            local function map(mode, l, r, opts)
                opts = opts or {}
                opts.noremap = true
                vim.api.nvim_buf_set_keymap(bufnr, mode, l, r, opts)
            end

            -- Navigation
            map('n', ']g', '&diff ? "]g" : "<cmd>Gitsigns next_hunk<CR>"', { expr = true })
            map('n', '[g', '&diff ? "[g" : "<cmd>Gitsigns prev_hunk<CR>"', { expr = true })

            -- Actions
            map('n', '<leader>gs', '<cmd>Gitsigns stage_hunk<CR>')
            map('x', '<leader>gs', '<cmd>Gitsigns stage_hunk<CR>')
            map('n', '<leader>gr', '<cmd>Gitsigns reset_hunk<CR>')
            map('x', '<leader>gr', '<cmd>Gitsigns reset_hunk<CR>')
            map('n', '<leader>gS', '<cmd>Gitsigns stage_buffer<CR>')
            map('n', '<leader>gu', '<cmd>Gitsigns undo_stage_hunk<CR>')
            map('n', '<leader>gR', '<cmd>Gitsigns reset_buffer<CR>')
            map('n', '<leader>gp', '<cmd>Gitsigns preview_hunk<CR>')
            map('n', '<leader>gb', '<cmd>lua require"gitsigns".blame_line{full = true}<CR>')
            map('n', '<leader>gd', '<cmd>Gitsigns diffthis<CR>')
            map('n', '<leader>gD', '<cmd>lua require"gitsigns".diffthis("~")<CR>')
            map('n', '<leader>gU', '<cmd>Gitsigns reset_buffer_index<CR>')
            map('n', '<leader>gz', '<cmd>Gitsigns toggle_deleted<CR>')

            -- Text objects
            map('o', 'ih', ':<C-u>Gitsigns select_hunk<CR>')
            map('x', 'ih', ':<C-u>Gitsigns select_hunk<CR>')
        end,
    }
end

return M
