#include "conectores-operacional.h"
#include <grp.h>
#include <pwd.h>

typedef struct { char origem[PATH_MAX]; char destino[PATH_MAX]; char papel[64]; } Dep;

static void uso(void) {
    printf("Uso:\n");
    printf("  ./instalar-aprovados [--dry-run] [--forcar] [--homologacao-sem-selo] [--destino=/var/www/html/sisc/siscore] [--pacote=/caminho/pacote.tar.gz]\n\n");
    printf("Instala pacotes com selo-validacao; no SISC real exige tambem selo-sandbox valido.\n");
    printf("--homologacao-sem-selo e permitido somente para destino alternativo/testesis e revalida o projeto antes de copiar.\n");
}

static int add_dep(Dep deps[], int *n, const char *origem, const char *destino, const char *papel) {
    if (!origem || !destino || !*origem || !*destino) return -1;
    for (int i = 0; i < *n; i++) if (strcmp(deps[i].destino, destino) == 0) return 0;
    if (*n >= 512) return -1;
    snprintf(deps[*n].origem, sizeof(deps[*n].origem), "%s", origem);
    snprintf(deps[*n].destino, sizeof(deps[*n].destino), "%s", destino);
    snprintf(deps[*n].papel, sizeof(deps[*n].papel), "%s", papel && *papel ? papel : "arquivo");
    (*n)++;
    return 0;
}

static int parse_deps(const char *manifesto, Dep deps[], int *n) {
    const char *p = manifesto;
    while ((p = strstr(p, "\"origem\"")) != NULL) {
        char origem[PATH_MAX] = "", destino[PATH_MAX] = "", papel[64] = "arquivo";
        const char *ini = p > manifesto + 400 ? p - 400 : manifesto;
        const char *pap = strstr(ini, "\"papel\"");
        if (pap && pap < p) json_get_string(pap, "papel", papel, sizeof(papel));
        if (!json_get_string(p, "origem", origem, sizeof(origem))) { p += 8; continue; }
        const char *d = strstr(p, "\"destino\"");
        if (!d || !json_get_string(d, "destino", destino, sizeof(destino))) { p += 8; continue; }
        add_dep(deps, n, origem, destino, papel);
        p = d + 9;
    }
    return *n > 0 ? 0 : -1;
}

static int extract_messages_content(const char *txt, char **out) {
    const char *p = strstr(txt, "\"mensagens\"");
    if (!p) return -1;
    p = strchr(p, '['); if (!p) return -1;
    const char *start = p + 1;
    int depth = 1, str = 0, esc = 0;
    for (p = start; *p; p++) {
        if (esc) { esc = 0; continue; }
        if (str) { if (*p == '\\') esc = 1; else if (*p == '"') str = 0; continue; }
        if (*p == '"') str = 1;
        else if (*p == '[') depth++;
        else if (*p == ']') { depth--; if (depth == 0) break; }
    }
    if (*p != ']') return -1;
    size_t n = (size_t)(p - start);
    *out = malloc(n + 1); if (!*out) return -1;
    memcpy(*out, start, n); (*out)[n] = '\0';
    return 0;
}

static int uid_gid_www_data(uid_t *uid, gid_t *gid) {
    if (!uid || !gid) return -1;
    struct passwd *pw = getpwnam("www-data");
    struct group *gr = getgrnam("www-data");
    if (!pw || !gr) return -1;
    *uid = pw->pw_uid;
    *gid = gr->gr_gid;
    return 0;
}

static void ajustar_item_instalado(const char *path, int diretorio, int executavel, char *log, size_t logsz) {
    if (!path || !*path) return;
    uid_t uid = 0;
    gid_t gid = 0;
    if (geteuid() == 0 && uid_gid_www_data(&uid, &gid) == 0) {
        if (chown(path, uid, gid) != 0) sb_add(log, logsz, "aviso: falha ajustando dono www-data:www-data em %s\n", path);
    }
    mode_t modo = diretorio ? 02775 : (executavel ? 0755 : 0664);
    if (chmod(path, modo) != 0) sb_add(log, logsz, "aviso: falha ajustando permissao %04o em %s\n", (unsigned)modo, path);
}

static void ajustar_permissoes_rel_destino(const char *sisc, const char *rel, int executavel, char *log, size_t logsz) {
    if (!sisc || !rel || !safe_rel(rel)) return;
    char parcial[PATH_MAX];
    snprintf(parcial, sizeof(parcial), "%s", sisc);
    const char *p = rel;
    while (*p) {
        const char *barra = strchr(p, '/');
        if (!barra) break;
        size_t atual = strlen(parcial);
        size_t comp = (size_t)(barra - p);
        if (atual + 1 + comp >= sizeof(parcial)) return;
        parcial[atual] = '/';
        memcpy(parcial + atual + 1, p, comp);
        parcial[atual + 1 + comp] = '\0';
        if (is_dir_p(parcial)) ajustar_item_instalado(parcial, 1, 0, log, logsz);
        p = barra + 1;
    }
    char arquivo[PATH_MAX];
    snprintf(arquivo, sizeof(arquivo), "%s/%s", sisc, rel);
    if (is_file_p(arquivo)) ajustar_item_instalado(arquivo, 0, executavel, log, logsz);
}

static void ajustar_marca_do_pacote(const char *pacote, const char *marca, mode_t modo, char *log, size_t logsz) {
    if (!pacote || !marca || geteuid() != 0) return;
    struct stat st;
    if (stat(pacote, &st) != 0) return;
    if (chown(marca, st.st_uid, st.st_gid) != 0) sb_add(log, logsz, "aviso: falha ajustando dono da marca %s\n", marca);
    chmod(marca, modo);
}

static void normalizar_arvore_instalada(const char *path, char *log, size_t logsz) {
    struct stat st;
    if (!path || lstat(path, &st) != 0 || S_ISLNK(st.st_mode)) return;
    if (S_ISDIR(st.st_mode)) {
        ajustar_item_instalado(path, 1, 0, log, logsz);
        DIR *d = opendir(path);
        if (!d) return;
        struct dirent *e;
        while ((e = readdir(d))) {
            if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue;
            char sub[PATH_MAX];
            snprintf(sub, sizeof(sub), "%s/%s", path, e->d_name);
            normalizar_arvore_instalada(sub, log, logsz);
        }
        closedir(d);
    } else if (S_ISREG(st.st_mode)) {
        ajustar_item_instalado(path, 0, strstr(path, "/handlers/") ? 1 : 0, log, logsz);
    }
}

static int atualizar_catalogo(const char *sisc, char *log, size_t logsz) {
    char web[PATH_MAX]; snprintf(web, sizeof(web), "%s/web-api", sisc);
    DIR *d = opendir(web); if (!d) { sb_add(log, logsz, "web-api ausente no destino\n"); return -1; }
    char *partes[2048]; int np = 0;
    struct dirent *e;
    while ((e = readdir(d))) {
        if (!starts_with(e->d_name, "catalogo-") || !ends_with(e->d_name, ".json") || strcmp(e->d_name, "catalogo-mensagens.json") == 0) continue;
        char path[PATH_MAX]; snprintf(path, sizeof(path), "%s/%s", web, e->d_name);
        char *txt = read_file_alloc(path, NULL); if (!txt) continue;
        char *msgs = NULL;
        if (extract_messages_content(txt, &msgs) == 0 && msgs) partes[np++] = msgs;
        free(txt);
        if (np >= 2048) break;
    }
    closedir(d);
    if (np == 0) { sb_add(log, logsz, "nenhum catalogo modular para agregar\n"); return -1; }
    size_t tam = 4096;
    for (int i = 0; i < np; i++) tam += strlen(partes[i]) + 8;
    char *out = malloc(tam); if (!out) return -1;
    snprintf(out, tam,
        "{\n"
        "  \"tipo\": \"catalogo-mensagens-sisc\",\n"
        "  \"versao\": 3,\n"
        "  \"descricao\": \"Catalogo agregado automaticamente por gitconectores/instalar-aprovados.c\",\n"
        "  \"formatoEscuta\": \"formatos-escuta/protocolo-mensagem-siscore.v1.json\",\n"
        "  \"regraComunicacao\": \"somente-espaco-sisc-sem-acesso-direto\",\n"
        "  \"geradoPor\": \"gitconectores/instalar-aprovados\",\n"
        "  \"mensagens\": [\n");
    for (int i = 0; i < np; i++) {
        strcat(out, partes[i]);
        if (i + 1 < np) strcat(out, ",\n"); else strcat(out, "\n");
        free(partes[i]);
    }
    strcat(out, "  ]\n}\n");
    char dest[PATH_MAX]; snprintf(dest, sizeof(dest), "%s/catalogo-mensagens.json", web);
    int rc = write_text_file(dest, out, 0664);
    free(out);
    if (rc != 0) { sb_add(log, logsz, "falha gravando catalogo agregado\n"); return -1; }
    ajustar_permissoes_rel_destino(sisc, "web-api/catalogo-mensagens.json", 0, log, logsz);
    sb_add(log, logsz, "catalogo agregado atualizado\n");
    return 0;
}

static int destino_igual(const char *a, const char *b) {
    char ra[PATH_MAX], rb[PATH_MAX];
    if (canonical_path(a, ra, sizeof(ra)) != 0 || canonical_path(b, rb, sizeof(rb)) != 0) return 0;
    return strcmp(ra, rb) == 0;
}

static int remover_arvore_segura(const char *path, const char *base, char *log, size_t logsz) {
    if (!path || !base || !starts_with(path, base)) return -1;
    char qp[PATH_MAX * 2], cmd[PATH_MAX * 4];
    shell_quote(path, qp, sizeof(qp));
    snprintf(cmd, sizeof(cmd), "rm -rf -- %s", qp);
    int rc = run_cmd(cmd);
    if (rc != 0) sb_add(log, logsz, "falha removendo arvore: %s\n", path);
    return rc == 0 ? 0 : -1;
}

static void limpar_testesis_conector_aprovado(const char *nome, char *log, size_t logsz) {
    if (!nome_conector_valido(nome)) { sb_add(log, logsz, "limpeza testesis ignorada: nome invalido\n"); return; }
    if (!is_dir_p(GC_TESTESIS_PADRAO)) { sb_add(log, logsz, "limpeza testesis ignorada: destino ausente\n"); return; }

    char path[PATH_MAX];
    snprintf(path, sizeof(path), "%s/conectores/%s", GC_TESTESIS_PADRAO, nome);
    if (is_dir_p(path) && remover_arvore_segura(path, GC_TESTESIS_PADRAO, log, logsz) == 0) sb_add(log, logsz, "testesis: removido conectores/%s\n", nome);

    snprintf(path, sizeof(path), "%s/web-api/catalogo-%s.json", GC_TESTESIS_PADRAO, nome);
    if (is_file_p(path)) {
        if (unlink(path) == 0) sb_add(log, logsz, "testesis: removido web-api/catalogo-%s.json\n", nome);
        else sb_add(log, logsz, "testesis: falha removendo web-api/catalogo-%s.json\n", nome);
    }

    snprintf(path, sizeof(path), "%s/secretos/%s.json", GC_TESTESIS_PADRAO, nome);
    if (is_file_p(path)) { if (unlink(path) == 0) sb_add(log, logsz, "testesis: removido secreto do conector\n"); }
    snprintf(path, sizeof(path), "%s/secretos/%s.sample.json", GC_TESTESIS_PADRAO, nome);
    if (is_file_p(path)) { if (unlink(path) == 0) sb_add(log, logsz, "testesis: removido sample secreto do conector\n"); }

    char qbase[PATH_MAX * 2], qnome[PATH_MAX * 2], cmd[PATH_MAX * 8];
    shell_quote(GC_TESTESIS_PADRAO, qbase, sizeof(qbase));
    shell_quote(nome, qnome, sizeof(qnome));
    snprintf(cmd, sizeof(cmd),
        "find %s -path %s'/.git' -prune -o -type f -name '*%s*' -delete 2>/dev/null; "
        "grep -RIl --exclude-dir=.git -- %s %s 2>/dev/null | xargs -r rm -f --; "
        "find %s -path %s'/.git' -prune -o -depth -type d -name '*%s*' -exec rm -rf -- {} + 2>/dev/null",
        qbase, qbase, nome, qnome, qbase, qbase, qbase, nome);
    run_cmd(cmd);
    sb_add(log, logsz, "testesis: vestigios de nome/conteudo limpos para %s\n", nome);

    char qpp[PATH_MAX * 2];
    shell_quote("/var/www/html/sisc/pp", qpp, sizeof(qpp));
    snprintf(cmd, sizeof(cmd), "cd %s && %s --atualizar-catalogo-mensagens >/dev/null 2>&1", qbase, qpp);
    if (run_cmd(cmd) == 0) sb_add(log, logsz, "testesis: catalogo agregado regenerado\n");
    else sb_add(log, logsz, "testesis: aviso: catalogo agregado nao regenerou automaticamente\n");
}

static void marker_tipo_instalado(const char *destino, char *out, size_t tam) {
    if (destino_igual(destino, GC_SISC_PADRAO)) { snprintf(out, tam, "instalado"); return; }
    const char *base = strrchr(destino ? destino : "", '/');
    base = base ? base + 1 : (destino ? destino : "destino");
    char limpo[96]; size_t j = 0;
    for (size_t i = 0; base[i] && j + 1 < sizeof(limpo); i++) {
        unsigned char c = (unsigned char)base[i];
        limpo[j++] = (isalnum(c) || c == '.' || c == '_' || c == '-') ? (char)c : '-';
    }
    if (j == 0) snprintf(limpo, sizeof(limpo), "destino");
    else limpo[j] = '\0';
    snprintf(out, tam, "instalado-%s", limpo);
}

static int instalar_um(const char *pacote, const char *sisc, int dry, int forcar, int exigir_sandbox, int homologacao_sem_selo, char *nome, size_t ntam, char *log, size_t logsz) {
    char con_val[256] = "", con_sandbox[256] = "";
    if (homologacao_sem_selo && exigir_sandbox) {
        sb_add(log, logsz, "homologacao-sem-selo recusada para o SISC real\n");
        return -1;
    }
    if (!homologacao_sem_selo && gc_validar_selo_pacote(pacote, "selo-validacao", con_val, sizeof(con_val), log, logsz) != 0) {
        sb_add(log, logsz, "pacote sem selo-validacao valido\n");
        return -1;
    }
    if (homologacao_sem_selo) sb_add(log, logsz, "homologacao sem selo: revalidando pacote antes de instalar em destino alternativo\n");
    if (exigir_sandbox) {
        if (gc_validar_selo_pacote(pacote, "selo-sandbox", con_sandbox, sizeof(con_sandbox), log, logsz) != 0) {
            sb_add(log, logsz, "instalacao no SISC real bloqueada: falta selo-sandbox valido\n");
            return -1;
        }
        if (strcmp(con_val, con_sandbox) != 0) { sb_add(log, logsz, "selos pertencem a conectores diferentes\n"); return -1; }
    }

    char tipo_instalado[128], instalado[PATH_MAX];
    marker_tipo_instalado(sisc, tipo_instalado, sizeof(tipo_instalado));
    marker_path(pacote, tipo_instalado, instalado, sizeof(instalado));
    if (is_file_p(instalado) && !forcar) { sb_add(log, logsz, "pacote ja instalado neste destino (%s)\n", tipo_instalado); return 1; }
    if (!is_dir_p(sisc)) { sb_add(log, logsz, "destino SISC nao existe: %s\n", sisc); return -1; }
    char cdir[PATH_MAX], wdir[PATH_MAX]; snprintf(cdir, sizeof(cdir), "%s/conectores", sisc); snprintf(wdir, sizeof(wdir), "%s/web-api", sisc);
    if (!is_dir_p(cdir) || !is_dir_p(wdir)) { sb_add(log, logsz, "destino nao parece siscore valido\n"); return -1; }

    char tmp[PATH_MAX]; snprintf(tmp, sizeof(tmp), GC_BASE "/tmp/instalacao-c-%ld-%ld", (long)time(NULL), (long)getpid());
    int rc = -1;
    if (extrair_pacote(pacote, tmp, log, logsz) != 0) { rm_rf(tmp); return -1; }
    char root[PATH_MAX];
    if (find_project_root(tmp, root, sizeof(root)) != 0) { sb_add(log, logsz, "raiz do projeto nao encontrada\n"); rm_rf(tmp); return -1; }
    if (validar_projeto(root, nome, ntam, log, logsz) != 0) { sb_add(log, logsz, "revalidacao falhou\n"); rm_rf(tmp); return -1; }

    char manifesto_path[PATH_MAX]; snprintf(manifesto_path, sizeof(manifesto_path), "%s/conectores/%s/%s.json", root, nome, nome);
    char *man = read_file_alloc(manifesto_path, NULL); if (!man) { rm_rf(tmp); return -1; }
    Dep deps[512]; int ndeps = 0;
    parse_deps(man, deps, &ndeps);
    char handler[PATH_MAX] = "", formato[PATH_MAX] = "", manual[PATH_MAX] = "";
    json_get_string(man, "handlerLerMensagem", handler, sizeof(handler));
    json_get_string(man, "formatoConector", formato, sizeof(formato));
    if (!json_get_string(man, "manualUsuario", manual, sizeof(manual))) snprintf(manual, sizeof(manual), "conectores/%s/manual-%s.html", nome, nome);
    char *hrel = handler; if (starts_with(hrel, "./")) hrel += 2;
    char *murel = manual; if (starts_with(murel, "./")) murel += 2;
    add_dep(deps, &ndeps, hrel, hrel, "handler");
    add_dep(deps, &ndeps, formato, formato, "formato");
    add_dep(deps, &ndeps, murel, murel, "manual-usuario");
    char mrel[PATH_MAX], corig[PATH_MAX], cdest[PATH_MAX]; snprintf(mrel, sizeof(mrel), "conectores/%s/%s.json", nome, nome); snprintf(corig, sizeof(corig), "siscconectores/web-api/catalogo-%s.json", nome); snprintf(cdest, sizeof(cdest), "web-api/catalogo-%s.json", nome);
    add_dep(deps, &ndeps, mrel, mrel, "manifesto");
    add_dep(deps, &ndeps, corig, cdest, "catalogo-mensagens");
    free(man);

    char backup[PATH_MAX]; snprintf(backup, sizeof(backup), GC_BASE "/backups/%ld--%s", (long)time(NULL), nome);
    for (int i = 0; i < ndeps; i++) {
        if (!safe_rel(deps[i].origem) || !safe_rel(deps[i].destino)) { sb_add(log, logsz, "dependencia insegura: %s => %s\n", deps[i].origem, deps[i].destino); rc = -1; goto fim; }
        if (starts_with(deps[i].origem, "secretos/") || starts_with(deps[i].destino, "secretos/") || starts_with(deps[i].origem, "siscconectores/secretos/") || starts_with(deps[i].destino, "siscconectores/secretos/")) { sb_add(log, logsz, "nao copio secretos: %s\n", deps[i].destino); rc = -1; goto fim; }
        if (!starts_with(deps[i].destino, "conectores/") && !starts_with(deps[i].destino, "web-api/") && !starts_with(deps[i].destino, "siscconectores/")) { sb_add(log, logsz, "destino fora de area permitida: %s\n", deps[i].destino); rc = -1; goto fim; }
        char src[PATH_MAX], dst[PATH_MAX]; snprintf(src, sizeof(src), "%s/%s", root, deps[i].origem); snprintf(dst, sizeof(dst), "%s/%s", sisc, deps[i].destino);
        if (!is_file_p(src)) { sb_add(log, logsz, "origem ausente: %s\n", deps[i].origem); rc = -1; goto fim; }
        sb_add(log, logsz, "%s %s -> %s\n", dry ? "simular" : "copiar", deps[i].origem, deps[i].destino);
        if (!dry) {
            if (is_file_p(dst)) {
                char bk[PATH_MAX]; snprintf(bk, sizeof(bk), "%s/%s", backup, deps[i].destino);
                if (copy_file(dst, bk, 0664) != 0) { sb_add(log, logsz, "falha backup: %s\n", deps[i].destino); rc = -1; goto fim; }
            }
            int executavel = strstr(deps[i].destino, "/handlers/") ? 1 : 0;
            mode_t modo = executavel ? 0755 : 0664;
            if (copy_file(src, dst, modo) != 0) { sb_add(log, logsz, "falha copiando: %s\n", deps[i].destino); rc = -1; goto fim; }
            ajustar_permissoes_rel_destino(sisc, deps[i].destino, executavel, log, logsz);
        }
    }
    if (!dry && atualizar_catalogo(sisc, log, logsz) != 0) { rc = -1; goto fim; }
    if (!dry) {
        char arvore_conector[PATH_MAX];
        snprintf(arvore_conector, sizeof(arvore_conector), "%s/conectores/%s", sisc, nome);
        normalizar_arvore_instalada(arvore_conector, log, logsz);
    }
    if (!dry && exigir_sandbox) limpar_testesis_conector_aprovado(nome, log, logsz);
    rc = 0;
fim:
    rm_rf(tmp);
    return rc;
}

static int listar_aprovados(char arr[][PATH_MAX], int max) {
    DIR *d = opendir(GC_RECEBIDOS); if (!d) return 0;
    int n = 0; struct dirent *e;
    while ((e = readdir(d)) && n < max) {
        if (!ends_with(e->d_name, ".selo-validacao.json")) continue;
        char pacote[PATH_MAX]; snprintf(pacote, sizeof(pacote), "%s/%s", GC_RECEBIDOS, e->d_name);
        pacote[strlen(pacote) - strlen(".selo-validacao.json")] = '\0';
        if (is_file_p(pacote)) snprintf(arr[n++], PATH_MAX, "%s", pacote);
    }
    closedir(d);
    return n;
}

int main(int argc, char **argv) {
    const char *destino = GC_SISC_PADRAO, *somente = NULL;
    int dry = 0, forcar = 0, homologacao_sem_selo = 0;
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--dry-run") == 0) dry = 1;
        else if (strcmp(argv[i], "--homologacao-sem-selo") == 0 || strcmp(argv[i], "--sem-selo") == 0) homologacao_sem_selo = 1;
        else if (strcmp(argv[i], "--forcar") == 0 || strcmp(argv[i], "--force") == 0) forcar = 1;
        else if (starts_with(argv[i], "--destino=")) destino = argv[i] + 10;
        else if (starts_with(argv[i], "--pacote=")) somente = argv[i] + 9;
        else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--ajuda") == 0) { uso(); return 0; }
        else { fprintf(stderr, "argumento invalido: %s\n", argv[i]); uso(); return 2; }
    }
    mkdir_p(GC_RECEBIDOS, 0777); chmod(GC_RECEBIDOS, 01777);
    mkdir_p(GC_BASE "/tmp", 0770); chmod(GC_BASE "/tmp", 0770);
    mkdir_p(GC_BASE "/backups", 0770);
    static char pacotes[4096][PATH_MAX]; int total = 0;
    if (somente) snprintf(pacotes[total++], PATH_MAX, "%s", somente);
    else total = listar_aprovados(pacotes, 4096);
    if (total == 0) { printf("Nenhum pacote com selo-validacao encontrado em %s\n", GC_RECEBIDOS); return 0; }

    int exigir_sandbox = destino_igual(destino, GC_SISC_PADRAO);
    if (homologacao_sem_selo && exigir_sandbox) {
        fprintf(stderr, "instalar-aprovados: --homologacao-sem-selo nunca pode usar o destino SISC real.\n");
        return 2;
    }
    int okn = 0, errn = 0, ign = 0;
    for (int i = 0; i < total; i++) {
        char pacote_real[PATH_MAX];
        if (canonical_path(pacotes[i], pacote_real, sizeof(pacote_real)) != 0) { printf("[ERRO] pacote invalido: %s\n", pacotes[i]); errn++; continue; }
        char nome[256] = "", log[131072] = "";
        int rc = instalar_um(pacote_real, destino, dry, forcar, exigir_sandbox, homologacao_sem_selo, nome, sizeof(nome), log, sizeof(log));
        if (rc == 0) {
            printf("[%s] %s => %s\n", dry ? "SIMULADO" : "INSTALADO", pacote_real, nome);
            if (!dry) {
                char tipo_instalado[128]; marker_tipo_instalado(destino, tipo_instalado, sizeof(tipo_instalado));
                char marca[PATH_MAX], sha[128], json[16384]; marker_path(pacote_real, tipo_instalado, marca, sizeof(marca)); sha256_file(pacote_real, sha, sizeof(sha));
                char epac[PATH_MAX * 2], enome[512], edest[PATH_MAX * 2], esha[160], etipo[160];
                gc_json_escape_copy(pacote_real, epac, sizeof(epac)); gc_json_escape_copy(nome, enome, sizeof(enome));
                gc_json_escape_copy(destino, edest, sizeof(edest)); gc_json_escape_copy(sha, esha, sizeof(esha)); gc_json_escape_copy(tipo_instalado, etipo, sizeof(etipo));
                snprintf(json, sizeof(json), "{\n  \"status\": \"%s\",\n  \"pacote\": \"%s\",\n  \"sha256\": \"%s\",\n  \"conector\": \"%s\",\n  \"destino\": \"%s\",\n  \"seloValidacaoConferido\": %s,\n  \"seloSandboxConferido\": %s,\n  \"homologacaoSemSelo\": %s,\n  \"instaladoEm\": %ld,\n  \"instalador\": \"instalar-aprovados.c\"\n}\n", etipo, epac, esha, enome, edest, homologacao_sem_selo ? "false" : "true", exigir_sandbox ? "true" : "false", homologacao_sem_selo ? "true" : "false", (long)time(NULL));
                write_text_file(marca, json, 0444);
                chmod(marca, 0444);
                ajustar_marca_do_pacote(pacote_real, marca, 0444, log, sizeof(log));
                char lp[PATH_MAX]; snprintf(lp, sizeof(lp), "%s.log", marca); write_text_file(lp, log, 0664); ajustar_marca_do_pacote(pacote_real, lp, 0664, log, sizeof(log));
            }
            okn++;
        } else if (rc == 1) { printf("[IGNORADO] %s ja instalado neste destino\n", pacote_real); ign++; }
        else {
            printf("[ERRO] %s => %s\n", pacote_real, log[0] ? log : "erro");
            char marca[PATH_MAX]; marker_path(pacote_real, "falha-instalacao", marca, sizeof(marca));
            write_text_file(marca, "{\n  \"status\": \"falha-instalacao\"\n}\n", 0664);
            ajustar_marca_do_pacote(pacote_real, marca, 0664, log, sizeof(log));
            char lp[PATH_MAX]; snprintf(lp, sizeof(lp), "%s.log", marca); write_text_file(lp, log, 0664); ajustar_marca_do_pacote(pacote_real, lp, 0664, log, sizeof(log));
            errn++;
        }
    }
    printf("\nResumo: total=%d instalados=%d erros=%d ignorados=%d destino=%s%s%s\n", total, okn, errn, ign, destino, dry ? " dry-run" : "", exigir_sandbox ? " exigiu-selo-sandbox" : "");
    return errn > 0 ? 1 : 0;
}
