---library for `kpsewhich`
---@module kpathsea
---@copyright 2025
local kpse = require 'kpse'
local argparse = require 'argparse'
local semver = require 'semver'
local M = {}

---get parser
---@param progname string program name
---@return table parser
function M.get_parser(progname)
    local parser = argparse(progname):add_complete()
    parser:argument('file', 'file name'):args('*')
    parser:option('--progname', 'set program name', progname)
    parser:option('--help-formats -l', 'display information about all supported file formats by -l, -ll'):args(0):count(
        '*')
    parser:option('--expand-braces', 'output variable and brace expansion'):count('*')
    parser:option('--expand-path', 'output complete path expansion'):count('*')
    parser:option('--expand-var', 'output variable expansion'):count('*')
    parser:option('--var-value', 'output variable-expanded value of variable'):count('*')
    local names = {}
    for name, _ in pairs(M.formats) do
        table.insert(names, name)
    end
    for alias, _ in pairs(M.aliases) do
        table.insert(names, alias)
    end
    table.sort(names)
    parser:option('--show-path', 'output search path for file type'):count('*'):choices(names)
    parser:option('--version', 'display version information number and exit.'):args(0)
    parser:option('--debug -d', 'set debug flags'):args(0):count('*')
    return parser
end

---**entry for kpsewhich**
---@param args string[] command line arguments
function M.main(args)
    local parser = M.get_parser(args[0])
    args = parser:parse(args)
    if args.version then
        print(kpse.version())
        return
    elseif args.help_formats > 0 then
        if args.help_formats > 1 then
            kpse.set_program_name(args.progname)
        end
        local names = {}
        for name, _ in pairs(M.formats) do
            table.insert(names, name)
        end
        table.sort(names)
        for _, name in ipairs(names) do
            local format = M.formats[name]
            local aliases = { name }
            for k, v in pairs(M.aliases) do
                if v == name then
                    table.insert(aliases, k)
                end
            end
            print(table.concat(aliases, ', ') ..
                ': ' .. table.concat(format.vars, ', ') .. ': defined by ' .. (format.source or 'texmf.cnf'))
            if args.help_formats > 1 then
                if format.source then
                    print(kpse.default_texmfcnf())
                else
                    print(kpse.show_path(name))
                end
            end
        end
        return
    end
    kpse.set_program_name(args.progname)
    for _, v in ipairs(args.expand_braces or {}) do
        if args.debug == 0 then
            v = v .. ' -> ' .. kpse.expand_braces(v)
        end
        print(v)
    end
    for _, v in ipairs(args.expand_path or {}) do
        if args.debug == 0 then
            v = v .. ' -> ' .. kpse.expand_path(v)
        end
        print(v)
    end
    for _, v in ipairs(args.expand_var or {}) do
        if args.debug == 0 then
            v = v .. ' -> ' .. kpse.expand_var(v)
        end
        print(v)
    end
    for _, v in ipairs(args.var_value or {}) do
        if args.debug == 0 then
            v = v .. ' = ' .. kpse.var_value(v)
        end
        print(v)
    end
    for _, v in ipairs(args.show_path or {}) do
        v = M.aliases[v] or v
        if args.debug == 0 then
            v = v .. ': ' .. kpse.show_path(v)
        end
        print(v)
    end
    local err
    for k, v in ipairs(args.file) do
        if args.debug == 0 then
            v = kpse.lookup(v)
        end
        if v == nil then
            v = args.file[k] .. ' not found'
            err = true
        end
        print(v)
    end
    if err then
        os.exit(1)
    end
end

M.aliases = {
    bitmapfont = 'bitmap font',
    mpsupport = 'MetaPost support',
    source = 'TeX system sources',
    doc = 'TeX system documentation',
    trofffont = 'Troff fonts',
    dvipsconfig = 'dvips config',
    web2c = 'web2c files',
    othertext = 'other text files',
    otherbin = 'other binary files',
    miscfont = 'misc fonts',
    cmap = 'cmap files',
    pdftexconfig = 'pdftex config',
}

M.formats = {
    gf = {
        patterns = { '*.gf' },
        vars = { 'GFFONTS', 'GLYPHFONTS', 'TEXFONTS' }
    },
    pk = {
        patterns = { '*.pk' },
        vars = { 'PKFONTS', 'TEXPKS', 'GLYPHFONTS', 'TEXFONTS' }
    },
    ['bitmap font'] = {
        vars = { 'GLYPHFONTS', 'TEXFONTS' }
    },
    tfm = {
        patterns = { '*.tfm' },
        vars = { 'TFMFONTS', 'TEXFONTS' }
    },
    afm = {
        patterns = { '*.afm' },
        vars = { 'AFMFONTS', 'TEXFONTS' }
    },
    base = {
        patterns = { '*.base' },
        vars = { 'MFBASES', 'TEXMFINI' }
    },
    bib = {
        patterns = { '*.bib' },
        vars = { 'BIBINPUTS', 'TEXBIB' }
    },
    bst = {
        patterns = { '*.bst' },
        vars = { 'BSTINPUTS' }
    },
    cnf = {
        source = 'paths.h',
        patterns = { '*.cnf' },
        vars = { 'TEXMFCNF' }
    },
    ['ls-R'] = {
        patterns = { 'ls-R', 'ls-r' },
        vars = { 'TEXMFDBS' }
    },
    fmt = {
        patterns = { '*.fmt' },
        vars = { 'TEXFORMATS', 'TEXMFINI' }
    },
    map = {
        patterns = { '*.map' },
        vars = { 'TEXFONTMAPS', 'TEXFONTS' }
    },
    mem = {
        patterns = { '*.mem' },
        vars = { 'MPMEMS', 'TEXMFINI' }
    },
    mf = {
        patterns = { '*.mf' },
        vars = { 'MFINPUTS' }
    },
    mfpool = {
        patterns = { '*.pool' },
        vars = { 'MFPOOL', 'TEXMFINI' }
    },
    mft = {
        patterns = { '*.mft' },
        vars = { 'MFTINPUTS' }
    },
    mp = {
        patterns = { '*.mp' },
        vars = { 'MPINPUTS' }
    },
    mppool = {
        patterns = { '*.pool' },
        vars = { 'MPPOOL', 'TEXMFINI' }
    },
    ['MetaPost support'] = {
        vars = { 'MPSUPPORT' }
    },
    ocp = {
        patterns = { '*.ocp' },
        vars = { 'OCPINPUTS' }
    },
    ofm = {
        patterns = { '*.ofm' },
        vars = { 'OFMFONTS', 'TEXFONTS' }
    },
    opl = {
        patterns = { '*.opl', '*.pl' },
        vars = { 'OPLFONTS', 'TEXFONTS' }
    },
    otp = {
        patterns = { '*.otp' },
        vars = { 'OTPINPUTS' }
    },
    ovf = {
        patterns = { '*.ovf', '*.vf' },
        vars = { 'OVFFONTS', 'TEXFONTS' }
    },
    ovp = {
        patterns = { '*.ovp', '*.vpl' },
        vars = { 'OVPFONTS', 'TEXFONTS' }
    },
    ['graphic/figure'] = {
        patterns = { '*.eps', '*.epsi' },
        vars = { 'TEXPICTS', 'TEXINPUTS' }
    },
    tex = {
        patterns = { '*.tex', '*.sty', '*.cls', '*.fd', '*.aux', '.bbl', '.def', '.clo', '.ldf' },
        vars = { 'TEXINPUTS' }
    },
    ['TeX system documentation'] = {
        vars = { 'TEXDOCS' }
    },
    texpool = {
        patterns = { '*.pool' },
        vars = { 'TEXPOOL', 'TEXMFINI' }
    },
    ['TeX system sources'] = {
        patterns = { '*.dtx', '*.ins' },
        vars = { 'TEXSOURCES' }
    },
    ['PostScript header'] = {
        patterns = { '*.pro' },
        vars = { 'TEXPSHEADERS', 'PSHEADERS' }
    },
    ['Troff fonts'] = {
        vars = { 'TRFONTS' }
    },
    ['type1 fonts'] = {
        patterns = { '*.pfa', '*.pfb' },
        vars = { 'T1FONTS', 'T1INPUTS', 'TEXFONTS', 'TEXPSHEADERS', 'PSHEADERS' }
    },
    vf = {
        patterns = { '*.vf' },
        vars = { 'VFFONTS', 'TEXFONTS' }
    },
    ['dvips config'] = {
        vars = { 'TEXCONFIG' }
    },
    ist = {
        patterns = { '*.ist' },
        vars = { 'TEXINDEXSTYLE', 'INDEXSTYLE' }
    },
    ['truetype fonts'] = {
        patterns = { '*.ttf ', '*.ttc ', '*.TTF ', '*.TTC ', '*.dfont' },
        vars = { 'TTFONTS', 'TEXFONTS' }
    },
    ['type42 fonts'] = {
        patterns = { '*.t42 ', '*.T42 ' },
        vars = { 'T42FONTS', 'TEXFONTS' }
    },
    ['web2c files'] = {
        vars = { 'WEB2C' }
    },
    ['other text files'] = {
        vars = { '${PROGNAME}INPUTS' }
    },
    ['other binary files'] = {
        vars = { '${PROGNAME}INPUTS' }
    },
    ['misc fonts'] = {
        vars = { 'MISCFONTS', 'TEXFONTS' }
    },
    web = {
        patterns = { '*.web', '*.ch' },
        vars = { 'WEBINPUTS' }
    },
    cweb = {
        patterns = { '*.w', '*.web', '*.ch' },
        vars = { 'CWEBINPUTS' }
    },
    ['enc files'] = {
        patterns = { '*.enc' },
        vars = { 'ENCFONTS', 'TEXFONTS' }
    },
    ['cmap files'] = {
        vars = { 'CMAPFONTS', 'TEXFONTS' }
    },
    ['subfont definition files'] = {
        patterns = { '*.sfd' },
        vars = { 'SFDFONTS', 'TEXFONTS' }
    },
    ['opentype fonts'] = {
        patterns = { '*.otf', '*.OTF' },
        vars = { 'OPENTYPEFONTS', 'TEXFONTS' }
    },
    ['pdftex config'] = {
        vars = { 'PDFTEXCONFIG' }
    },
    ['lig files'] = {
        patterns = { '*.lig' },
        vars = { 'LIGFONTS', 'TEXFONTS' }
    },
    texmfscripts = {
        vars = { 'TEXMFSCRIPTS' }
    },
    lua = {
        patterns = { '*.lua', '*.luatex', '*.luc', '*.luctex', '*.texlua', '*.texluc', '*.tlu' },
        vars = { 'LUAINPUTS' }
    },
    ['font feature files'] = {
        patterns = { '*.fea' },
        vars = { 'FONTFEATURES' }
    },
    ['cid maps'] = {
        patterns = { '*.cid', '*.cidmap' },
        vars = { 'FONTCIDMAPS' }
    },
    mlbib = {
        patterns = { '*.mlbib', '*.bib' },
        vars = { 'MLBIBINPUTS', 'BIBINPUTS', 'TEXBIB' }
    },
    mlbst = {
        patterns = { '*.mlbst', '*.bst' },
        vars = { 'MLBSTINPUTS', 'BSTINPUTS' }
    },
    clua = {
        patterns = { '*.dll', '*.so' },
        vars = { 'CLUAINPUTS' }
    },
    ris = {
        patterns = { '*.ris' },
        vars = { 'RISINPUTS' }
    },
    bltxml = {
        patterns = { '*.bltxml' },
        vars = { 'BLTXMLINPUTS' }
    },
}
local v = semver(kpse.version():gsub(".* ", ''):gsub("/dev", ""))
if v < semver(6, 4, 0) then
    M.formats.ris = nil
    M.formats.bltxml = nil
end

return M
