/*
 * atualizar-spec - vasculha a internet via LLM e atualiza fonteInformacao e conteudo da spec
 * Uso: pp --atualizar-spec=<spec_arquivo.json> [--conta-llm=arquivo.json] [--dry-run] [--apenas-fontes]
 */

#define _GNU_SOURCE
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <time.h>
#include "../restricao.h"

/* ------------------------------------------------------------------ */

static const char *filtro_ajuda_arg(const char *arg)
{
    if (!arg) return NULL;
    if (!strncmp(arg, "--ajuda:", 8) || !strncmp(arg, "--ajuda=", 8)) return arg + 8;
    if (!strncmp(arg, "--help:", 7)  || !strncmp(arg, "--help=", 7))  return arg + 7;
    return NULL;
}

static int eh_ajuda_arg(const char *arg)
{
    return arg && (!strcmp(arg, "--help") || !strcmp(arg, "--ajuda") || !strcmp(arg, "-h") || filtro_ajuda_arg(arg));
}

static void uso(const char *filtro)
{
    char exe[PATH_MAX];
    ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
    if (n <= 0) { perror("atualizar-spec: readlink"); return; }
    exe[n] = '\0';
    char raiz[PATH_MAX];
    snprintf(raiz, sizeof(raiz), "%s", exe);
    char *s = strrchr(raiz, '/'); if (s) *s = '\0';
    s = strrchr(raiz, '/'); if (s) *s = '\0';
    s = strrchr(raiz, '/'); if (s) *s = '\0';

    char parent[PATH_MAX];
    snprintf(parent, sizeof(parent), "%s", raiz);
    char *base = strrchr(parent, '/');
    if (!base || !base[1]) { fprintf(stderr, "atualizar-spec: nao foi possivel resolver a raiz\n"); return; }
    *base++ = '\0';

    char pp[PATH_MAX * 2];
    snprintf(pp, sizeof(pp), "%s/pp", raiz);
    char sa_arg[PATH_MAX * 2];
    snprintf(sa_arg, sizeof(sa_arg), "--sa=%s/ajuda/atualizar-spec.json", base);

    if (chdir(parent) != 0) { perror("atualizar-spec: chdir"); return; }
    char proc_arg[PATH_MAX * 2];
    char *args[4];
    args[0] = pp;
    args[1] = sa_arg;
    if (filtro && *filtro) { snprintf(proc_arg, sizeof(proc_arg), "--proc=%s", filtro); args[2] = proc_arg; args[3] = NULL; }
    else { args[2] = NULL; }
    execv(pp, args);
    perror("atualizar-spec: exec pp --sa=sisc/ajuda/atualizar-spec.json");
}

/* ------------------------------------------------------------------ */

static int termina_com(const char *s, const char *suf)
{ size_t a = strlen(s), b = strlen(suf); return a >= b && strcasecmp(s + a - b, suf) == 0; }

static char *ler_arquivo(const char *p)
{ FILE *fp = fopen(p, "rb"); if (!fp) return NULL; fseek(fp, 0, SEEK_END); long t = ftell(fp); if (t < 0 || t > 20 * 1024 * 1024) { fclose(fp); return NULL; } fseek(fp, 0, SEEK_SET); char *b = malloc((size_t)t + 1); if (!b) { fclose(fp); return NULL; } size_t nr = fread(b, 1, (size_t)t, fp); fclose(fp); b[nr] = '\0'; return b; }

static int gravar_arquivo(const char *p, const char *s)
{ FILE *fp = fopen(p, "wb"); if (!fp) return -1; size_t n = strlen(s); if (fwrite(s, 1, n, fp) != n) { fclose(fp); return -1; } if (n == 0 || s[n - 1] != '\n') fputc('\n', fp); return fclose(fp) == 0 ? 0 : -1; }

static char *ler_fd(int fd)
{ size_t cap = 4096, len = 0; char *b = malloc(cap); if (!b) return NULL; b[0] = '\0'; char tmp[2048]; ssize_t r; while ((r = read(fd, tmp, sizeof(tmp))) > 0) { if (len + (size_t)r + 1 >= cap) { cap = (len + (size_t)r + 1) * 2; char *nb = realloc(b, cap); if (!nb) { free(b); return NULL; } b = nb; } memcpy(b + len, tmp, (size_t)r); len += (size_t)r; b[len] = '\0'; } return b; }

typedef struct { int code; char *out; char *err; } Resultado;

static Resultado run_capture(char *const argv[], const char *cwd)
{ Resultado r = {1, strdup(""), strdup("")}; int op[2], ep[2]; if (pipe(op) != 0 || pipe(ep) != 0) return r; pid_t pid = fork(); if (pid == 0) { if (cwd) chdir(cwd); close(op[0]); close(ep[0]); dup2(op[1], 1); dup2(ep[1], 2); close(op[1]); close(ep[1]); execvp(argv[0], argv); _exit(127); } close(op[1]); close(ep[1]); free(r.out); free(r.err); r.out = ler_fd(op[0]); r.err = ler_fd(ep[0]); close(op[0]); close(ep[0]); int st = 0; waitpid(pid, &st, 0); r.code = WIFEXITED(st) ? WEXITSTATUS(st) : 1; if (!r.out) r.out = strdup(""); if (!r.err) r.err = strdup(""); return r; }

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 *s = strrchr(exe, '/'); if (!s) return -1; strcpy(s + 1, "parallm"); snprintf(out, tam, "%s", exe); return 0; }

/* ------------------------------------------------------------------ */
/* mini JSON                                                           */
/* ------------------------------------------------------------------ */

static char *json_extrair_string(const char *json, const char *chave)
{
    if (!json || !chave) return NULL;
    char pat[256];
    int pl = snprintf(pat, sizeof(pat), "\"%s\"", chave);
    if (pl < 0 || (size_t)pl >= sizeof(pat)) return NULL;
    const char *p = strstr(json, pat);
    if (!p) return NULL;
    p = strchr(p + (size_t)pl, ':');
    if (!p) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '"') return NULL;
    p++;
    char *out = malloc(strlen(p) + 1);
    if (!out) return NULL;
    size_t j = 0;
    while (*p && *p != '"') {
        if (*p == '\\' && p[1]) { p++; out[j++] = *p++; }
        else out[j++] = *p++;
    }
    out[j] = '\0';
    return out;
}

static char *json_extrair_string_ate(const char *json, const char *chave, const char *limite)
{
    if (!json || !chave) return NULL;
    char pat[256];
    int pl = snprintf(pat, sizeof(pat), "\"%s\"", chave);
    if (pl < 0 || (size_t)pl >= sizeof(pat)) return NULL;
    const char *p = strstr(json, pat);
    if (!p || (limite && p >= limite)) return NULL;
    p = strchr(p + (size_t)pl, ':');
    if (!p || (limite && p >= limite)) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if ((limite && p >= limite) || *p != '"') return NULL;
    p++;
    char *out = malloc((size_t)((limite && limite > p) ? (limite - p + 1) : (strlen(p) + 1)));
    if (!out) return NULL;
    size_t j = 0;
    while (*p && *p != '"' && (!limite || p < limite)) {
        if (*p == '\\' && p[1] && (!limite || p + 1 < limite)) { p++; out[j++] = *p++; }
        else out[j++] = *p++;
    }
    out[j] = '\0';
    return out;
}

static char *mapa_comandos_primeiro_filho_nome(const char *obj)
{
    const char *filhos = strstr(obj, "\"filhos\"");
    if (!filhos) return NULL;
    const char *abre = strchr(filhos, '[');
    if (!abre) return NULL;
    const char *fecha = strchr(abre, ']');
    const char *nome = strstr(abre, "\"nome\"");
    if (!nome || !fecha || nome > fecha) return NULL;
    return json_extrair_string_ate(nome, "nome", fecha);
}

static char *mapa_comandos_sa_conta(const char *json, const char *comando)
{
    char *tipo = json_extrair_string(json, "tipo");
    int eh_sa = tipo && strcmp(tipo, "arvore-sa") == 0;
    free(tipo);
    if (!eh_sa || !comando || !*comando) return NULL;
    char alt[PATH_MAX];
    snprintf(alt, sizeof(alt), "%s%s", strncmp(comando, "--", 2) == 0 ? "" : "--", comando);
    const char *p = json;
    while ((p = strstr(p, "\"nome\"")) != NULL) {
        const char *prox = strstr(p + 6, "\"nome\"");
        char *nome = json_extrair_string_ate(p, "nome", prox);
        int achou = nome && (strcmp(nome, comando) == 0 || strcmp(nome, alt) == 0);
        free(nome);
        if (achou) {
            char *conta = json_extrair_string_ate(p, "mm", prox);
            if (!conta) conta = json_extrair_string_ate(p, "valor", prox);
            if (!conta) conta = mapa_comandos_primeiro_filho_nome(p);
            return conta;
        }
        p += 6;
    }
    return NULL;
}

static char *json_extrair_array_raw(const char *json, const char *chave)
{
    if (!json || !chave) return NULL;
    char pat[256];
    int pl = snprintf(pat, sizeof(pat), "\"%s\"", chave);
    if (pl < 0 || (size_t)pl >= sizeof(pat)) return NULL;
    const char *p = strstr(json, pat);
    if (!p) return NULL;
    p = strchr(p + (size_t)pl, ':');
    if (!p) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '[') return NULL;
    const char *inicio = p;
    int depth = 0, str_mode = 0, esc = 0;
    while (*p) {
        char c = *p;
        if (str_mode) {
            if (esc) esc = 0;
            else if (c == '\\') esc = 1;
            else if (c == '"') str_mode = 0;
            p++; continue;
        }
        if (c == '"') str_mode = 1;
        else if (c == '[') depth++;
        else if (c == ']') { depth--; if (depth == 0) { p++; break; } }
        p++;
    }
    size_t n = (size_t)(p - inicio);
    char *out = malloc(n + 1);
    if (!out) return NULL;
    memcpy(out, inicio, n);
    out[n] = '\0';
    return out;
}

/* ------------------------------------------------------------------ */
/* conta LLM                                                           */
/* ------------------------------------------------------------------ */

static int normalizar_conta(const char *in, char *out, size_t tam)
{
    if (!in || !*in || !out || tam < 8) return -1;
    while (isspace((unsigned char)*in)) in++;
    if (strchr(in, '/') || strchr(in, '\\') || strstr(in, "..")) return -1;
    snprintf(out, tam, "%s", in);
    size_t n = strlen(out);
    while (n > 0 && isspace((unsigned char)out[n - 1])) out[--n] = '\0';
    if (n == 0) return -1;
    if (!termina_com(out, ".json")) {
        if (n + 5 >= tam) return -1;
        strcat(out, ".json");
    }
    for (const char *p = out; *p; p++)
        if (!(isalnum((unsigned char)*p) || *p == '.' || *p == '_' || *p == '-')) return -1;
    return 0;
}

static void conta_mapeada(const char *comando, char *out, size_t tam)
{
    snprintf(out, tam, "llm.json");
    char raiz[PATH_MAX];
    if (!getcwd(raiz, sizeof(raiz))) return;
    char mapa[PATH_MAX * 2];
    snprintf(mapa, sizeof(mapa), "%s/config/mapa-comandos.json", raiz);
    char *j = ler_arquivo(mapa);
    if (!j) { char *s = strrchr(raiz, '/'); if (s && s != raiz) { *s = '\0';
        snprintf(mapa, sizeof(mapa), "%s/core/config/mapa-comandos.json", raiz); j = ler_arquivo(mapa); } }
    if (!j) { char exe[PATH_MAX]; ssize_t n = readlink("/proc/self/exe", exe, sizeof(exe) - 1); if (n <= 0) return;
        exe[n] = '\0'; char *s2 = strrchr(exe, '/'); if (s2) *s2 = '\0'; s2 = strrchr(exe, '/'); if (s2) *s2 = '\0';
        snprintf(mapa, sizeof(mapa), "%s/config/mapa-comandos.json", exe); j = ler_arquivo(mapa); }
    if (!j) return;
    char *v = mapa_comandos_sa_conta(j, comando);
    if (!v) v = json_extrair_string(j, comando);
    if (v) { normalizar_conta(v, out, tam); free(v); }
    free(j);
}

/* ------------------------------------------------------------------ */
/* json_escape                                                         */
/* ------------------------------------------------------------------ */

static char *json_escape(const char *s)
{
    size_t cap = strlen(s) * 2 + 16, len = 0;
    char *out = malloc(cap);
    if (!out) return NULL;
    for (const char *p = s; *p; p++) {
        if (len + 8 >= cap) { cap *= 2; out = realloc(out, cap); if (!out) return NULL; }
        switch (*p) {
            case '"':  out[len++] = '\\'; out[len++] = '"'; break;
            case '\\': out[len++] = '\\'; out[len++] = '\\'; break;
            case '\n': out[len++] = '\\'; out[len++] = 'n'; break;
            case '\r': break;
            case '\t': out[len++] = ' '; break;
            default:   out[len++] = *p; break;
        }
    }
    out[len] = '\0';
    return out;
}

/* ------------------------------------------------------------------ */
/* extrai conteudo da spec para o prompt                                */
/* ------------------------------------------------------------------ */

typedef struct {
    char titulo[1024];
    char nome_agente[256];
    char resumo[2048];
    char fontes[8192];
    char keywords[2048];
} InfoSpec;

static void extrair_info_spec(const char *json, InfoSpec *info)
{
    memset(info, 0, sizeof(*info));
    char *v;

    /* titulo do agente */
    const char *pos = strstr(json, "\"agente\"");
    if (pos) {
        v = json_extrair_string(pos, "titulo");
        if (v && *v) { snprintf(info->titulo, sizeof(info->titulo), "%s", v); free(v); }
        v = json_extrair_string(pos, "nome");
        if (v && *v) { snprintf(info->nome_agente, sizeof(info->nome_agente), "%s", v); free(v); }
    }

    /* resumo do materialTecnico */
    pos = strstr(json, "\"materialTecnico\"");
    if (pos) {
        v = json_extrair_string(pos, "resumo");
        if (v && *v) { snprintf(info->resumo, sizeof(info->resumo), "%s", v); free(v); }
    }

    /* fonteInformacao */
    char *arr = json_extrair_array_raw(json, "fonteInformacao");
    if (arr) {
        size_t len = strlen(arr);
        if (len > 2) {
            size_t j = 0;
            for (size_t i = 1; i < len - 1 && j < sizeof(info->fontes) - 1; i++) {
                char c = arr[i];
                if (c == '\n' || c == '\r' || c == '\t') c = ' ';
                if (c == ' ' && (j == 0 || info->fontes[j - 1] == ' ')) continue;
                info->fontes[j++] = c;
            }
            info->fontes[j] = '\0';
        }
        free(arr);
    }

    /* palavrasChave */
    pos = strstr(json, "\"palavrasChave\"");
    if (pos) {
        arr = json_extrair_array_raw(pos, "palavrasChave");
        if (arr) {
            size_t len = strlen(arr);
            if (len > 2) {
                size_t j = 0;
                for (size_t i = 1; i < len - 1 && j < sizeof(info->keywords) - 1; i++) {
                    char c = arr[i];
                    if (c == '\n' || c == '\r' || c == '\t') c = ' ';
                    if (c == ' ' && (j == 0 || info->keywords[j - 1] == ' ')) continue;
                    info->keywords[j++] = c;
                }
                info->keywords[j] = '\0';
            }
            free(arr);
        }
    }
}

/* ------------------------------------------------------------------ */
/* constroi prompt para a LLM                                          */
/* ------------------------------------------------------------------ */

static char *construir_prompt(const InfoSpec *info, int apenas_fontes)
{
    size_t cap = 65536;
    char *p = malloc(cap);
    if (!p) return NULL;

    const char *titulo = info->titulo[0] ? info->titulo : "especificacao tecnica";
    const char *resumo = info->resumo[0] ? info->resumo : "";
    const char *fontes = info->fontes[0] ? info->fontes : "nenhuma fonte registrada";
    const char *kw     = info->keywords[0] ? info->keywords : "";

    if (apenas_fontes) {
        snprintf(p, cap,
            "Voce eh um pesquisador tecnico. Para a especificacao abaixo, "
            "vasculhe a internet e encontre documentacao, artigos, normas, RFCs, "
            "boas praticas e referencias ATUALIZADAS (2024-2026) sobre o tema.\n\n"
            "ESPECIFICACAO:\n"
            "Titulo: %s\n"
            "Resumo: %s\n"
            "Palavras-chave: %s\n"
            "Fontes atuais: %s\n\n"
            "Responda SOMENTE com um JSON valido neste formato exato:\n"
            "{\"novasFontes\": [\"fonte 1\", \"fonte 2\"], \"observacoes\": \"breve nota\"}\n"
            "Onde novasFontes eh um array de strings com as fontes encontradas "
            "(URLs, nomes de normas, RFCs, artigos).",
            titulo, resumo, kw, fontes);
    } else {
        snprintf(p, cap,
            "Voce eh um pesquisador tecnico e arquiteto de especificacoes. "
            "Para a especificacao tecnica abaixo, vasculhe a internet em busca de "
            "material ATUALIZADO (2024-2026) sobre o tema: documentacao, normas, "
            "RFCs, artigos, boas praticas, padroes de industria e cases relevantes.\n\n"
            "ESPECIFICACAO A SER ATUALIZADA:\n"
            "Titulo: %s\n"
            "Resumo do material tecnico: %s\n"
            "Palavras-chave: %s\n"
            "Fontes atuais: %s\n\n"
            "Com base na sua pesquisa, produza um JSON com as seguintes secoes:\n\n"
            "{\n"
            "  \"novasFontes\": [\"url ou referencia 1\", ...],\n"
            "  \"resumoAtualizado\": \"resumo revisado do material tecnico\",\n"
            "  \"escopoAdicional\": [\"item extra 1\", ...],\n"
            "  \"criteriosAdicionais\": [\"criterio novo 1\", ...],\n"
            "  \"riscosAdicionais\": [\"risco ou antipadrao 1\", ...],\n"
            "  \"observacoes\": \"nota sobre as mudancas encontradas na internet\"\n"
            "}\n\n"
            "IMPORTANTE:\n"
            "- Responda SOMENTE com o JSON, sem texto adicional.\n"
            "- Use aspas duplas para strings.\n"
            "- Arrays vazios use [].\n"
            "- Se nao houver novidade, retorne arrays vazios e informe nas observacoes.\n"
            "- Seja objetivo e baseado em fontes reais da internet.",
            titulo, resumo, kw, fontes);
    }
    return p;
}

/* ------------------------------------------------------------------ */
/* extrai o JSON da resposta LLM (entre { e })                         */
/* ------------------------------------------------------------------ */

static char *extrair_json_resposta(const char *resp)
{
    const char *abre = strchr(resp, '{');
    if (!abre) return NULL;
    const char *fecha = strrchr(abre, '}');
    if (!fecha) return NULL;
    size_t n = (size_t)(fecha - abre) + 1;
    char *out = malloc(n + 1);
    if (!out) return NULL;
    memcpy(out, abre, n);
    out[n] = '\0';
    return out;
}

/* ------------------------------------------------------------------ */
/* avanca ate fechar o array/string atual                              */
/* ------------------------------------------------------------------ */

static const char *avancar_array_fim(const char *inicio)
{
    int depth = 0, str_mode = 0, esc = 0;
    while (*inicio) {
        char c = *inicio;
        if (str_mode) {
            if (esc) esc = 0;
            else if (c == '\\') esc = 1;
            else if (c == '"') str_mode = 0;
            inicio++; continue;
        }
        if (c == '"') str_mode = 1;
        else if (c == '[') depth++;
        else if (c == ']') { depth--; if (depth == 0) { inicio++; break; } }
        inicio++;
    }
    return inicio;
}

static const char *avancar_string_fim(const char *inicio)
{
    if (*inicio != '"') return inicio;
    inicio++;
    while (*inicio && *inicio != '"') {
        if (*inicio == '\\' && inicio[1]) inicio++;
        inicio++;
    }
    if (*inicio == '"') inicio++;
    return inicio;
}

/* ------------------------------------------------------------------ */
/* mescla atualizacoes na spec                                          */
/* ------------------------------------------------------------------ */

static char *mesclar_atualizacoes(const char *spec_original, const char *resp_json, int apenas_fontes)
{
    char *novas_fontes = json_extrair_array_raw(resp_json, "novasFontes");
    char *obs = json_extrair_string(resp_json, "observacoes");

    char *resumo_at = NULL, *escopo_arr = NULL, *criterios_arr = NULL, *riscos_arr = NULL;
    if (!apenas_fontes) {
        resumo_at     = json_extrair_string(resp_json, "resumoAtualizado");
        escopo_arr    = json_extrair_array_raw(resp_json, "escopoAdicional");
        criterios_arr = json_extrair_array_raw(resp_json, "criteriosAdicionais");
        riscos_arr    = json_extrair_array_raw(resp_json, "riscosAdicionais");
    }

    size_t cap = strlen(spec_original) + 65536;
    char *out = malloc(cap);
    if (!out) goto cleanup;
    out[0] = '\0';

    const char *s = spec_original;
    int fontes_substituidas = 0;

    /* --- fonteInformacao --- */
    if (novas_fontes && strlen(novas_fontes) > 2) {
        const char *p_fi = strstr(s, "\"fonteInformacao\"");
        if (p_fi) {
            p_fi = strchr(p_fi, '[');
            if (p_fi) {
                size_t prefixo = (size_t)(p_fi - s) + 1;
                memcpy(out, s, prefixo);
                size_t out_len = prefixo;
                const char *fn = novas_fontes + 1;
                size_t fn_len = strlen(fn);
                if (fn_len > 0 && fn[fn_len - 1] == ']') fn_len--;
                out_len += (size_t)snprintf(out + out_len, cap - out_len, "\n        %.*s\n    ", (int)fn_len, fn);
                const char *p_end = avancar_array_fim(p_fi);
                strncat(out, p_end, cap - out_len - 1);
                s = NULL;
                fontes_substituidas = 1;
            }
        }
    }

    if (!fontes_substituidas) {
        strncpy(out, s, cap - 1);
        out[cap - 1] = '\0';
    }

    /* --- resumo do materialTecnico --- */
    if (resumo_at && resumo_at[0] && !apenas_fontes) {
        char *novo = malloc(cap);
        if (novo) {
            const char *alvo = strstr(out, "\"materialTecnico\"");
            if (alvo) {
                alvo = strstr(alvo, "\"resumo\"");
                if (alvo) {
                    alvo = strchr(alvo, ':');
                    if (alvo) {
                        alvo++;
                        while (*alvo && isspace((unsigned char)*alvo)) alvo++;
                        if (*alvo == '"') {
                            const char *inicio_str = alvo;
                            alvo = avancar_string_fim(alvo);
                            size_t antes = (size_t)(inicio_str - out);
                            char *resumo_esc = json_escape(resumo_at);
                            memcpy(novo, out, antes);
                            snprintf(novo + antes, cap - antes, "\"%s\"%s", resumo_esc ? resumo_esc : resumo_at, alvo);
                            free(resumo_esc);
                            memcpy(out, novo, cap);
                        }
                    }
                }
            }
            free(novo);
        }
    }

    /* --- escopo --- */
    if (escopo_arr && strlen(escopo_arr) > 2 && !apenas_fontes) {
        const char *p_esc = strstr(out, "\"escopo\"");
        if (p_esc) {
            p_esc = strchr(p_esc, '[');
            if (p_esc) {
                const char *p_end = avancar_array_fim(p_esc);
                size_t antes = (size_t)(p_end - out) - 1;
                char *novo = malloc(strlen(out) + strlen(escopo_arr) + 512);
                if (novo) {
                    memcpy(novo, out, antes);
                    const char *ei = escopo_arr + 1;
                    size_t ei_len = strlen(ei);
                    if (ei_len > 0 && ei[ei_len - 1] == ']') ei_len--;
                    snprintf(novo + antes, strlen(out) + strlen(escopo_arr) + 512 - antes,
                             ",\n        %.*s\n    ]%s", (int)ei_len, ei, p_end);
                    memcpy(out, novo, strlen(novo) + 1);
                    free(novo);
                }
            }
        }
    }

    /* --- criteriosObrigatorios --- */
    if (criterios_arr && strlen(criterios_arr) > 2 && !apenas_fontes) {
        const char *p_cr = strstr(out, "\"criteriosObrigatorios\"");
        if (p_cr) {
            p_cr = strchr(p_cr, '[');
            if (p_cr) {
                const char *p_end = avancar_array_fim(p_cr);
                size_t antes = (size_t)(p_end - out) - 1;
                char *novo = malloc(strlen(out) + strlen(criterios_arr) + 512);
                if (novo) {
                    memcpy(novo, out, antes);
                    const char *ci = criterios_arr + 1;
                    size_t ci_len = strlen(ci);
                    if (ci_len > 0 && ci[ci_len - 1] == ']') ci_len--;
                    snprintf(novo + antes, strlen(out) + strlen(criterios_arr) + 512 - antes,
                             ",\n        %.*s\n    ]%s", (int)ci_len, ci, p_end);
                    memcpy(out, novo, strlen(novo) + 1);
                    free(novo);
                }
            }
        }
    }

    /* --- riscosEAntipadroes --- */
    if (riscos_arr && strlen(riscos_arr) > 2 && !apenas_fontes) {
        const char *p_ri = strstr(out, "\"riscosEAntipadroes\"");
        if (p_ri) {
            p_ri = strchr(p_ri, '[');
            if (p_ri) {
                const char *p_end = avancar_array_fim(p_ri);
                size_t antes = (size_t)(p_end - out) - 1;
                char *novo = malloc(strlen(out) + strlen(riscos_arr) + 512);
                if (novo) {
                    memcpy(novo, out, antes);
                    const char *ri = riscos_arr + 1;
                    size_t ri_len = strlen(ri);
                    if (ri_len > 0 && ri[ri_len - 1] == ']') ri_len--;
                    snprintf(novo + antes, strlen(out) + strlen(riscos_arr) + 512 - antes,
                             ",\n        %.*s\n    ]%s", (int)ri_len, ri, p_end);
                    memcpy(out, novo, strlen(novo) + 1);
                    free(novo);
                }
            }
        }
    }

    /* --- sincronizacao --- */
    {
        const char *p_sinc = strstr(out, "\"sincronizacao\"");
        if (p_sinc) {
            const char *p_por = strstr(p_sinc, "\"atualizadoPor\"");
            if (p_por) {
                p_por = strchr(p_por, ':');
                if (p_por) {
                    size_t ate_por = (size_t)(p_por - out);
                    const char *resto = strchr(p_por + 1, '\n');
                    if (resto) {
                        const char *p_em2 = strstr(resto + 1, "\"atualizadoEm\"");
                        if (p_em2) {
                            size_t entre = (size_t)(p_em2 - resto);
                            p_em2 = strchr(p_em2, ':');
                            if (p_em2) {
                                p_em2++;
                                while (*p_em2 && isspace((unsigned char)*p_em2)) p_em2++;
                                const char *fim_valor = (*p_em2 == '"') ? avancar_string_fim(p_em2) : p_em2 + strcspn(p_em2, ",\n\r}");
                                char *novo = malloc(strlen(out) + 512);
                                if (novo) {
                                    memcpy(novo, out, ate_por);
                                    size_t noff = ate_por;
                                    noff += (size_t)snprintf(novo + noff, strlen(out) + 512 - noff,
                                             ": \"pp --atualizar-spec\",");
                                    memcpy(novo + noff, resto, entre);
                                    noff += entre;
                                    noff += (size_t)snprintf(novo + noff, strlen(out) + 512 - noff,
                                             ": \"%ld\"", (long)time(NULL));
                                    strncat(novo + noff, fim_valor, strlen(out) + 512 - noff - 1);
                                    memcpy(out, novo, strlen(novo) + 1);
                                    free(novo);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /* --- qualidadeSpec.observacoes --- */
    if (obs && obs[0]) {
        const char *p_q = strstr(out, "\"qualidadeSpec\"");
        if (p_q) {
            const char *p_obs_arr = strstr(p_q, "\"observacoes\"");
            if (p_obs_arr) {
                p_obs_arr = strchr(p_obs_arr, '[');
                if (p_obs_arr) {
                    const char *p_end = avancar_array_fim(p_obs_arr);
                    size_t antes = (size_t)(p_end - out) - 1;
                    char *novo = malloc(strlen(out) + strlen(obs) + 512);
                    if (novo) {
                        memcpy(novo, out, antes);
                        char *obs_esc = json_escape(obs);
                        snprintf(novo + antes, strlen(out) + strlen(obs) + 512 - antes,
                                 ",\n            \"%s\"\n        ]%s", obs_esc ? obs_esc : obs, p_end);
                        free(obs_esc);
                        memcpy(out, novo, strlen(novo) + 1);
                        free(novo);
                    }
                }
            }
        }
    }

cleanup:
    free(novas_fontes);
    free(obs);
    free(resumo_at);
    free(escopo_arr);
    free(criterios_arr);
    free(riscos_arr);
    return out;
}

/* ------------------------------------------------------------------ */
/* main                                                                */
/* ------------------------------------------------------------------ */

int main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++)
        if (eh_ajuda_arg(argv[i])) { uso(filtro_ajuda_arg(argv[i])); return 0; }

    if (restricao_verificar("atualizar-spec") != 0) return 1;

    char spec_nome[512] = "";
    char conta[PATH_MAX] = "";
    int dry_run = 0;
    int apenas_fontes = 0;

    for (int i = 1; i < argc; i++) {
        if (eh_ajuda_arg(argv[i])) { uso(filtro_ajuda_arg(argv[i])); return 0; }
        if (strncmp(argv[i], "--atualizar-spec=", 17) == 0) {
            snprintf(spec_nome, sizeof(spec_nome), "%s", argv[i] + 17);
        } else if (strcmp(argv[i], "--dry-run") == 0) {
            dry_run = 1;
        } else if (strcmp(argv[i], "--apenas-fontes") == 0) {
            apenas_fontes = 1;
        } else if (strncmp(argv[i], "--conta-llm=", 12) == 0) {
            if (normalizar_conta(argv[i] + 12, conta, sizeof(conta)) != 0) {
                fprintf(stderr, "atualizar-spec: conta LLM invalida\n");
                return 1;
            }
        } else if (argv[i][0] != '-') {
            if (!spec_nome[0]) snprintf(spec_nome, sizeof(spec_nome), "%s", argv[i]);
        } else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "--ajuda") || !strcmp(argv[i], "-h")) {
            uso(NULL); return 0;
        }
    }

    if (!spec_nome[0]) {
        fprintf(stderr, "atualizar-spec: informe a spec. Ex: pp --atualizar-spec=spec_seguranca.json\n");
        return 1;
    }

    /* Normaliza nome */
    {
        char *v = spec_nome;
        if (!strncmp(v, "spec-diretivas/", 15)) v += 15;
        if (!termina_com(v, ".json")) {
            size_t len = strlen(v);
            if (len + 6 < sizeof(spec_nome)) strcat(spec_nome, ".json");
        }
        if (strncmp(spec_nome, "spec-diretivas/", 15) == 0)
            memmove(spec_nome, spec_nome + 15, strlen(spec_nome + 15) + 1);
    }

    /* Localiza o arquivo somente no diretorio de specs do sistema atual. */
    char raiz[PATH_MAX];
    if (!getcwd(raiz, sizeof(raiz))) { perror("atualizar-spec: getcwd"); return 1; }

    char dir_specs[PATH_MAX * 2];
    snprintf(dir_specs, sizeof(dir_specs), "%s/spec-diretivas", raiz);
    struct stat st_specs;
    if (stat(dir_specs, &st_specs) != 0 || !S_ISDIR(st_specs.st_mode)) {
        fprintf(stderr, "atualizar-spec: diretorio spec-diretivas/ nao encontrado na raiz do sistema atual.\n");
        return 1;
    }

    char path[PATH_MAX * 2];
    snprintf(path, sizeof(path), "%s/spec-diretivas/%s", raiz, spec_nome);

    if (access(path, F_OK) != 0) {
        fprintf(stderr, "atualizar-spec: spec nao encontrada em spec-diretivas/: %s\n", spec_nome);
        return 1;
    }

    char *json_original = ler_arquivo(path);
    if (!json_original) {
        fprintf(stderr, "atualizar-spec: nao foi possivel ler %s\n", path);
        return 1;
    }

    InfoSpec info;
    extrair_info_spec(json_original, &info);

    printf("=== Atualizando spec: %s ===\n", spec_nome);
    printf("  Titulo: %s\n", info.titulo[0] ? info.titulo : "(nao encontrado)");
    printf("  Fontes atuais: %s\n", info.fontes[0] ? info.fontes : "(nenhuma)");
    printf("  Palavras-chave: %s\n", info.keywords[0] ? info.keywords : "(nenhuma)");
    if (apenas_fontes) printf("  Modo: apenas fontes\n");
    if (dry_run) printf("  Modo: dry-run (sem gravacao)\n");

    if (!conta[0]) {
        const char *e = getenv("SISC_CONTA_LLM");
        if (e && *e) normalizar_conta(e, conta, sizeof(conta));
    }
    if (!conta[0]) conta_mapeada("atualizar-spec", conta, sizeof(conta));

    printf("  Conta LLM: %s\n", conta);
    printf("  Pesquisando na internet via LLM...\n");

    char *prompt = construir_prompt(&info, apenas_fontes);
    if (!prompt) {
        fprintf(stderr, "atualizar-spec: erro ao montar prompt\n");
        free(json_original);
        return 1;
    }

    char parallm_bin[PATH_MAX * 2];
    if (caminho_parallm(parallm_bin, sizeof(parallm_bin)) != 0) {
        fprintf(stderr, "atualizar-spec: nao foi possivel localizar parallm\n");
        free(prompt);
        free(json_original);
        return 1;
    }

    char carg[PATH_MAX + 32];
    snprintf(carg, sizeof(carg), "--conta-llm=%s", conta);
    char *pargv[] = { parallm_bin, prompt, "--sem-heb", carg, NULL };

    Resultado pr = run_capture(pargv, NULL);
    free(prompt);

    if (pr.code != 0 || !pr.out || !*pr.out) {
        fprintf(stderr, "atualizar-spec: erro ao chamar LLM (codigo %d)\n", pr.code);
        if (pr.err && *pr.err) fprintf(stderr, "%s\n", pr.err);
        free(pr.out); free(pr.err);
        free(json_original);
        return 1;
    }

    printf("  Resposta recebida da LLM.\n");

    char *resp_json = extrair_json_resposta(pr.out);
    if (!resp_json) {
        fprintf(stderr, "atualizar-spec: resposta da LLM nao contem JSON valido\n");
        fprintf(stderr, "Resposta:\n%s\n", pr.out ? pr.out : "(vazia)");
        free(pr.out); free(pr.err);
        free(json_original);
        return 1;
    }

    printf("  JSON extraido da resposta.\n");

    /* Preview fontes */
    char *novas_fontes = json_extrair_array_raw(resp_json, "novasFontes");
    if (novas_fontes && strlen(novas_fontes) > 2) {
        printf("  Novas fontes encontradas:\n");
        const char *p = novas_fontes + 1;
        int str_mode = 0, esc = 0;
        while (*p) {
            if (str_mode) {
                if (esc) esc = 0;
                else if (*p == '\\') esc = 1;
                else if (*p == '"') str_mode = 0;
                p++; continue;
            }
            if (*p == '"') {
                str_mode = 1;
                const char *inicio = p + 1;
                p++;
                while (*p && *p != '"') { if (*p == '\\' && p[1]) p++; p++; }
                size_t n = (size_t)(p - inicio);
                if (n > 0) {
                    char *item = malloc(n + 1);
                    memcpy(item, inicio, n);
                    item[n] = '\0';
                    printf("    - %s\n", item);
                    free(item);
                }
                if (*p == '"') p++;
                continue;
            }
            p++;
        }
    } else {
        printf("  Nenhuma fonte nova encontrada.\n");
    }

    if (!apenas_fontes) {
        char *r = json_extrair_string(resp_json, "resumoAtualizado");
        if (r && *r) printf("  Resumo atualizado: %s\n", r);
        free(r);
        r = json_extrair_string(resp_json, "observacoes");
        if (r && *r) printf("  Observacoes: %s\n", r);
        free(r);
    }
    free(novas_fontes);

    char *json_atualizado = mesclar_atualizacoes(json_original, resp_json, apenas_fontes);

    if (dry_run) {
        printf("\n[Dry-run] Spec NAO foi alterada. Use sem --dry-run para aplicar.\n");
    } else {
        if (gravar_arquivo(path, json_atualizado) != 0) {
            fprintf(stderr, "atualizar-spec: erro ao gravar %s\n", path);
            free(json_atualizado); free(resp_json);
            free(pr.out); free(pr.err);
            free(json_original);
            return 1;
        }
        printf("\nSpec atualizada com sucesso: %s\n", path);
        printf("Revise a qualidade da spec atualizada: %s\n", spec_nome);
    }

    free(json_atualizado);
    free(resp_json);
    free(pr.out);
    free(pr.err);
    free(json_original);
    return 0;
}
