/*
 * divergencia-sistema - audita divergencias entre o sistema atual e a árvore-base: arvores/operador-manual-funcional.json
 * Uso: pp --divergencia-sistema [--sem-llm] [--rel] [--nivel=0..3]
 */
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <dirent.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>
#include "../restricao.h"

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

#define MAX_ARVORE_BYTES   (320 * 1024)
#define MAX_MAPA_BYTES     (320 * 1024)
#define MAX_ARQUIVO_BYTES  (16 * 1024)
#define MAX_DOSSIE_BYTES   (420 * 1024)
#define MAX_SA_CTX_VALIDAR_BYTES (80 * 1024)
#define MAX_SA_CTX_INDICE_BYTES  (120 * 1024)
#define MAX_SA_CTX_EXTRAIR_BYTES (180 * 1024)
#define MAX_LISTAGEM       2500
#define MAX_CONTEUDOS      40

typedef struct { char *buf; size_t len, cap; } Str;

static void str_init(Str *s) { s->cap = 8192; s->len = 0; s->buf = malloc(s->cap); if (s->buf) s->buf[0] = '\0'; }
static int str_reserve(Str *s, size_t extra) { if (!s || !s->buf) return -1; while (s->len + extra + 1 > s->cap) { s->cap *= 2; char *nb = realloc(s->buf, s->cap); if (!nb) return -1; s->buf = nb; } return 0; }
static int str_addn(Str *s, const char *txt, size_t n) { if (!txt || n == 0) return 0; if (str_reserve(s, n) != 0) return -1; memcpy(s->buf + s->len, txt, n); s->len += n; s->buf[s->len] = '\0'; return 0; }
static int str_add(Str *s, const char *txt) { return str_addn(s, txt ? txt : "", strlen(txt ? txt : "")); }
static int str_addf(Str *s, const char *fmt, ...) { va_list ap, ap2; va_start(ap, fmt); va_copy(ap2, ap); int n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (n < 0) { va_end(ap2); return -1; } char *tmp = malloc((size_t)n + 1); if (!tmp) { va_end(ap2); return -1; } vsnprintf(tmp, (size_t)n + 1, fmt, ap2); va_end(ap2); int r = str_add(s, tmp); free(tmp); return r; }

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --divergencia-sistema [opcoes]\n"
        "  pp --divergencia-sistema --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Analisa profundamente o sistema atual e compara sua posição real com o\n"
        "  contexto SA do sistema em arvores/*.json, incluindo a árvore-base\n"
        "  arvores/operador-manual-funcional.json quando existir.\n"
        "  As árvores devem refletir o sistema: atores, processos, artefatos,\n"
        "  contratos, integrações e arquivos reais apontados por ra.\n"
        "  O comando usa sa-ctx para validar árvores e incluir evidências de ra/código,\n"
        "  monta dossiê/prompt apenas em memória e materializa somente a árvore SA final.\n"
        "  Por padrão, chama a LLM via parallm --sem-heb para produzir parecer técnico.\n"
        "\n"
        "Grupos de ajuda: uso, comportamento, opcoes, arquivos, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>                Filtra ajuda pelo pp. Ex: --ajuda:opcoes\n"
        "\n"
        "2) Comportamento [comportamento]\n"
        "  Usa o diretório atual como sistema atual. Se arvores/operador-manual-funcional.json existir,\n"
        "  ela entra como base histórica; caso contrário, audita as demais árvores SA disponíveis.\n"
        "  Não faz leitura superficial: inclui validação sa-ctx, índice das árvores, extração com ra,\n"
        "  mapa funcional quando presente, listagem profunda, agentes, conectores,\n"
        "  web-api, specs, configs, testes e artefatos textuais/código.\n"
        "  A política de exclusão de contexto vem exclusivamente de sisc/.ignorecontexto.\n"
        "\n"
        "3) Opções [opcoes]\n"
        "  --sem-llm                      Não chama LLM; gera apenas árvore SA mínima de saída\n"
        "  --rel                          Repassa --rel ao parallm\n"
        "  --nivel=0..3                   Nível de raciocínio da LLM; padrão 3\n"
        "\n"
        "4) Arquivos [arquivos saida]\n"
        "  Único artefato persistido: arvores/divergencias.json\n"
        "  O operador interage somente por árvore SA; dossiê, prompt e resposta não são salvos.\n"
        "\n"
        "5) Exemplos [exemplos]\n"
        "  pp --divergencia-sistema\n"
        "  pp --divergencia-sistema --sem-llm\n"
        "  pp --divergencia-sistema --rel --nivel=3\n",
        stdout);
}

static int eh_dir(const char *p) { struct stat st; return p && stat(p, &st) == 0 && S_ISDIR(st.st_mode); }
static int eh_arq(const char *p) { struct stat st; return p && stat(p, &st) == 0 && S_ISREG(st.st_mode); }
static long tam_arq(const char *p) { struct stat st; return p && stat(p, &st) == 0 && S_ISREG(st.st_mode) ? (long)st.st_size : -1; }
static const char *base_name(const char *p) { const char *s = p ? strrchr(p, '/') : NULL; return s ? s + 1 : (p ? p : ""); }

static char *ler_arquivo_limite(const char *path, size_t max, size_t *lidos, int *trunc)
{
    if (lidos) *lidos = 0;
    if (trunc) *trunc = 0;
    FILE *f = fopen(path, "rb"); if (!f) return NULL;
    if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; }
    long tam = ftell(f); if (tam < 0) { fclose(f); return NULL; } rewind(f);
    size_t nler = (size_t)tam; if (nler > max) { nler = max; if (trunc) *trunc = 1; }
    char *buf = malloc(nler + 1); if (!buf) { fclose(f); return NULL; }
    size_t n = fread(buf, 1, nler, f); int erro = ferror(f); fclose(f); if (erro) { free(buf); return NULL; }
    buf[n] = '\0'; if (lidos) *lidos = n; if ((size_t)tam > n && trunc) *trunc = 1; return buf;
}

static const char *extensao(const char *path) { const char *b = base_name(path); const char *d = strrchr(b, '.'); return d && d != b ? d + 1 : ""; }
static int ext_textual(const char *path)
{
    const char *e = extensao(path); if (!*e) return 1;
    const char *ok[] = {"json","md","txt","html","htm","css","js","ts","php","c","h","sql","yaml","yml","xml","csv","ini","conf","env","sh","mk"};
    for (size_t i = 0; i < sizeof(ok)/sizeof(ok[0]); i++) if (!strcasecmp(e, ok[i])) return 1;
    return 0;
}

static char **ctx_ignore = NULL;
static int ctx_ignore_count = 0;

static void ctx_carregar_ignore_unico(void)
{
    char raiz_sisc[PATH_MAX];
    if (raiz_sisc_por_executavel(raiz_sisc, sizeof(raiz_sisc)) != 0) return;
    char path[PATH_MAX * 2];
    snprintf(path, sizeof(path), "%s/.ignorecontexto", raiz_sisc);
    size_t l = 0; int tr = 0;
    char *txt = ler_arquivo_limite(path, 128 * 1024, &l, &tr);
    (void)tr;
    if (!txt) return;
    int cap = 32;
    ctx_ignore = calloc((size_t)cap, sizeof(char *));
    if (!ctx_ignore) { free(txt); return; }
    char *save = NULL;
    char *linha = strtok_r(txt, "\n", &save);
    while (linha) {
        while (*linha == ' ' || *linha == '\t') linha++;
        char *fim = linha + strlen(linha);
        while (fim > linha && (fim[-1] == ' ' || fim[-1] == '\t' || fim[-1] == '\r')) *--fim = '\0';
        if (*linha && *linha != '#') {
            if (ctx_ignore_count >= cap) {
                cap *= 2;
                char **nx = realloc(ctx_ignore, (size_t)cap * sizeof(char *));
                if (!nx) break;
                ctx_ignore = nx;
            }
            ctx_ignore[ctx_ignore_count++] = strdup(linha);
        }
        linha = strtok_r(NULL, "\n", &save);
    }
    free(txt);
}

static void ctx_liberar_ignore(void)
{
    for (int i = 0; i < ctx_ignore_count; i++) free(ctx_ignore[i]);
    free(ctx_ignore); ctx_ignore = NULL; ctx_ignore_count = 0;
}

static int ctx_deve_ignorar_path(const char *path, int eh_dir)
{
    if (!path) return 0;
    size_t len = strlen(path);
    for (int i = 0; i < ctx_ignore_count; i++) {
        const char *pat = ctx_ignore[i];
        if (!pat || !*pat) continue;
        if (pat[0] == '!') {
            if (!eh_dir && strcmp(pat, "!sem-extensao") == 0) {
                const char *b = base_name(path);
                if (b && *b && !strchr(b, '.')) return 1;
            }
            continue;
        }
        size_t lp = strlen(pat);
        if (lp <= len) {
            for (size_t p = 0; p + lp <= len; p++) if (strncmp(path + p, pat, lp) == 0) return 1;
        }
        if (eh_dir && lp >= 2 && pat[0] == '/' && pat[lp - 1] == '/') {
            size_t core = lp - 1;
            if (len >= core && strncmp(path + len - core, pat, core) == 0) return 1;
        }
    }
    return 0;
}

static int ignorar_nome(const char *nome)
{
    return !nome || nome[0] == '.';
}

static void add_listagem_rec(Str *d, const char *abs, const char *rel, int prof, int *total)
{
    if (!d || !abs || !rel || !total || *total >= MAX_LISTAGEM || prof > 12) return;
    DIR *dir = opendir(abs); if (!dir) return;
    struct dirent *e;
    while ((e = readdir(dir)) && *total < MAX_LISTAGEM) {
        if (!strcmp(e->d_name,".") || !strcmp(e->d_name,"..") || ignorar_nome(e->d_name)) continue;
        char pabs[PATH_MAX*2], prel[PATH_MAX*2]; snprintf(pabs,sizeof(pabs),"%s/%s",abs,e->d_name); snprintf(prel,sizeof(prel),"%s/%s",rel,e->d_name);
        struct stat st; if (stat(pabs,&st)!=0) continue;
        if (ctx_deve_ignorar_path(pabs, S_ISDIR(st.st_mode))) continue;
        if (S_ISDIR(st.st_mode)) { str_addf(d,"- %s/\n",prel); (*total)++; add_listagem_rec(d,pabs,prel,prof+1,total); }
        else if (S_ISREG(st.st_mode)) { str_addf(d,"- %s (%ld bytes)\n",prel,(long)st.st_size); (*total)++; }
    }
    closedir(dir);
}

static void add_bloco_arquivo(Str *d, const char *abs, const char *rel, int *conteudos)
{
    if (!d || !abs || !rel || !conteudos || *conteudos >= MAX_CONTEUDOS || d->len >= MAX_DOSSIE_BYTES) return;
    if (!ext_textual(rel)) return;
    size_t lidos=0; int trunc=0; char *txt = ler_arquivo_limite(abs, MAX_ARQUIVO_BYTES, &lidos, &trunc); if (!txt) return;
    if (memchr(txt, '\0', lidos)) { free(txt); return; }
    size_t disponivel = d->len < MAX_DOSSIE_BYTES ? MAX_DOSSIE_BYTES - d->len : 0; if (disponivel < 1024) { free(txt); return; }
    size_t usar = lidos; if (usar + 512 > disponivel) { usar = disponivel - 512; trunc = 1; }
    str_addf(d,"\n### %s%s\n\n```%s\n", rel, trunc ? " (truncado)" : "", *extensao(rel) ? extensao(rel) : "text");
    str_addn(d, txt, usar); if (usar == 0 || txt[usar-1] != '\n') str_add(d,"\n"); str_add(d,"```\n");
    (*conteudos)++; free(txt);
}

static void add_conteudos_dir(Str *d, const char *abs, const char *rel, int prof, int *conteudos)
{
    if (!d || !abs || !rel || !conteudos || *conteudos >= MAX_CONTEUDOS || prof > 8 || d->len >= MAX_DOSSIE_BYTES) return;
    DIR *dir = opendir(abs); if (!dir) return;
    struct dirent *e;
    while ((e = readdir(dir)) && *conteudos < MAX_CONTEUDOS && d->len < MAX_DOSSIE_BYTES) {
        if (!strcmp(e->d_name,".") || !strcmp(e->d_name,"..") || ignorar_nome(e->d_name)) continue;
        char pabs[PATH_MAX*2], prel[PATH_MAX*2]; snprintf(pabs,sizeof(pabs),"%s/%s",abs,e->d_name); snprintf(prel,sizeof(prel),"%s/%s",rel,e->d_name);
        struct stat st; if (stat(pabs,&st)!=0) continue;
        if (ctx_deve_ignorar_path(pabs, S_ISDIR(st.st_mode))) continue;
        if (S_ISDIR(st.st_mode)) add_conteudos_dir(d,pabs,prel,prof+1,conteudos);
        else if (S_ISREG(st.st_mode)) add_bloco_arquivo(d,pabs,prel,conteudos);
    }
    closedir(dir);
}

static int nome_relevante_ausente(const char *tree, const char *nome)
{
    if (!tree || !nome || !*nome) return 0;
    if (strstr(tree, nome)) return 0;
    char stem[PATH_MAX]; snprintf(stem, sizeof(stem), "%s", nome); char *dot = strrchr(stem, '.'); if (dot) *dot = '\0';
    return stem[0] && !strstr(tree, stem);
}

static void add_cheques_objetivos_arvore(Str *d, const char *raiz, const char *sistema, const char *nome_arvore, const char *tree)
{
    int achou = 0;
    str_addf(d, "### Checagens contra `%s`\n\n", nome_arvore ? nome_arvore : "árvore");
    const char *dirs[] = {"agentes","conectores","web-api","api.php","config","spec-diretivas","formatos-escuta","escuta","public","testes"};
    for (size_t i=0;i<sizeof(dirs)/sizeof(dirs[0]);i++) {
        char p[PATH_MAX*2]; snprintf(p,sizeof(p),"%s/%s",raiz,dirs[i]);
        if ((eh_dir(p) || eh_arq(p)) && nome_relevante_ausente(tree, dirs[i])) { str_addf(d,"- POSSÍVEL DIVERGÊNCIA: `%s` existe no sistema, mas não aparece claramente em %s.\n", dirs[i], nome_arvore ? nome_arvore : "árvore"); achou++; }
    }
    char agdir[PATH_MAX*2]; snprintf(agdir,sizeof(agdir),"%s/agentes",raiz);
    DIR *dir = opendir(agdir);
    if (dir) {
        struct dirent *e; while ((e=readdir(dir))) {
            if (e->d_name[0]=='.' || !strstr(e->d_name,".json")) continue;
            if (nome_relevante_ausente(tree, e->d_name)) { str_addf(d,"- POSSÍVEL DIVERGÊNCIA: agente `%s` existe em agentes/, mas não aparece claramente em %s.\n", e->d_name, nome_arvore ? nome_arvore : "árvore"); achou++; }
        }
        closedir(dir);
    }
    if (!achou) str_add(d,"- Nenhuma divergência objetiva simples encontrada por nomes diretos nesta árvore. A auditoria profunda ainda deve comparar responsabilidades, artefatos e contratos.\n");
    str_add(d,"\n");
    (void)sistema;
}

static void add_cheques_objetivos(Str *d, const char *raiz, const char *sistema, const char *tree_sistema)
{
    str_add(d, "## Checagens objetivas iniciais\n\n");
    char nome_sistema[PATH_MAX]; snprintf(nome_sistema, sizeof(nome_sistema), "arvores/operador-manual-funcional.json");
    add_cheques_objetivos_arvore(d, raiz, sistema, nome_sistema, tree_sistema);
}

static int caminho_sa_ctx(char *out, size_t tam)
{
    char exe[PATH_MAX];
    ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
    if (n <= 0) return -1;
    exe[n] = '\0';
    char *slash = strrchr(exe, '/');
    if (!slash) return -1;
    strcpy(slash + 1, "sa-ctx");
    return snprintf(out, tam, "%s", exe) < (int)tam && access(exe, X_OK) == 0 ? 0 : -1;
}

static int capturar_exec(char *const args[], size_t max_bytes, char **saida_out, int *trunc_out)
{
    if (saida_out) *saida_out = NULL;
    if (trunc_out) *trunc_out = 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);
        dup2(pipefd[1], STDERR_FILENO);
        close(pipefd[1]);
        execv(args[0], args);
        perror("divergencia-sistema: execv");
        _exit(127);
    }
    close(pipefd[1]);
    size_t cap = max_bytes + 1;
    if (cap < 8192) cap = 8192;
    char *buf = malloc(cap);
    if (!buf) { close(pipefd[0]); waitpid(pid, NULL, 0); return 1; }
    size_t len = 0;
    char tmp[4096];
    ssize_t r;
    while ((r = read(pipefd[0], tmp, sizeof(tmp))) > 0) {
        size_t rr = (size_t)r;
        if (len + rr <= max_bytes) {
            memcpy(buf + len, tmp, rr);
            len += rr;
        } else {
            size_t take = len < max_bytes ? max_bytes - len : 0;
            if (take > 0) { memcpy(buf + len, tmp, take); len += take; }
            if (trunc_out) *trunc_out = 1;
        }
    }
    close(pipefd[0]);
    buf[len] = '\0';
    int st = 0;
    waitpid(pid, &st, 0);
    if (saida_out) *saida_out = buf; else free(buf);
    if (WIFEXITED(st)) return WEXITSTATUS(st);
    if (WIFSIGNALED(st)) return 128 + WTERMSIG(st);
    return 1;
}

static void add_bloco_comando(Str *d, const char *titulo, char *const args[], size_t max_bytes)
{
    if (!d || d->len >= MAX_DOSSIE_BYTES) return;
    char *saida = NULL;
    int trunc = 0;
    int rc = capturar_exec(args, max_bytes, &saida, &trunc);
    if (!saida) return;
    size_t disponivel = d->len < MAX_DOSSIE_BYTES ? MAX_DOSSIE_BYTES - d->len : 0;
    if (disponivel > 2048) {
        str_addf(d, "\n## %s\n\nComando interno: `", titulo ? titulo : "comando");
        for (int i = 0; args[i]; i++) str_addf(d, "%s%s", i ? " " : "", base_name(args[i]));
        str_addf(d, "` — rc=%d%s\n\n```text\n", rc, trunc ? " — saída truncada" : "");
        size_t usar = strlen(saida);
        disponivel = d->len < MAX_DOSSIE_BYTES ? MAX_DOSSIE_BYTES - d->len : 0;
        if (usar + 32 > disponivel) { usar = disponivel > 32 ? disponivel - 32 : 0; trunc = 1; }
        str_addn(d, saida, usar);
        if (usar == 0 || saida[usar - 1] != '\n') str_add(d, "\n");
        if (trunc) str_add(d, "[saída truncada por limite do dossiê]\n");
        str_add(d, "```\n");
    }
    free(saida);
}

static void add_sa_ctx_dossie(Str *d)
{
    char sa_ctx[PATH_MAX];
    if (caminho_sa_ctx(sa_ctx, sizeof(sa_ctx)) != 0) {
        str_add(d, "\n## sa-ctx indisponível\n\n- O binário `sa-ctx` não foi encontrado; auditoria segue sem validação estruturada de árvores/ra.\n");
        return;
    }

    char *fontes[] = { sa_ctx, "--ctx-fontes", "--origem=auto", NULL };
    add_bloco_comando(d, "Fontes normalizadas de contexto SA", fontes, 64 * 1024);

    char *validar[] = { sa_ctx, "--ctx-validar", "--origem=auto", NULL };
    add_bloco_comando(d, "Validação estrutural via sa-ctx (todas as fontes)", validar, MAX_SA_CTX_VALIDAR_BYTES);

    char *indice[] = { sa_ctx, "--ctx-indice", "--origem=sistema", NULL };
    add_bloco_comando(d, "Índice das árvores SA do sistema alvo", indice, MAX_SA_CTX_INDICE_BYTES);

    char consulta[] = "--ctx-extrair=auditoria divergencia sistema contexto design processo agente conector web api contrato requisito ra codigo evidencia";
    char max_bytes_arg[64], max_itens_arg[64];
    snprintf(max_bytes_arg, sizeof(max_bytes_arg), "--max-bytes=%d", MAX_SA_CTX_EXTRAIR_BYTES);
    snprintf(max_itens_arg, sizeof(max_itens_arg), "--max-itens=%d", 120);
    char *extrair[] = { sa_ctx, consulta, "--origem=sistema", max_bytes_arg, max_itens_arg, "--include-ra", NULL };
    add_bloco_comando(d, "Extração híbrida sa-ctx com evidências ra/código", extrair, MAX_SA_CTX_EXTRAIR_BYTES + 8192);
}

static char *montar_dossie(const char *raiz, const char *sistema, const char *tree_abs, const char *tree_json, int arv_trunc)
{
    Str d; str_init(&d); if (!d.buf) return NULL;
    str_addf(&d,"# Dossiê de divergência sistema x contexto SA/código\n\nSistema atual: `%s`\nRaiz: `%s`\nÁrvores do sistema: `<sistema>/arvores/*.json`\nMapa funcional histórico: `arvores/operador-manual-funcional.json` quando existir\n\n", sistema, raiz);
    str_add(&d,"## Critério de auditoria\n\nAs árvores SA do sistema devem refletir a posição real do sistema: atores, processos, contratos, artefatos, integrações, arquivos relevantes e governança. A auditoria deve comparar descrições das árvores, referências `ra` e código/arquivos reais. Divergência não é só arquivo ausente: também inclui responsabilidade descrita em árvore sem evidência no sistema, componente existente sem representação em árvore, `ra` quebrado ou incompatível, processo implementado fora do contrato, documentação desatualizada e ordem/processo incompatível.\n\n");

    add_sa_ctx_dossie(&d);

    str_add(&d,"\n## Mapa funcional histórico\n\n");
    long tree_tam = tam_arq(tree_abs);
    if (tree_tam >= 0) {
        str_addf(&d,"Arquivo: `arvores/operador-manual-funcional.json` (%ld bytes)\n\n```json\n", tree_tam);
        str_add(&d, tree_json ? tree_json : ""); if (tree_json && tree_json[0] && tree_json[strlen(tree_json)-1] != '\n') str_add(&d,"\n");
        if (arv_trunc) str_add(&d,"\n/* mapa funcional truncado por limite do dossiê */\n");
        str_add(&d,"```\n\n");
    } else {
        str_add(&d,"Arquivo `arvores/operador-manual-funcional.json` ausente. A auditoria deve usar as demais árvores SA do sistema e apontar a ausência da árvore-base se ela for obrigatória para este sistema.\n\n");
    }

    add_cheques_objetivos(&d, raiz, sistema, tree_json);

    str_add(&d,"## Listagem profunda do sistema atual\n\n"); int total=0; add_listagem_rec(&d, raiz, sistema, 0, &total); if (total>=MAX_LISTAGEM) str_add(&d,"- ... listagem truncada por limite de itens\n");

    str_add(&d,"\n## Conteúdos relevantes do sistema atual\n"); int conteudos=0;
    const char *prioritarios[] = {"agentes","conectores","web-api","config","spec-diretivas","formatos-escuta","escuta","public","testes","arvores"};
    for (size_t i=0;i<sizeof(prioritarios)/sizeof(prioritarios[0]) && conteudos<MAX_CONTEUDOS;i++) {
        char abs[PATH_MAX*2], rel[PATH_MAX*2]; snprintf(abs,sizeof(abs),"%s/%s",raiz,prioritarios[i]); snprintf(rel,sizeof(rel),"%s/%s",sistema,prioritarios[i]);
        if (eh_dir(abs)) add_conteudos_dir(&d,abs,rel,0,&conteudos); else if (eh_arq(abs)) add_bloco_arquivo(&d,abs,rel,&conteudos);
    }
    const char *files[] = {"sisc-api-cliente.php","Makefile","heb.txt"};
    for (size_t i=0;i<sizeof(files)/sizeof(files[0]) && conteudos<MAX_CONTEUDOS;i++) { char abs[PATH_MAX*2], rel[PATH_MAX*2]; snprintf(abs,sizeof(abs),"%s/%s",raiz,files[i]); snprintf(rel,sizeof(rel),"%s/%s",sistema,files[i]); if (eh_arq(abs)) add_bloco_arquivo(&d,abs,rel,&conteudos); }
    if (d.len >= MAX_DOSSIE_BYTES) str_add(&d,"\n[Dossiê truncado por limite de tamanho.]\n");
    return d.buf;
}

static char *montar_prompt(const char *sistema, const char *dossie)
{
    Str p; str_init(&p); if (!p.buf) return NULL;
    str_add(&p,"Você é um auditor técnico sênior do SISC. Sua tarefa é encontrar divergências profundas entre a posição real do sistema, suas árvores SA de contexto e os arquivos/código reais apontados por ra.\n\n");
    str_addf(&p,"Sistema auditado: `%s`. Fontes esperadas: `<sistema>/arvores/*.json`, `ra` dos itens SA e código/artefatos reais do sistema.\n\n", sistema);
    str_add(&p,"IMPORTANTE: faça análise profunda. Não se limite a nomes de arquivos. Compare responsabilidades, atores, processos, etapas, artefatos gerados, contratos, integrações, governança, testes, documentação, conteúdo dos arquivos ra e código real. As árvores SA devem ser reflexo fiel e atualizado da posição atual do sistema. Aponte divergências sistema x árvores SA x evidências ra/código.\n\n");
    str_add(&p,"Responda em português, Markdown estruturado. O comando converterá esta resposta para uma árvore --sa para revisão do operador. Use este formato obrigatório:\n\n");
    str_add(&p,"# Divergência sistema x árvore\n\n## 1. Status geral\n- Status: aderente | parcialmente aderente | divergente | informação insuficiente\n- Risco: baixo | médio | alto\n\n## 2. Resumo executivo\nExplique a aderência geral.\n\n## 3. Divergências encontradas\nPara cada divergência use:\n- Tipo: sistema sem árvore | árvore sem evidência no sistema | descrição desatualizada | contrato/processo divergente | artefato ausente | inconsistência entre árvores | outro\n- Árvore afetada: caminho da árvore SA afetada\n- Item/área da árvore afetada, com ID se disponível\n- Evidência no sistema/código/arquivo ra\n- Evidência na árvore\n- Impacto\n- Correção recomendada\n\n## 4. Pontos aderentes importantes\nListe evidências de alinhamento relevantes.\n\n## 5. Prioridade de correção\nListe ações em ordem: crítica, alta, média, baixa.\n\n## 6. Sugestões de atualização da árvore\nSugira GCs manuais para atualizar a árvore, com --em, --itens e --ra quando possível.\n\n=== DOSSIÊ ===\n");
    str_add(&p,dossie?dossie:""); str_add(&p,"\n=== FIM DO DOSSIÊ ===\n"); return p.buf;
}

static int caminho_parallm(char *out, size_t tam)
{
    char exe[PATH_MAX]; ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe)-1); if (n<=0) return -1; exe[n]='\0'; char *slash=strrchr(exe,'/'); if(!slash) return -1; strcpy(slash+1,"parallm"); snprintf(out,tam,"%s",exe); return 0;
}
static int executar_parallm(const char *prompt, int nivel, int relatorio, char **saida_out)
{
    if (saida_out) *saida_out = NULL;
    char parallm[PATH_MAX];
    if (caminho_parallm(parallm, sizeof(parallm)) != 0) return 1;

    int outpipe[2];
    int inpipe[2];
    if (pipe(outpipe) != 0) return 1;
    if (pipe(inpipe) != 0) { close(outpipe[0]); close(outpipe[1]); return 1; }

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

    if (pid == 0) {
        close(outpipe[0]);
        close(inpipe[1]);
        dup2(outpipe[1], STDOUT_FILENO);
        dup2(outpipe[1], STDERR_FILENO);
        dup2(inpipe[0], STDIN_FILENO);
        close(outpipe[1]);
        close(inpipe[0]);
        char nivel_arg[32];
        snprintf(nivel_arg, sizeof(nivel_arg), "--nivel=%d", nivel);
        char *args[6];
        int pos = 0;
        args[pos++] = parallm;
        args[pos++] = "--sem-heb";
        args[pos++] = nivel_arg;
        if (relatorio) args[pos++] = "--rel";
        args[pos] = NULL;
        execv(parallm, args);
        perror("divergencia-sistema: execv parallm");
        _exit(127);
    }

    close(outpipe[1]);
    close(inpipe[0]);

    const char *p = prompt ? prompt : "";
    size_t total = strlen(p), escrito = 0;
    while (escrito < total) {
        ssize_t w = write(inpipe[1], p + escrito, total - escrito);
        if (w <= 0) break;
        escrito += (size_t)w;
    }
    close(inpipe[1]);

    size_t cap = 65536, len = 0;
    char *buf = malloc(cap);
    if (!buf) { close(outpipe[0]); waitpid(pid, NULL, 0); return 1; }
    char tmp[4096];
    ssize_t r;
    while ((r = read(outpipe[0], tmp, sizeof(tmp))) > 0) {
        if (len + (size_t)r + 1 > cap) {
            while (len + (size_t)r + 1 > cap) cap *= 2;
            char *nb = realloc(buf, cap);
            if (!nb) { free(buf); close(outpipe[0]); waitpid(pid, NULL, 0); return 1; }
            buf = nb;
        }
        memcpy(buf + len, tmp, (size_t)r);
        len += (size_t)r;
    }
    close(outpipe[0]);
    buf[len] = '\0';
    int st = 0;
    waitpid(pid, &st, 0);
    if (saida_out) *saida_out = buf; else free(buf);
    if (WIFEXITED(st)) return WEXITSTATUS(st);
    if (WIFSIGNALED(st)) return 128 + WTERMSIG(st);
    return 1;
}

static int caminho_pp_instalado(char *out, size_t tam)
{
    char exe[PATH_MAX]; ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe)-1); if(n<=0) return -1; exe[n]='\0';
    char *slash=strrchr(exe,'/'); if(!slash) return -1; *slash='\0';
    slash=strrchr(exe,'/'); if(!slash) return -1; *slash='\0';
    slash=strrchr(exe,'/'); if(!slash) return -1; *slash='\0';
    return snprintf(out,tam,"%s/pp",exe) < (int)tam ? 0 : -1;
}
static char *sa_arg(const char *pref, const char *valor)
{ size_t n=strlen(pref)+strlen(valor?valor:"")+1; char*out=malloc(n); if(out)snprintf(out,n,"%s%s",pref,valor?valor:""); return out; }
static int sa_add_galho(const char *raiz, const char *arvore, const char *em, const char *nome)
{
    char pp[PATH_MAX*2]; if(caminho_pp_instalado(pp,sizeof(pp))!=0) return 1;
    char *arg_sa=sa_arg("--sa=",arvore), *arg_ag=sa_arg("--ag=",(nome&&*nome)?nome:"(vazio)"), *arg_em=(em&&*em)?sa_arg("--em=",em):NULL;
    if(!arg_sa||!arg_ag||((em&&*em)&&!arg_em)){free(arg_sa);free(arg_ag);free(arg_em);return 1;}
    int inpipe[2]; if(pipe(inpipe)!=0){free(arg_sa);free(arg_ag);free(arg_em);return 1;}
    pid_t pid=fork(); if(pid<0){close(inpipe[0]);close(inpipe[1]);free(arg_sa);free(arg_ag);free(arg_em);return 1;}
    if(pid==0){ if(raiz)chdir(raiz); close(inpipe[1]); dup2(inpipe[0],STDIN_FILENO); close(inpipe[0]); int devnull=open("/dev/null",O_WRONLY); if(devnull>=0){dup2(devnull,STDOUT_FILENO);dup2(devnull,STDERR_FILENO); if(devnull>STDERR_FILENO)close(devnull);} char *args[5]; int pos=0; args[pos++]=pp; args[pos++]=arg_sa; args[pos++]=arg_ag; if(arg_em)args[pos++]=arg_em; args[pos]=NULL; execv(pp,args); _exit(127); }
    close(inpipe[0]); write(inpipe[1],"s\n",2); close(inpipe[1]); int st=0; waitpid(pid,&st,0); free(arg_sa);free(arg_ag);free(arg_em); return WIFEXITED(st)?WEXITSTATUS(st):1;
}
static int sa_add_texto_em_partes(const char *raiz, const char *arvore, const char *pai, const char *prefixo, const char *texto)
{
    if(!texto||!*texto) return sa_add_galho(raiz,arvore,pai,prefixo?prefixo:"(vazio)");
    const size_t max=2800; size_t n=strlen(texto), pos=0; int parte=1;
    while(pos<n){ size_t fim=pos+max; if(fim>n)fim=n; else{ size_t corte=fim; while(corte>pos+400&&texto[corte]!='\n')corte--; if(corte>pos+400)fim=corte; } char titulo[3200]; snprintf(titulo,sizeof(titulo),"%s parte %d: %.*s",prefixo?prefixo:"texto",parte++,(int)(fim-pos),texto+pos); int rc=sa_add_galho(raiz,arvore,pai,titulo); if(rc!=0)return rc; pos=fim; while(pos<n&&texto[pos]=='\n')pos++; } return 0;
}
static int materializar_divergencia_sa(const char *raiz, const char *arvore_rel, const char *sistema, const char *status,
                                       const char *resposta)
{
    int rc=0; char buf[1024];
    snprintf(buf,sizeof(buf),"auditoria de divergência do sistema %s contra árvores SA, referências ra e código real",sistema?sistema:""); rc|=sa_add_galho(raiz,arvore_rel,NULL,buf);
    snprintf(buf,sizeof(buf),"status da execução: %s",status?status:""); rc|=sa_add_galho(raiz,arvore_rel,"1",buf);
    rc|=sa_add_galho(raiz,arvore_rel,"1","artefato persistido único: esta árvore SA; dossiê, prompt e resposta foram usados apenas em memória");
    rc|=sa_add_galho(raiz,arvore_rel,NULL,"resposta da LLM para revisão do operador");
    rc|=sa_add_texto_em_partes(raiz,arvore_rel,"2","resposta",resposta?resposta:"sem resposta");
    rc|=sa_add_galho(raiz,arvore_rel,NULL,"próximo passo do operador");
    rc|=sa_add_galho(raiz,arvore_rel,"3","revisar divergências, evidências ra/código e sugestões de atualização das árvores SA antes de criar GCs manuais");
    snprintf(buf,sizeof(buf),"abrir com: pp --sa=%s",arvore_rel?arvore_rel:""); rc|=sa_add_galho(raiz,arvore_rel,"3",buf);
    return rc;
}

int main(int argc, char **argv)
{
    if (restricao_verificar("divergencia-sistema") != 0) return 1;
    int sem_llm=0, rel=0, nivel=3;
    for(int i=1;i<argc;i++){
        if(!strcmp(argv[i],"--ajuda")||!strcmp(argv[i],"--help")||!strcmp(argv[i],"-h")){uso();return 0;}
        else if(!strcmp(argv[i],"--sem-llm")) sem_llm=1;
        else if(!strcmp(argv[i],"--rel")) rel=1;
        else if(!strncmp(argv[i],"--nivel=",8)) nivel=atoi(argv[i]+8);
        else { uso(); return 1; }
    }
    if(nivel<0) nivel=0;
    if(nivel>3) nivel=3;
    ctx_carregar_ignore_unico();
    char raiz[PATH_MAX]; if(!getcwd(raiz,sizeof(raiz))){perror("divergencia-sistema: getcwd");ctx_liberar_ignore();return 1;}
    const char *sistema=base_name(raiz); if(!sistema||!*sistema){fprintf(stderr,"divergencia-sistema: sistema atual inválido.\n");ctx_liberar_ignore();return 1;}
    char tree_abs[PATH_MAX*2]; snprintf(tree_abs,sizeof(tree_abs),"%s/arvores/operador-manual-funcional.json",raiz);
    size_t l=0; int trunc=0;
    char *tree_json = NULL;
    if (eh_arq(tree_abs)) {
        tree_json=ler_arquivo_limite(tree_abs,MAX_ARVORE_BYTES,&l,&trunc);
        if(!tree_json){fprintf(stderr,"divergencia-sistema: não foi possível ler %s\n",tree_abs);ctx_liberar_ignore();return 1;}
    } else {
        fprintf(stderr,"divergencia-sistema: aviso: arvores/operador-manual-funcional.json ausente; usando demais árvores SA via sa-ctx.\n");
        tree_json = strdup("");
        if (!tree_json) { ctx_liberar_ignore(); return 1; }
    }
    char *dossie=montar_dossie(raiz,sistema,tree_abs,tree_json,trunc); free(tree_json); if(!dossie) { ctx_liberar_ignore(); return 1; }
    char *prompt=montar_prompt(sistema,dossie); if(!prompt){free(dossie);ctx_liberar_ignore();return 1;}
    char arvore_rel[PATH_MAX*2];
    snprintf(arvore_rel,sizeof(arvore_rel),"arvores/divergencias");
    char arvore_abs[PATH_MAX*2];
    snprintf(arvore_abs,sizeof(arvore_abs),"%s/%s.json",raiz,arvore_rel);
    unlink(arvore_abs);
    char *resposta=NULL; int rc=0; const char *status="sem-llm";
    if(!sem_llm){
        rc=executar_parallm(prompt,nivel,rel,&resposta);
        if (rc == 0 && resposta && strstr(resposta, "(não foi possível extrair a resposta)")) {
            status = "llm-retornou-resposta-invalida";
            rc = 1;
        } else status = rc==0 ? "concluida-com-llm" : "llm-retornou-erro";
    }
    else resposta=strdup("Modo --sem-llm: LLM não chamada. A árvore registra apenas a estrutura mínima da execução; execute sem --sem-llm para análise técnica completa.");
    if(materializar_divergencia_sa(raiz,arvore_rel,sistema,status,resposta?resposta:"sem resposta") == 0){ printf("Árvore de divergência para revisão do operador: %s.json\n",arvore_rel); printf("Abra com: pp --sa=%s\n",arvore_rel); }
    else fprintf(stderr,"divergencia-sistema: falha ao materializar árvore SA final.\n");
    if(rc!=0 && !sem_llm) fprintf(stderr,"divergencia-sistema: parallm retornou código %d; a resposta parcial/erro foi incorporada à árvore quando disponível.\n",rc);
    free(resposta); free(prompt); free(dossie); ctx_liberar_ignore(); return rc;
}
