/*
 * listar-comandos - Lista comandos disponíveis cujo nome contém um trecho.
 * Uso: pp --listar-comandos[=trecho]
 *
 * Sem trecho, lista todos os comandos em ordem alfabética.
 * Lista os executáveis no mesmo diretório deste comando (core/comandos/).
 * O diretório de execução continua sendo a raiz do sistema em construção;
 * este comando só usa o caminho do próprio executável para localizar seus pares.
 */

#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>


static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --listar-comandos[=<filtro>]\n"
        "  pp --listar-comandos <filtro>\n"
        "  pp --listar-comandos --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Lista comandos disponíveis no sistema atual, com filtro opcional por trecho.\n"
        "\n"
        "Grupos de ajuda: uso, filtro, comportamento, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>                Filtra um grupo. Ex: pp --listar-comandos --ajuda:filtro\n"
        "\n"
        "2) Filtro [filtro entrada]\n"
        "  =<filtro>                      Filtra pelo trecho informado\n"
        "  <filtro>                       Forma alternativa como argumento separado\n"
        "  Sem filtro                     Lista todos os itens\n"
        "  Exemplo com todas as flags do grupo:\n"
        "    pp --listar-comandos=agente  ou  pp --listar-comandos agente\n"
        "\n"
        "3) Comportamento [comportamento]\n"
        "  Ordena os resultados alfabeticamente. A busca por trecho é case-insensitive\n"
        "  quando suportada pelo comando.\n"
        "\n"
        "4) Exemplos [exemplos]\n"
        "  pp --listar-comandos\n"
        "  pp --listar-comandos=llm\n"
        "  pp --listar-comandos llm\n",
        stdout);
}

static int eh_executavel_regular(const char *caminho)
{
    struct stat st;
    if (stat(caminho, &st) != 0) return 0;
    if (!S_ISREG(st.st_mode)) return 0;
    return access(caminho, X_OK) == 0;
}

static int comparar_strings(const void *a, const void *b)
{
    const char *sa = *(const char *const *)a;
    const char *sb = *(const char *const *)b;
    return strcmp(sa, sb);
}

int main(int argc, char *argv[])
{
    if (argc > 2 || (argc == 2 && (strcmp(argv[1], "--ajuda") == 0 || strcmp(argv[1], "-h") == 0))) {
        uso();
        return argc == 2 ? 0 : 1;
    }

    const char *trecho = (argc == 2) ? argv[1] : "";

    char exe[PATH_MAX];
    ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
    if (n < 0) {
        perror("listar-comandos: readlink");
        return 1;
    }
    exe[n] = '\0';

    char *slash = strrchr(exe, '/');
    if (!slash) {
        fprintf(stderr, "listar-comandos: não foi possível localizar o diretório dos comandos.\n");
        return 1;
    }
    *slash = '\0';

    DIR *dir = opendir(exe);
    if (!dir) {
        perror("listar-comandos: opendir");
        return 1;
    }

    char **nomes = NULL;
    int total = 0, cap = 0;
    struct dirent *ent;
    while ((ent = readdir(dir)) != NULL) {
        const char *nome = ent->d_name;
        if (nome[0] == '.') continue;
        if (!strstr(nome, trecho)) continue;

        char caminho[PATH_MAX * 2];
        snprintf(caminho, sizeof(caminho), "%s/%s", exe, nome);
        if (!eh_executavel_regular(caminho)) continue;

        if (total >= cap) {
            cap = cap ? cap * 2 : 32;
            char **novo = realloc(nomes, (size_t)cap * sizeof(char *));
            if (!novo) {
                closedir(dir);
                for (int i = 0; i < total; i++) free(nomes[i]);
                free(nomes);
                fprintf(stderr, "listar-comandos: erro de memória.\n");
                return 1;
            }
            nomes = novo;
        }
        nomes[total] = strdup(nome);
        if (!nomes[total]) {
            closedir(dir);
            for (int i = 0; i < total; i++) free(nomes[i]);
            free(nomes);
            fprintf(stderr, "listar-comandos: erro de memória.\n");
            return 1;
        }
        total++;
    }
    closedir(dir);

    qsort(nomes, (size_t)total, sizeof(char *), comparar_strings);

    for (int i = 0; i < total; i++) {
        printf("%s\n", nomes[i]);
        free(nomes[i]);
    }
    free(nomes);

    return 0;
}
