--- @module "zync.api.commands.commands"
---
--- Zync API submodule providing CLI commands
---
local Commands = {}

--- Dependencies
local Command = require('zync.api.commands.Command')
local manager = require('zync.api.manager')

--- @type table<string, zync.api.commands.arg>
---
--- Zync command args
---
local command_args = {
    board = {
        flag = 'b',
        desc = 'App board',
        arg = 'board',
    },
    runner = {
        flag = 'r',
        desc = 'Board runner',
        arg = 'runner',
    },
    norebuild = {
        flag = 'n',
        desc = 'Skip rebuild when running simulator binary',
        commands = { 'run' },
    },
    image = {
        flag = 'i',
        desc = 'Project image to flash',
        arg = 'image',
        commands = { 'flash', 'run' },
    },
    list = {
        flag = 'l',
        desc = 'List project images',
        commands = { 'flash', 'run' },
    },
    ['  '] = {
        desc = 'Additional args',
        arg = 'additional args',
        nargs = '*',
        commands = { 'conf', 'build', 'flash', 'run' },
    },
}

--- @class zync.api.commands.metadata
---
--- Zync command metadata
---
--- @field short string     Command shorthand
--- @field summary string   Command summary
--- @field desc string      Command description
--- @field handler function Command runtime handler function

--- Constructs command metadata
---
--- @param short string     Command shorthand
--- @param summary string   Command summary
--- @param desc string      Command description
--- @param handler function Command runtime handler function
---
--- @return zync.api.commands.metadata Constructed command metadata
---
local function cmd(short, summary, desc, handler)
    return {
        short = short,
        summary = summary,
        desc = desc,
        handler = handler,
    }
end

--- @type table<zync.api.commands.metadata>
---
--- Table of metadata for zync commands
---
local command_setup = {
    -- stylua: ignore start
    init =      cmd('i',    'Init zync config.',    'Initialize zync config.',              manager.init),
    conf =      cmd('cf',   'Configure app.',       'Configure app using CMake.',           manager.configure),
    build =     cmd('b',    'Build app.',           'Build app using Ninja.',               manager.build),
    flash =     cmd('f',    'Flash app.',           'Flash app using configured runner.',   manager.flash),
    run =       cmd('r',    'Run app.',             'Run app simulation.',                  manager.run),
    clean =     cmd('c',    'Clean build dir.',     'Cleans entire root build directory.',  manager.clean),
    boards =    cmd('bo',   'List boards.',         'List available Zephyr boards.',        manager.boards),
    runners =   cmd('rn',   'List runners.',        'List available Zephyr runners.',       manager.runners),
    profiles =  cmd('p',    'List profiles.',       'List configured profiles.',            manager.profiles),
    -- stylua: ignore end
}

--- Builds zync commands
---
--- @param parser any   Argparse parser object
---
--- @return table<string, zync.api.commands.Command> Table of zync commands
---
function Commands.build(parser)
    -- sort zync commands
    local sorted = {}
    for command, opts in pairs(command_setup) do
        table.insert(sorted, { cmd = command, opts = opts })
    end
    table.sort(sorted, function(a, b)
        return a.cmd < b.cmd
    end)

    -- construct command objects and set up their args
    local commands = {}
    for _, command in ipairs(sorted) do
        commands[command.cmd] = Command:new(
            parser,
            command.cmd,
            command.opts.short,
            command.opts.summary,
            command.opts.desc,
            command.opts.handler
        )
        commands[command.cmd]:args(command_args)
    end

    return commands
end

return Commands
