/*
 * criar-sistema - Cria a estrutura de diretorios de um novo sistema
 * Uso: cd sisc && pp --criar-sistema=<nome>
 *
 * Deve ser executado diretamente no diretorio sisc.
 * Le recursivamente a estrutura de diretorios presente em siscore/ e cria a
 * mesma estrutura no novo sistema. Arquivos nao sao espelhados: copia apenas
 * os arquivos essenciais relacionados abaixo.
 * Specs NAO sao copiadas para o sistema: a fonte canonica permanece em
 * core/biblioteca-specs/. O template atual preserva a operacao de conectores,
 * API declarativa, front-api AJAX e escuta. spec-diretivas/ local e apenas area opcional
 * para diretivas/sobrescritas especificas do sistema. relspec/ e criado
 * como area local para relatorios/artefatos derivados de specs.
 * Se o destino ja existir, a criacao e recusada para evitar mistura de
 * artefatos internos e entrega existente.
 */

#define _GNU_SOURCE
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>

static gid_t gid_wwwdata_cache = (gid_t)-1;
static int gid_wwwdata_cache_ok = 0;

static gid_t obter_gid_wwwdata(void)
{
    if (!gid_wwwdata_cache_ok) {
        struct group *g = getgrnam("www-data");
        gid_wwwdata_cache = g ? g->gr_gid : (gid_t)-1;
        gid_wwwdata_cache_ok = 1;
    }
    return gid_wwwdata_cache;
}

static int ajustar_grupo_espaco(const char *path)
{
    gid_t g = obter_gid_wwwdata();
    if (g == (gid_t)-1) return -1;
    return chown(path, (uid_t)-1, g);
}

static void aplicar_permissao_espaco(const char *path)
{
    /* Tenta mover o grupo para www-data. Se conseguir, restringe para
     * 03775 (rwxrwsrwt): escrita apenas para dono e grupo www-data,
     * com setgid para herdar o grupo nos novos arquivos e sticky bit
     * para impedir remocao por usuarios que nao sejam donos/root.
     * Se nao conseguir (usuario comum sem privilegio de chown), mantem
     * 03777 (rwxrwxrwt) para que o servidor web escreva via "outros". */
    if (ajustar_grupo_espaco(path) == 0) {
        chmod(path, 03775);
    } else {
        chmod(path, 03777);
    }
}

static void aplicar_permissao_secretos(const char *destino_dir)
{
    char dir[PATH_MAX * 2], cred[PATH_MAX * 2];
    snprintf(dir, sizeof(dir), "%s/secretos", destino_dir);
    snprintf(cred, sizeof(cred), "%s/secretos/conector-email.json", destino_dir);

    gid_t g = obter_gid_wwwdata();
    if (g != (gid_t)-1 && chown(dir, (uid_t)-1, g) == 0) {
        chmod(dir, 0750);
    } else {
        chmod(dir, 0755);
    }

    struct stat st;
    if (stat(cred, &st) == 0 && S_ISREG(st.st_mode)) {
        if (g != (gid_t)-1 && chown(cred, (uid_t)-1, g) == 0) {
            chmod(cred, 0640);
        } else {
            chmod(cred, 0644);
        }
    }
}

static int termina_com(const char *s, const char *suf)
{
    if (!s || !suf) return 0;
    size_t ls = strlen(s), lsuf = strlen(suf);
    return lsuf <= ls && strcmp(s + ls - lsuf, suf) == 0;
}

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --criar-sistema=<nome>\n"
        "  pp --criar-sistema --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Deve ser executado diretamente no diretório sisc. Cria a estrutura de um\n"
        "  novo sistema a partir do template siscore/. Specs não são copiadas: a fonte\n"
        "  canônica continua em core/biblioteca-specs/.\n"
        "\n"
        "Grupos de ajuda: uso, entrada, processo, arquivos, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>                Filtra um grupo. Ex: pp --criar-sistema --ajuda:processo\n"
        "\n"
        "2) Entrada [entrada]\n"
        "  --criar-sistema=<nome>         Nome do sistema; normalizado para minúsculas e hífens\n"
        "\n"
        "3) Processo [processo comportamento]\n"
        "  Lê recursivamente os diretórios existentes em siscore/ e recria a mesma\n"
        "  estrutura no novo sistema. Arquivos não são espelhados em massa.\n"
        "  Copia somente os arquivos essenciais atualmente relacionados e não copia specs.\n"
        "  Se o destino já existir, a criação é recusada para evitar mistura de\n"
        "  artefatos.\n"
        "  Diretórios de espaco/ são criados com permissão compatível com api.php\n"
        "  via servidor web e escuta local, sem expor acesso HTTP por causa do .htaccess.\n"
        "  Exemplo com todas as flags do grupo:\n"
        "    cd /var/www/html/sisc && pp --criar-sistema=meu-projeto\n"
        "\n"
        "4) Arquivos principais [arquivos]\n"
        "  conectores/, conexao-externo/ (api.php e front-api.php),\n"
        "  public/ (sisc-api-cliente.php), escuta/, web-api/, formatos-escuta/,\n"
        "  token-externo/, relspec/\n"
        "  spec-diretivas/ pode existir como área local opcional de diretivas/overrides;\n"
        "  relspec/ é a área local de relatórios/artefatos derivados de specs;\n"
        "  a biblioteca padrão de specs fica somente em core/biblioteca-specs/.\n"
        "\n"
        "5) Exemplos [exemplos]\n"
        "  cd /var/www/html/sisc && pp --criar-sistema=meu-projeto\n"
        "  cd /var/www/html/sisc && pp --criar-sistema='Meu Projeto'\n",
        stdout);
}

static int eh_dir(const char *p) { struct stat st; return stat(p, &st) == 0 && S_ISDIR(st.st_mode); }


static int normalizar_nome(const char *in, char *out, size_t tam)
{
    if (!in || !*in || !out || tam == 0) return -1;
    char tmp[PATH_MAX];
    snprintf(tmp, sizeof(tmp), "%s", in);

    char *v = tmp;
    while (isspace((unsigned char)*v)) v++;
    /* Remove prefixos com caminho */
    if (strchr(v, '/') || strchr(v, '\\')) return -1;
    if (strncmp(v, "siscore", 7) == 0) return -1;

    size_t j = 0;
    int last_sep = 0;
    for (size_t i = 0; v[i] && j + 1 < tam; i++) {
        unsigned char c = (unsigned char)v[i];
        if (isalnum(c)) {
            out[j++] = (char)tolower(c);
            last_sep = 0;
        } else if (c == '-' || c == '_' || isspace(c)) {
            if (!last_sep && j > 0) out[j++] = '-';
            last_sep = 1;
        } else if (c >= 128) {
            if (!last_sep && j > 0) out[j++] = '-';
            last_sep = 1;
        }
    }
    while (j > 0 && (out[j - 1] == '-' || out[j - 1] == '_')) j--;
    out[j] = '\0';
    return j > 0 ? 0 : -1;
}

static int criar_dir(const char *path, mode_t modo)
{
    struct stat st;
    if (stat(path, &st) == 0) {
        if (S_ISDIR(st.st_mode)) {
            /* Se as permissoes estao diferentes, ajusta */
            if ((st.st_mode & 07777) != (modo & 07777)) {
                chmod(path, modo);
            }
            return 0;
        }
        /* Existe mas nao eh diretorio - erro */
        return -1;
    }
    if (mkdir(path, modo) != 0 && errno != EEXIST) return -1;
    chmod(path, modo);
    return 0;
}

static int copiar_arquivo_simples(const char *src_path, const char *dst_path, mode_t modo)
{
    FILE *fsrc = fopen(src_path, "rb");
    if (!fsrc) return -1;
    FILE *fdst = fopen(dst_path, "wb");
    if (!fdst) { fclose(fsrc); return -1; }
    char buf[65536];
    size_t n;
    while ((n = fread(buf, 1, sizeof(buf), fsrc)) > 0) {
        if (fwrite(buf, 1, n, fdst) != n) {
            fclose(fsrc); fclose(fdst);
            return -1;
        }
    }
    int erro = ferror(fsrc) ? -1 : 0;
    fclose(fsrc);
    if (fclose(fdst) != 0) erro = -1;
    if (erro == 0) chmod(dst_path, modo);
    return erro;
}

/* Copia os arquivos do diretorio escuta/ (excecao a regra de apenas diretorios).
 * Retorna o numero de arquivos copiados, ou -1 em caso de erro. */
static int copiar_arquivos_escuta(const char *origem_escuta, const char *destino_escuta, int *copiados)
{
    DIR *d = opendir(origem_escuta);
    if (!d) return 0;  /* escuta/ pode nao existir no template - ok */

    struct dirent *e;
    while ((e = readdir(d))) {
        const char *nome = e->d_name;
        if (nome[0] == '.') continue;

        char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
        snprintf(src_path, sizeof(src_path), "%s/%s", origem_escuta, nome);
        snprintf(dst_path, sizeof(dst_path), "%s/%s", destino_escuta, nome);

        struct stat st;
        if (stat(src_path, &st) != 0) continue;
        if (!S_ISREG(st.st_mode)) continue;

        mode_t modo = (st.st_mode & 0111) ? 0755 : 0664;
        if (!strcmp(nome, "escuta") || !strcmp(nome, "runtime-agente") || !strcmp(nome, "runtime-conector")) modo = 0755;

        if (copiar_arquivo_simples(src_path, dst_path, modo) != 0) {
            fprintf(stderr, "  [erro] falha ao copiar: %s\n", dst_path);
            closedir(d);
            return -1;
        }
        printf("  [copiado] escuta/%s%s\n", nome, (modo & 0111) ? " (executavel)" : "");
        (*copiados)++;
    }
    closedir(d);
    return 0;
}

static int copiar_arquivos_public(const char *raiz_sisc, const char *destino_dir)
{
    /* Copia todos os arquivos regulares de siscore/public/ para <destino>/public/.
     * A pasta public/ e a interface exemplo-cliente.php fazem parte da entrega ao cliente. */
    char origem_dir[PATH_MAX * 2], destino_public[PATH_MAX * 2];
    snprintf(origem_dir, sizeof(origem_dir), "%s/siscore/public", raiz_sisc);
    snprintf(destino_public, sizeof(destino_public), "%s/public", destino_dir);

    DIR *d = opendir(origem_dir);
    if (!d) return 0; /* public/ pode nao existir no template - ok */

    int copiados = 0;
    struct dirent *e;
    while ((e = readdir(d))) {
        const char *nome = e->d_name;
        if (nome[0] == '.') continue;

        char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
        snprintf(src_path, sizeof(src_path), "%s/%s", origem_dir, nome);
        snprintf(dst_path, sizeof(dst_path), "%s/%s", destino_public, nome);

        struct stat st;
        if (stat(src_path, &st) != 0 || !S_ISREG(st.st_mode)) continue;

        int ok = copiar_arquivo_simples(src_path, dst_path, 0664);
        if (ok != 0) {
            fprintf(stderr, "  [erro] falha ao copiar: %s\n", dst_path);
            closedir(d);
            return -1;
        }
        printf("  [copiado] public/%s\n", nome);
        copiados++;
    }
    closedir(d);
    return copiados;
}

/* Copia o exemplo-cliente.php (se existir em siscore/testes/exemplo-cliente/)
 * para a raiz do novo sistema, ajustando o require para o sisc-api-cliente.php
 * local em public/. */
/* Substitui em *conteudo (alocacao dinamica) todas as ocorrencias de agulha
 * por substituto. Atualiza *tamanho. Retorna 0 em sucesso, -1 em erro. */
static int substituir_texto(char **conteudo, long *tamanho, const char *agulha, const char *substituto)
{
    char *p = strstr(*conteudo, agulha);
    if (!p) return 0;
    size_t len_agulha = strlen(agulha);
    size_t len_sub = strlen(substituto);
    size_t novo_tamanho = strlen(*conteudo) - len_agulha + len_sub;
    char *novo = malloc(novo_tamanho + 1);
    if (!novo) return -1;
    size_t prefixo = (size_t)(p - *conteudo);
    memcpy(novo, *conteudo, prefixo);
    memcpy(novo + prefixo, substituto, len_sub);
    strcpy(novo + prefixo + len_sub, p + len_agulha);
    free(*conteudo);
    *conteudo = novo;
    *tamanho = (long)novo_tamanho;
    return 0;
}

static int copiar_exemplo_cliente(const char *raiz_sisc, const char *destino_dir,
                                  const char *nome_sistema)
{
    char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
    snprintf(src_path, sizeof(src_path), "%s/siscore/testes/exemplo-cliente/exemplo-cliente.php", raiz_sisc);
    snprintf(dst_path, sizeof(dst_path), "%s/exemplo-cliente.php", destino_dir);

    struct stat st;
    if (stat(src_path, &st) != 0 || !S_ISREG(st.st_mode)) return 0; /* opcional */

    FILE *fsrc = fopen(src_path, "rb");
    if (!fsrc) return -1;
    if (fseek(fsrc, 0, SEEK_END) != 0) { fclose(fsrc); return -1; }
    long tamanho = ftell(fsrc);
    if (tamanho < 0) { fclose(fsrc); return -1; }
    rewind(fsrc);

    char *conteudo = malloc((size_t)tamanho + 1);
    if (!conteudo) { fclose(fsrc); return -1; }
    if (fread(conteudo, 1, (size_t)tamanho, fsrc) != (size_t)tamanho) {
        free(conteudo); fclose(fsrc); return -1;
    }
    fclose(fsrc);
    conteudo[tamanho] = '\0';

    const char *require_padrao = "require_once __DIR__ . '/../../../siscore/public/sisc-api-cliente.php';";
    const char *require_novo = "require_once __DIR__ . '/public/sisc-api-cliente.php';";
    if (substituir_texto(&conteudo, &tamanho, require_padrao, require_novo) != 0) {
        free(conteudo); return -1;
    }

    char sistema_padrao[] = "__SISC_SISTEMA__";
    if (substituir_texto(&conteudo, &tamanho, sistema_padrao, nome_sistema) != 0) {
        free(conteudo); return -1;
    }

    FILE *fdst = fopen(dst_path, "wb");
    if (!fdst) { free(conteudo); return -1; }
    if (fwrite(conteudo, 1, (size_t)tamanho, fdst) != (size_t)tamanho) {
        free(conteudo); fclose(fdst); return -1;
    }
    free(conteudo);
    if (fclose(fdst) != 0) return -1;
    chmod(dst_path, 0664);
    printf("  [copiado] exemplo-cliente.php\n");
    return 1;
}

/* Tokens de cliente SISC nao sao duplicados: o unico local canonico e token-externo/<cliente>.txt. */
static int copiar_makefile_raiz(const char *template_dir, const char *destino_dir)
{
    char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
    snprintf(src_path, sizeof(src_path), "%s/Makefile", template_dir);
    snprintf(dst_path, sizeof(dst_path), "%s/Makefile", destino_dir);
    struct stat st;
    if (stat(src_path, &st) != 0 || !S_ISREG(st.st_mode)) return 0;
    if (copiar_arquivo_simples(src_path, dst_path, 0664) != 0) return -1;
    printf("  [copiado] Makefile\n");
    return 1;
}

/* Copia todos os arquivos regulares de um subdiretorio do template para o
 * destino, preservando o modo. Usado para conexao-externo/ (api.php/front-api.php)
 * e token-externo/. */
static int copiar_arquivos_de_subdir(const char *template_dir, const char *destino_dir,
                                     const char *subdir, int *copiados_out)
{
    char origem[PATH_MAX * 2], destino[PATH_MAX * 2];
    snprintf(origem, sizeof(origem), "%s/%s", template_dir, subdir);
    snprintf(destino, sizeof(destino), "%s/%s", destino_dir, subdir);

    DIR *d = opendir(origem);
    if (!d) return 0; /* subdir pode nao existir no template - ok */

    int copiados = 0;
    struct dirent *e;
    while ((e = readdir(d))) {
        const char *nome = e->d_name;
        if (nome[0] == '.') continue;

        char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
        snprintf(src_path, sizeof(src_path), "%s/%s", origem, nome);
        snprintf(dst_path, sizeof(dst_path), "%s/%s", destino, nome);

        struct stat st;
        if (stat(src_path, &st) != 0) continue;
        if (!S_ISREG(st.st_mode)) continue;

        mode_t modo;
        if (strcmp(subdir, "conexao-externo") == 0) {
            modo = 0644; /* servidor web precisa ler api.php/front-api.php */
        } else if (strcmp(subdir, "token-externo") == 0) {
            modo = 0644; /* tokens: servidor web precisa validar no api.php */
        } else {
            modo = st.st_mode & 0777;
        }
        /* Handlers de conectores precisam ser executaveis pela escuta. */
        if (strstr(subdir, "handlers") && termina_com(nome, ".php")) {
            modo = 0755;
        }
        if (copiar_arquivo_simples(src_path, dst_path, modo) != 0) {
            fprintf(stderr, "  [erro] falha ao copiar: %s\n", dst_path);
            closedir(d);
            return -1;
        }
        printf("  [copiado] %s/%s\n", subdir, nome);
        copiados++;
    }
    closedir(d);
    if (copiados_out) *copiados_out = copiados;
    return 0;
}

static int copiar_conector_email_padrao(const char *template_dir, const char *destino_dir)
{
    const char *arquivos[] = {
        "conectores/conector-email/conector-email.json",
        "conectores/conector-email/formatos/formato-conector-email.json",
        "conectores/conector-email/handlers/conector-email.php",
        NULL
    };
    int copiados = 0;
    for (int i = 0; arquivos[i]; i++) {
        char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
        snprintf(src_path, sizeof(src_path), "%s/%s", template_dir, arquivos[i]);
        snprintf(dst_path, sizeof(dst_path), "%s/%s", destino_dir, arquivos[i]);
        struct stat st;
        if (stat(src_path, &st) != 0 || !S_ISREG(st.st_mode)) continue;
        mode_t modo = 0664;
        if (strstr(arquivos[i], "handlers/") && termina_com(arquivos[i], ".php")) modo = 0755;
        if (copiar_arquivo_simples(src_path, dst_path, modo) != 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar %s\n", arquivos[i]);
            return -1;
        }
        printf("  [copiado] %s%s\n", arquivos[i], (modo & 0111) ? " (executavel)" : "");
        copiados++;
    }

    /* Credenciais do conector-email: se ja existir em siscore/secretos/,
     * copia a configuracao pronta (com credenciais reais). Caso contrario,
     * cria um template vazio para o operador preencher. */
    char cred_path[PATH_MAX * 2];
    snprintf(cred_path, sizeof(cred_path), "%s/secretos/conector-email.json", destino_dir);
    if (access(cred_path, F_OK) != 0) {
        char cred_src[PATH_MAX * 2];
        snprintf(cred_src, sizeof(cred_src), "%s/secretos/conector-email.json", template_dir);
        struct stat st_cred;
        if (stat(cred_src, &st_cred) == 0 && S_ISREG(st_cred.st_mode)) {
            if (copiar_arquivo_simples(cred_src, cred_path, 0600) != 0) {
                fprintf(stderr, "criar-sistema: erro ao copiar credenciais do conector-email\n");
                return -1;
            }
            printf("  [copiado] secretos/conector-email.json (configuracao pronta do siscore)\n");
        } else {
            const char *tpl =
                "{\n"
                "  \"tipo\": \"credenciais-conector-email\",\n"
                "  \"nome\": \"conector-email\",\n"
                "  \"ativo\": false,\n"
                "  \"descricao\": \"Configuracao local do conector-email. Preencha com dados reais no ambiente seguro.\",\n"
                "  \"smtp\": {\n"
                "    \"ativo\": false,\n"
                "    \"host\": \"smtp.exemplo.com\",\n"
                "    \"porta\": 587,\n"
                "    \"criptografia\": \"tls\",\n"
                "    \"usuario\": \"\",\n"
                "    \"senha\": \"\",\n"
                "    \"remetenteEmail\": \"\",\n"
                "    \"remetenteNome\": \"SISC\"\n"
                "  },\n"
                "  \"recebimento\": {\n"
                "    \"ativo\": false,\n"
                "    \"protocolo\": \"imap\",\n"
                "    \"host\": \"imap.exemplo.com\",\n"
                "    \"porta\": 993,\n"
                "    \"criptografia\": \"ssl\",\n"
                "    \"usuario\": \"\",\n"
                "    \"senha\": \"\",\n"
                "    \"caixa\": \"INBOX\",\n"
                "    \"limiteMensagens\": 10,\n"
                "    \"marcarComoLida\": false\n"
                "  }\n"
                "}\n";
            FILE *f = fopen(cred_path, "wb");
            if (!f) {
                fprintf(stderr, "criar-sistema: erro ao criar template de credenciais do conector-email\n");
                return -1;
            }
            fputs(tpl, f);
            if (fclose(f) != 0) return -1;
            chmod(cred_path, 0600);
            printf("  [criado] secretos/conector-email.json (template sem credenciais)\n");
        }
    }
    return copiados;
}

static int copiar_arquivos_essenciais_sisc(const char *template_dir, const char *destino_dir)
{
    const char *arquivos[] = {
        "web-api/catalogo-mensagens.json",
        "formatos-escuta/protocolo-mensagem-siscore.v1.json"
    };
    int copiados = 0;
    for (size_t i = 0; i < sizeof(arquivos)/sizeof(arquivos[0]); i++) {
        char src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
        snprintf(src_path, sizeof(src_path), "%s/%s", template_dir, arquivos[i]);
        snprintf(dst_path, sizeof(dst_path), "%s/%s", destino_dir, arquivos[i]);
        struct stat st;
        if (stat(src_path, &st) != 0 || !S_ISREG(st.st_mode)) continue;
        if (access(dst_path, F_OK) == 0) {
            printf("  [existente] %s\n", arquivos[i]);
            continue;
        }
        if (copiar_arquivo_simples(src_path, dst_path, 0664) != 0) return -1;
        printf("  [copiado] %s\n", arquivos[i]);
        copiados++;
    }

    /* Copia tambem catalogos modulares web-api/catalogo-*.json. O arquivo
     * catalogo-mensagens.json continua sendo o agregado gerado para compatibilidade. */
    char web_template[PATH_MAX * 2];
    snprintf(web_template, sizeof(web_template), "%s/web-api", template_dir);
    DIR *d = opendir(web_template);
    if (d) {
        struct dirent *e;
        while ((e = readdir(d))) {
            const char *nome = e->d_name;
            if (strncmp(nome, "catalogo-", 9) != 0 || !termina_com(nome, ".json") || strcmp(nome, "catalogo-mensagens.json") == 0) continue;
            char rel[PATH_MAX * 2], src_path[PATH_MAX * 2], dst_path[PATH_MAX * 2];
            snprintf(rel, sizeof(rel), "web-api/%s", nome);
            snprintf(src_path, sizeof(src_path), "%s/%s", template_dir, rel);
            snprintf(dst_path, sizeof(dst_path), "%s/%s", destino_dir, rel);
            struct stat st;
            if (stat(src_path, &st) != 0 || !S_ISREG(st.st_mode)) continue;
            if (access(dst_path, F_OK) == 0) {
                printf("  [existente] %s\n", rel);
                continue;
            }
            if (copiar_arquivo_simples(src_path, dst_path, 0664) != 0) { closedir(d); return -1; }
            printf("  [copiado] %s\n", rel);
            copiados++;
        }
        closedir(d);
    }
    return copiados;
}

static int criar_htaccess_negado(const char *destino_dir, const char *subdir)
{
    char dir[PATH_MAX * 2], path[PATH_MAX * 2];
    snprintf(dir, sizeof(dir), "%s/%s", destino_dir, subdir);
    if (!eh_dir(dir)) return 0;
    snprintf(path, sizeof(path), "%s/.htaccess", dir);
    FILE *f = fopen(path, "wb");
    if (!f) return -1;
    fputs("# SISC: bloqueia acesso HTTP ao diretorio sensivel.\n"
          "# Nao altera leitura local por processos do servidor/CLI.\n"
          "<IfModule mod_authz_core.c>\n"
          "    Require all denied\n"
          "</IfModule>\n"
          "<IfModule !mod_authz_core.c>\n"
          "    Order deny,allow\n"
          "    Deny from all\n"
          "</IfModule>\n", f);
    if (fclose(f) != 0) return -1;
    chmod(path, 0644);
    printf("  [protegido] %s/.htaccess\n", subdir);
    return 0;
}

static mode_t modo_dir_template(const char *rel)
{
    if (!rel) return 0775;
    if (!strcmp(rel, "secretos") || !strncmp(rel, "secretos/", 9)) return 0700;

    /*
     * O api.php publica mensagens como usuario do servidor web (ex: www-data)
     * e a escuta pode rodar como operador local. O modo 03777 em espaco/
     * garante:
     *   - escrita pelo servidor web mesmo sem grupo comum;
     *   - heranca do grupo do diretorio (setgid), permitindo leitura pela escuta;
     *   - protecao contra remocao por usuarios que nao sejam dono do arquivo,
     *     dono do diretorio ou root (sticky bit).
     * O acesso HTTP continua bloqueado pelo .htaccess criado para espaco/.
     */
    if (!strcmp(rel, "espaco") || !strncmp(rel, "espaco/", 7)) return 03777;

    return 0775;
}

/* Recria, no destino, somente a estrutura de diretorios existente em siscore/.
 * Arquivos continuam sendo copiados apenas pelas rotinas explicitas de arquivos
 * relacionados/essenciais. */
static int copiar_estrutura_rec(const char *origem_base, const char *destino_base, const char *rel, int *criados)
{
    char origem_atual[PATH_MAX * 2];
    if (rel && *rel) snprintf(origem_atual, sizeof(origem_atual), "%s/%s", origem_base, rel);
    else snprintf(origem_atual, sizeof(origem_atual), "%s", origem_base);

    DIR *d = opendir(origem_atual);
    if (!d) {
        fprintf(stderr, "  [erro] nao foi possivel ler diretorio template: %s\n", origem_atual);
        return -1;
    }

    struct dirent *e;
    while ((e = readdir(d))) {
        const char *nome = e->d_name;
        if (!strcmp(nome, ".") || !strcmp(nome, "..") || nome[0] == '.') continue;

        char rel_filho[PATH_MAX * 2];
        if (rel && *rel) snprintf(rel_filho, sizeof(rel_filho), "%s/%s", rel, nome);
        else snprintf(rel_filho, sizeof(rel_filho), "%s", nome);

        char origem_filho[PATH_MAX * 2];
        snprintf(origem_filho, sizeof(origem_filho), "%s/%s", origem_base, rel_filho);

        struct stat st;
        if (lstat(origem_filho, &st) != 0) continue;
        if (!S_ISDIR(st.st_mode)) continue;

        char destino_filho[PATH_MAX * 2];
        snprintf(destino_filho, sizeof(destino_filho), "%s/%s", destino_base, rel_filho);

        int ja_existia = eh_dir(destino_filho);
        if (criar_dir(destino_filho, modo_dir_template(rel_filho)) != 0) {
            fprintf(stderr, "  [erro] nao foi possivel criar: %s/\n", rel_filho);
            closedir(d);
            return -1;
        }
        if (strcmp(rel_filho, "espaco") == 0 || strncmp(rel_filho, "espaco/", 7) == 0) {
            aplicar_permissao_espaco(destino_filho);
        }
        if (!ja_existia) {
            printf("  [criado] %s/\n", rel_filho);
            (*criados)++;
        } else {
            printf("  [existente] %s/\n", rel_filho);
        }

        if (copiar_estrutura_rec(origem_base, destino_base, rel_filho, criados) != 0) {
            closedir(d);
            return -1;
        }
    }

    closedir(d);
    return 0;
}

static int copiar_estrutura(const char *origem, const char *destino, int *criados)
{
    return copiar_estrutura_rec(origem, destino, "", criados);
}

static int garantir_relspec_dir(const char *destino_dir, int *criados)
{
    char path[PATH_MAX * 2];
    snprintf(path, sizeof(path), "%s/relspec", destino_dir);
    int ja_existia = eh_dir(path);
    if (criar_dir(path, 0775) != 0) return -1;
    if (!ja_existia) {
        printf("  [criado] relspec/\n");
        if (criados) (*criados)++;
    }
    return 0;
}

static int obter_raiz_sisc(char *out, size_t outsz)
{
    if (!out || outsz == 0) return -1;

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

    /* Sobe 3 níveis: core/comandos/<exe> -> core/comandos -> core -> sisc */
    for (int i = 0; i < 3; i++) {
        char *slash = strrchr(exe, '/');
        if (!slash) return -1;
        *slash = '\0';
    }

    snprintf(out, outsz, "%s", exe);
    return 0;
}

static int verificar_execucao_em_sisc(char *raiz_sisc, size_t raiz_sz)
{
    char cwd[PATH_MAX], real_cwd[PATH_MAX], raiz[PATH_MAX], real_raiz[PATH_MAX];

    if (obter_raiz_sisc(raiz, sizeof(raiz)) != 0) {
        fprintf(stderr, "criar-sistema: nao foi possivel determinar raiz sisc.\n");
        return -1;
    }
    if (!getcwd(cwd, sizeof(cwd))) { perror("getcwd"); return -1; }
    if (!realpath(cwd, real_cwd)) snprintf(real_cwd, sizeof(real_cwd), "%s", cwd);
    if (!realpath(raiz, real_raiz)) snprintf(real_raiz, sizeof(real_raiz), "%s", raiz);

    if (strcmp(real_cwd, real_raiz) != 0) {
        fprintf(stderr, "criar-sistema: so pode ser executado diretamente no diretorio sisc.\n");
        fprintf(stderr, "Diretorio sisc: %s\n", real_raiz);
        fprintf(stderr, "Diretorio atual: %s\n", real_cwd);
        return -1;
    }

    if (raiz_sisc && raiz_sz > 0) snprintf(raiz_sisc, raiz_sz, "%s", real_raiz);
    return 0;
}

int main(int argc, char **argv)
{
    char raiz_sisc[PATH_MAX];
    if (verificar_execucao_em_sisc(raiz_sisc, sizeof(raiz_sisc)) != 0) return 1;

    if (argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "--ajuda") || !strcmp(argv[1], "-h")) {
        uso();
        return (argc == 2 && (argv[1][0] == '-')) ? 0 : (argc < 2 ? 1 : 0);
    }

    /* Normaliza o nome do sistema destino */
    char nome_destino[256];
    if (normalizar_nome(argv[1], nome_destino, sizeof(nome_destino)) != 0) {
        fprintf(stderr, "criar-sistema: nome de sistema invalido: %s\n", argv[1]);
        fprintf(stderr, "Use apenas letras, numeros e hifens. Nao use 'siscore' como nome.\n");
        return 1;
    }

    /* Template de origem: procura siscore/ na raiz do sisc */
    char template_dir[PATH_MAX * 2];
    snprintf(template_dir, sizeof(template_dir), "%s/siscore", raiz_sisc);
    if (!eh_dir(template_dir)) {
        fprintf(stderr, "criar-sistema: template siscore/ nao encontrado em %s\n", raiz_sisc);
        return 1;
    }

    /* Destino */
    char destino_dir[PATH_MAX * 2];
    snprintf(destino_dir, sizeof(destino_dir), "%s/%s", raiz_sisc, nome_destino);

    printf("Template: %s\n", template_dir);
    printf("Destino:  %s\n\n", destino_dir);

    int ja_existia_sistema = eh_dir(destino_dir);
    if (ja_existia_sistema) {
        fprintf(stderr, "criar-sistema: sistema ja existe em sisc/%s\n", nome_destino);
        fprintf(stderr, "criar-sistema: nenhuma atualizacao foi realizada; a criacao e somente inicial.\n");
        fprintf(stderr, "criar-sistema: escolha outro nome ou remova/arquive o diretorio existente antes de criar.\n");
        return 1;
    }

    /* Cria o diretorio raiz do sistema */
    int criados_total = 0;
    if (criar_dir(destino_dir, 0775) != 0) {
        fprintf(stderr, "criar-sistema: nao foi possivel criar %s\n", destino_dir);
        return 1;
    }
    if (!ja_existia_sistema) {
        printf("[criado] %s/\n", nome_destino);
        criados_total++;
    }

    /* Copia a estrutura de diretorios de siscore */
    if (copiar_estrutura(template_dir, destino_dir, &criados_total) != 0) {
        fprintf(stderr, "criar-sistema: erro ao copiar estrutura de diretorios.\n");
        return 1;
    }

    /* relspec/ deve existir em todo sistema, mesmo que o template local ainda
     * nao tenha sido materializado com esse diretorio. */
    if (garantir_relspec_dir(destino_dir, &criados_total) != 0) {
        fprintf(stderr, "criar-sistema: erro ao criar relspec/.\n");
        return 1;
    }

    /* Copia a interface publica e o Makefile como excecoes autocontidas */
    {
        int public_copiada = copiar_arquivos_public(raiz_sisc, destino_dir);
        if (public_copiada < 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar arquivos de siscore/public/.\n");
            return 1;
        }
        if (copiar_exemplo_cliente(raiz_sisc, destino_dir, nome_destino) < 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar exemplo-cliente.php.\n");
            return 1;
        }
        int makefile_copiado = copiar_makefile_raiz(template_dir, destino_dir);
        if (makefile_copiado < 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar Makefile.\n");
            return 1;
        }
        int essenciais = copiar_arquivos_essenciais_sisc(template_dir, destino_dir);
        if (essenciais < 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar catalogo web-api ou formato de escuta.\n");
            return 1;
        }
        if (copiar_conector_email_padrao(template_dir, destino_dir) < 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar conector-email padrao.\n");
            return 1;
        }
        aplicar_permissao_secretos(destino_dir);
        int ce_copiados = 0, te_copiados = 0;
        if (copiar_arquivos_de_subdir(template_dir, destino_dir, "conexao-externo", &ce_copiados) != 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar arquivos de conexao-externo/.\n");
            return 1;
        }
        if (copiar_arquivos_de_subdir(template_dir, destino_dir, "token-externo", &te_copiados) != 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar arquivos de token-externo/.\n");
            return 1;
        }
        printf("  [ok] tokens de cliente permanecem somente em token-externo/*.txt\n");
        /* O operador-manual-funcional.json NAO eh copiado — o SISC consulta o mapa funcional do siscore.
         * Specs tambem NAO sao copiadas: core/biblioteca-specs/ permanece como
         * fonte canonica. */
        if ((eh_dir(destino_dir) && criar_htaccess_negado(destino_dir, "secretos") != 0) ||
            (eh_dir(destino_dir) && criar_htaccess_negado(destino_dir, "espaco") != 0)) {
            fprintf(stderr, "criar-sistema: erro ao proteger diretorios sensiveis.\n");
            return 1;
        }
    }

    /* Copia os arquivos do diretorio escuta/ como excecao */
    {
        char origem_escuta[PATH_MAX * 2], destino_escuta[PATH_MAX * 2];
        snprintf(origem_escuta, sizeof(origem_escuta), "%s/escuta", template_dir);
        snprintf(destino_escuta, sizeof(destino_escuta), "%s/escuta", destino_dir);
        int arquivos_copiados = 0;
        if (copiar_arquivos_escuta(origem_escuta, destino_escuta, &arquivos_copiados) != 0) {
            fprintf(stderr, "criar-sistema: erro ao copiar arquivos de escuta/.\n");
            return 1;
        }
        if (arquivos_copiados > 0)
            printf("\nArquivos copiados de escuta/: %d\n", arquivos_copiados);
    }

    printf("\nEstrutura criada com sucesso.\n");
    if (ja_existia_sistema) {
        printf("Diretorios novos adicionados: %d\n", criados_total);
    } else {
        printf("Sistema '%s' criado com %d diretorios.\n", nome_destino, criados_total);
    }

    return 0;
}
