local M = {}

local states = require("lazy-filer.states")
local rpc_call = require("lazy-filer.call_lib")
local ns = require("lazy-filer.namespace")
local subwin = require("lazy-filer.subwin")
local hl = require("lazy-filer.highlight")

local ui = states.ui

local myui = require("my-ui")

local api = vim.api

local function get_line_idx()
    local win = ui.main.get_win()
    if not win then return end

    local cursor = api.nvim_win_get_cursor(win)
    return cursor[1]
end

M.fn = {
    expand_dir = function()
        local line_idx = get_line_idx()
        rpc_call.expand_dir(line_idx)
    end,

    get_dir = function()
        local line_idx = get_line_idx()
        return rpc_call.get_dir(line_idx)
    end,

    chdir_to_cursor = function()
        local line_idx = get_line_idx()
        local dir = rpc_call.get_dir(line_idx)
        if not dir.name then return end
        api.nvim_set_current_dir(dir.name)

        states.dir_displayed.set(dir.name)
        rpc_call.refresh(dir.name)
    end,

    move_to_parent = function()
        local cwd = states.dir_displayed.get()
        if not cwd then return end
        rpc_call.move_to_parent(cwd)

        local parent = vim.fs.dirname(cwd)
        states.dir_displayed.set(parent)
    end,

    new_filer = function()
        local cwd = vim.uv.cwd()
        states.dir_displayed.set(cwd)
        rpc_call.new_filer(cwd)
    end,

    open_file = function()
        local line_idx = get_line_idx()
        rpc_call.open_file(line_idx)
    end,

    open_or_expand = function()
        local line_idx = get_line_idx()
        rpc_call.open_or_expand(line_idx)
    end,

    refresh = function()
        local cwd = states.dir_displayed.get()
        if cwd then
            rpc_call.refresh(cwd)
        else
            cwd = vim.uv.cwd()
            states.dir_displayed.set(cwd)
            rpc_call.refresh(cwd)
        end
    end,

    create_entry = subwin.create_entry.exec,
    open_new_entry_win = subwin.create_entry.open_win,
    open_delete_entry_win = subwin.delete_entry.open_win,
    open_rename_entry_win = subwin.rename_entry.open_win,
    rename_entry = subwin.rename_entry.exec,

    move_to_filer = function()
        ui.main.focus()
    end,
    move_to_subwin = function()
        ui.companion.focus()
    end,

    close_filer = function()
        if not myui.focus_on_last_active_ui() then myui.focus_on_last_active_win() end
        ui.main.close()
    end,
    close_subwin = function()
        ui.main.focus()
        ui.companion.close()
    end,
}

local function define_keymaps_wrap(args, default_opts)
    local opts = vim.tbl_deep_extend("force", vim.deepcopy(default_opts), args[4] or {})

    local rhs = args[3]
    if type(rhs) == "string" and M.fn[rhs] then
        vim.keymap.set(args[1], args[2], M.fn[rhs], opts)
    else
        vim.keymap.set(args[1], args[2], rhs, opts)
    end
end

function M.setup(opts)
    if opts.keymaps then
        if opts.keymaps.global then
            for _, args in ipairs(opts.keymaps.global) do
                define_keymaps_wrap(args, { silent = true })
            end
        end

        if opts.keymaps.filer then
            ui.opts.main.setup_buf = function(buf)
                for _, args in ipairs(opts.keymaps.filer) do
                    define_keymaps_wrap(args, { buffer = buf, silent = true })
                end
            end
        end

        subwin.set_keymaps({
            new_entry = function(buf)
                if opts.keymaps.new_entry then
                    for _, args in ipairs(opts.keymaps.new_entry) do
                        define_keymaps_wrap(args, { buffer = buf, silent = true })
                    end
                end
            end,

            rename_entry = function(buf)
                if opts.keymaps.rename_entry then
                    for _, args in ipairs(opts.keymaps.rename_entry) do
                        define_keymaps_wrap(args, { buffer = buf, silent = true })
                    end
                end
            end,
        })
    end

    if opts.border then
        ui.update_opts({ background = opts.border })
    end

    hl.set_highlight_groups(opts.hl)

    ns.register(opts.plugin_dir, opts.rpc_ns)
end

return M
