-- Get input lines from stdin. Decorate each line, e.g., prepend a devicon to the filename in each
-- line or ANSI color part of texts. And output processed lines to stdout.

local devicons = require('nvim-web-devicons')
local color = require('rockyz.utils.color')

local source = vim.g.source
local cwd = vim.uv.cwd()

---@type table<string, string> A map from highlight group to ANSI color code
local cached_ansi = {}

local function ansi_string(string, hl)
    if not cached_ansi[hl] then
        cached_ansi[hl] = color.hl2ansi(hl)
    end
    return cached_ansi[hl] .. string .. '\x1b[m'
end

local function ansi_icon(filename)
    local ext = filename:match('^.+%.(.+)$')
    local icon, hl = devicons.get_icon(filename, ext, { default = true })
    return ansi_string(icon, hl)
end

local dotfile_changed

for line in io.lines() do
    local output_line
    if source == 'fd' then
        -- line is a filename
        -- output: <icon> <filename>\t<absolute_path>
        local icon = ansi_icon(line)
        output_line = icon .. ' ' .. line .. '\t' .. cwd .. '/' .. line
    elseif source == 'ls_gitfiles' then
        -- lines are output of ls-gitfiles
        -- (1) Some file changed, i.e., git status has output
        -- <status_code> <status_text>\t<filename> ---> <status_code> <icon>
        -- <status_text>\t<filename>
        -- (2) No file changed, i.e., git status has no output
        -- <filename>\t<filename> ---> <icon> <filename>\t<filename>
        local filename, filepath = unpack(vim.split(line, '\t'))
        local status_code = filename:match('^(%[.*%])')
        if status_code then
            filename = line:match('^%[.*%]%s(.*)\t')
            dotfile_changed = true
        else
            filename = vim.trim(filename)
        end
        local icon = ansi_icon(filepath)
        if not dotfile_changed then
            -- For (2)
            output_line = icon .. ' ' .. filename .. '\t' .. filepath
        else
            -- For (1)
            output_line = string.format(
                '%s %s %s\t%s',
                status_code and status_code or string.rep(' ', 4),
                icon,
                filename,
                filepath
            )
        end
    elseif source == 'git_status' then
        -- lines are generated by `git status --porcelain=v1`. Each lines has one of these formats:
        -- (1). XY FILENAME
        -- (2). XY OLD_FILENAME -> NEW_FILENAME
        -- output: [<staged><unstaged>] <git status text>\t<filename>
        local f1, f2 = line:sub(4):gsub([["]], ''), nil
        if f1:match('%s%->%s') then
            f1, f2 = f1:match('(.*)%s%->%s(.*)')
        end
        local icon_f1 = f1 and (ansi_icon(f1) .. ' ' .. f1)
        local icon_f2 = f2 and (ansi_icon(f2) .. ' ' .. f2)
        local staged = ansi_string(line:sub(1, 1), 'GitStatusStaged')
        local unstaged = ansi_string(line:sub(2, 2), 'GitStatusUnstaged')
        -- \t as the delimiter for the finder "git status"
        output_line = string.format('[%s%s]  %s\t%s', staged, unstaged, (f2 and ('%s -> %s'):format(icon_f1, icon_f2) or icon_f1), f2 or f1)
    end

    io.stdout:write(output_line .. '\n')
end
