/*
 * listar-commits - Lista commits do sistema atual do mais novo para o mais antigo.
 * Uso: pp --listar-commits
 *
 * O sistema atual é o primeiro diretório abaixo da raiz SISC onde o pp está
 * instalado. Assim, mesmo executando em um subdiretório, a listagem considera
 * todo o sistema atual.
 */

#define _DEFAULT_SOURCE
#include <stdio.h>
#include "../restricao.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>


static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --listar-commits\n"
        "  pp --listar-commits --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Exibe os commits realizados no sistema atual, do mais novo para o mais antigo.\n"
        "\n"
        "Grupos de ajuda: uso, comportamento, saida, 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-commits --ajuda:saida\n"
        "\n"
        "2) Comportamento [comportamento sistema git]\n"
        "  Considera como sistema atual o primeiro diretório abaixo da raiz SISC.\n"
        "  Lista somente commits que alteraram arquivos desse sistema.\n"
        "  Usa a ordem padrão do git log: mais novo primeiro, mais antigo por último.\n"
        "\n"
        "3) Saída [saida formato]\n"
        "  Cada linha contém: hash curto, data ISO do commit, autor e mensagem.\n"
        "  Formato: <hash> <data> <autor> - <mensagem>\n"
        "\n"
        "4) Exemplos [exemplos]\n"
        "  pp --listar-commits\n",
        stdout);
}

static int ajuda_arg(const char *arg)
{
    return arg && (strcmp(arg, "--ajuda") == 0 || strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0);
}

static int caminho_sistema_atual(char *out, size_t out_tam, char *nome, size_t nome_tam)
{
    char raiz[PATH_MAX];
    char cwd[PATH_MAX];

    if (raiz_sisc_por_executavel(raiz, sizeof(raiz)) != 0) return -1;
    if (!getcwd(cwd, sizeof(cwd))) return -1;

    size_t rl = strlen(raiz);

    /* Caso especial: executando no diretório raiz do SISC */
    if (strcmp(cwd, raiz) == 0) {
        const char *base = strrchr(raiz, '/');
        base = base ? base + 1 : raiz;
        size_t bn = strlen(base);
        if (bn == 0 || bn + 1 > nome_tam) return -1;
        memcpy(nome, base, bn + 1);
        if (rl + 1 > out_tam) return -1;
        memcpy(out, raiz, rl + 1);
        return 0;
    }

    if (strncmp(cwd, raiz, rl) != 0 || cwd[rl] != '/') return -1;

    const char *ini = cwd + rl + 1;
    const char *fim = strchr(ini, '/');
    size_t n = fim ? (size_t)(fim - ini) : strlen(ini);
    if (n == 0 || n + 1 > nome_tam) return -1;

    memcpy(nome, ini, n);
    nome[n] = '\0';

    if (rl + 1 + n + 1 > out_tam) return -1;
    memcpy(out, raiz, rl);
    out[rl] = '/';
    memcpy(out + rl + 1, nome, n);
    out[rl + 1 + n] = '\0';
    return 0;
}

static int run_wait_silencioso(char *const argv[])
{
    pid_t pid = fork();
    if (pid < 0) return -1;

    if (pid == 0) {
        int fd = open("/dev/null", O_RDWR);
        if (fd >= 0) {
            dup2(fd, STDOUT_FILENO);
            dup2(fd, STDERR_FILENO);
            if (fd > STDERR_FILENO) close(fd);
        }
        execvp(argv[0], argv);
        _exit(127);
    }

    int status = 0;
    if (waitpid(pid, &status, 0) < 0) return -1;
    if (WIFEXITED(status)) return WEXITSTATUS(status);
    if (WIFSIGNALED(status)) return 128 + WTERMSIG(status);
    return -1;
}

static int run_capture_silencioso(char *const argv[], char *buf, size_t buf_tam)
{
    if (!buf || buf_tam == 0) return -1;
    buf[0] = '\0';

    int pipefd[2];
    if (pipe(pipefd) != 0) return -1;

    pid_t pid = fork();
    if (pid < 0) {
        close(pipefd[0]);
        close(pipefd[1]);
        return -1;
    }

    if (pid == 0) {
        close(pipefd[0]);
        dup2(pipefd[1], STDOUT_FILENO);
        if (pipefd[1] > STDERR_FILENO) close(pipefd[1]);
        int fd = open("/dev/null", O_WRONLY);
        if (fd >= 0) {
            dup2(fd, STDERR_FILENO);
            if (fd > STDERR_FILENO) close(fd);
        }
        execvp(argv[0], argv);
        _exit(127);
    }

    close(pipefd[1]);
    size_t len = 0;
    while (len + 1 < buf_tam) {
        ssize_t r = read(pipefd[0], buf + len, buf_tam - len - 1);
        if (r <= 0) break;
        len += (size_t)r;
    }
    buf[len] = '\0';

    /* Drena eventual excedente para o filho não bloquear se a saída passar do buffer. */
    char tmp[256];
    while (read(pipefd[0], tmp, sizeof(tmp)) > 0) {}
    close(pipefd[0]);

    int status = 0;
    if (waitpid(pid, &status, 0) < 0) return -1;
    if (WIFEXITED(status)) return WEXITSTATUS(status);
    if (WIFSIGNALED(status)) return 128 + WTERMSIG(status);
    return -1;
}

static long parse_long_trim(const char *s)
{
    while (s && *s && isspace((unsigned char)*s)) s++;
    if (!s || !*s) return 0;
    return strtol(s, NULL, 10);
}

int main(int argc, char *argv[])
{

    if (argc == 2 && ajuda_arg(argv[1])) {
        uso();
        return 0;
    }
    if (argc != 1) {
        fprintf(stderr, "listar-commits: este comando não recebe argumentos. Use: pp --listar-commits\n");
        return 1;
    }

    char sistema_path[PATH_MAX];
    char sistema_nome[PATH_MAX];
    if (caminho_sistema_atual(sistema_path, sizeof(sistema_path), sistema_nome, sizeof(sistema_nome)) != 0) {
        fprintf(stderr, "listar-commits: não foi possível detectar o sistema atual.\n");
        return 1;
    }

    char *check_args[] = { "git", "-C", sistema_path, "rev-parse", "--is-inside-work-tree", NULL };
    if (run_wait_silencioso(check_args) != 0) {
        fprintf(stderr, "listar-commits: o sistema atual '%s' não está em um repositório git.\n", sistema_nome);
        return 1;
    }

    char count_buf[128];
    char *count_args[] = { "git", "-C", sistema_path, "rev-list", "--count", "HEAD", "--", ".", NULL };
    int count_rc = run_capture_silencioso(count_args, count_buf, sizeof(count_buf));
    long total = (count_rc == 0) ? parse_long_trim(count_buf) : 0;
    if (total <= 0) {
        printf("Nenhum commit encontrado para o sistema atual '%s'.\n", sistema_nome);
        return 0;
    }

    char *log_args[] = {
        "git", "-C", sistema_path, "--no-pager", "log",
        "--date=iso-strict",
        "--pretty=tformat:%h %cI %an - %s",
        "--", ".",
        NULL
    };
    execvp(log_args[0], log_args);

    perror("listar-commits: git log");
    return 1;
}
