/*
 * sincronizar-agente - materializa um agente consultor derivado de uma spec local
 * Uso: pp --sincronizar-agente=<nome-spec>
 *
 * Exemplo:
 *   pp --sincronizar-agente=seguranca_zero_trust_web_api
 *   -> lê spec-diretivas/spec_seguranca_zero_trust_web_api.json
 *   -> grava agentes/seguranca_zero_trust_web_api.json
 */

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

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --sincronizar-agente=<spec> [--force] [--dry-run]\n"
        "  pp --sincronizar-agente --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Cria/atualiza um agente consultor em agentes/<spec>.json a partir da\n"
        "  persona declarada em spec-diretivas/spec_<spec>.json. O agente gerado\n"
        "  é acionado por pp --agente=<spec>:<conector> e deve produzir análise\n"
        "  em relspec/ usando pp --sa, sem aplicar alterações diretamente.\n"
        "\n"
        "Grupos de ajuda: uso, entrada, arquivos, seguranca, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>                Filtra um grupo via pp\n"
        "\n"
        "2) Entrada [entrada]\n"
        "  <spec>                         Nome sem prefixo spec_ e sem .json\n"
        "  --force                        Sobrescreve agente manual existente\n"
        "  --dry-run                      Mostra o destino sem gravar\n"
        "\n"
        "3) Arquivos [arquivos]\n"
        "  Origem:  spec-diretivas/spec_<spec>.json\n"
        "  Destino: agentes/<spec>.json\n"
        "\n"
        "4) Segurança [seguranca]\n"
        "  Agentes fixos não são sobrescritos. Agentes manuais só são sobrescritos\n"
        "  com --force. O agente criado trabalha como consultor: gera árvore SA em\n"
        "  relspec/ e não altera conectores/código diretamente.\n"
        "\n"
        "5) Exemplos [exemplos]\n"
        "  pp --sincronizar-agente=seguranca_zero_trust_web_api\n"
        "  pp --agente=seguranca_zero_trust_web_api:conector-email\n",
        stdout);
}

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

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

static int garantir_dir(const char *dir, mode_t modo)
{
    struct stat st;
    if (stat(dir, &st) == 0) return S_ISDIR(st.st_mode) ? 0 : -1;
    if (mkdir(dir, modo) != 0 && errno != EEXIST) return -1;
    chmod(dir, modo);
    return 0;
}

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

static int normalizar_spec(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++;
    if (!strncmp(v, "spec-diretivas/", 15)) v += 15;
    if (strchr(v, '/') || strchr(v, '\\') || strstr(v, "..")) return -1;
    size_t len = strlen(v);
    while (len > 0 && isspace((unsigned char)v[len - 1])) v[--len] = '\0';
    if (len >= 5 && !strcasecmp(v + len - 5, ".json")) v[len - 5] = '\0';
    if (!strncmp(v, "spec_", 5)) v += 5;

    size_t j = 0;
    int 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); sep = 0; }
        else if (c == '-' || c == '_' || isspace(c)) {
            if (!sep && j > 0) out[j++] = '_';
            sep = 1;
        } else return -1;
    }
    while (j > 0 && out[j - 1] == '_') j--;
    out[j] = '\0';
    return out[0] ? 0 : -1;
}

static char *json_str_intervalo(const char *ini, const char *fim, const char *k)
{
    if (!ini || !fim || fim <= ini || !k) return NULL;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", k);
    size_t plen = strlen(pat);
    for (const char *p = ini; p < fim; p++) {
        const char *m = strstr(p, pat);
        if (!m || m >= fim || m + plen >= fim) return NULL;
        const char *q = strchr(m + plen, ':');
        if (!q || q >= fim) return NULL;
        q++;
        while (q < fim && isspace((unsigned char)*q)) q++;
        if (q >= fim || *q != '"') { p = m + plen; continue; }
        q++;
        size_t cap = 128, n = 0;
        char *out = malloc(cap);
        if (!out) return NULL;
        int esc = 0;
        while (q < fim && *q) {
            unsigned char c = (unsigned char)*q++;
            if (esc) {
                if (c == 'n') c = '\n';
                else if (c == 't') c = '\t';
                else if (c == 'r') c = '\r';
                esc = 0;
            } else if (c == '\\') { esc = 1; continue; }
            else if (c == '"') break;
            if (n + 1 >= cap) {
                cap *= 2;
                char *nb = realloc(out, cap);
                if (!nb) { free(out); return NULL; }
                out = nb;
            }
            out[n++] = (char)c;
        }
        out[n] = '\0';
        return out;
    }
    return NULL;
}

static char *json_str(const char *j, const char *k)
{
    return json_str_intervalo(j, j ? j + strlen(j) : NULL, k);
}

static const char *json_objeto_fim(const char *obj)
{
    if (!obj || *obj != '{') return NULL;
    int nivel = 0, em_string = 0, esc = 0;
    for (const char *p = obj; *p; p++) {
        unsigned char c = (unsigned char)*p;
        if (em_string) {
            if (esc) { esc = 0; continue; }
            if (c == '\\') { esc = 1; continue; }
            if (c == '"') em_string = 0;
            continue;
        }
        if (c == '"') { em_string = 1; continue; }
        if (c == '{') nivel++;
        else if (c == '}') {
            nivel--;
            if (nivel == 0) return p + 1;
        }
    }
    return NULL;
}

static char *json_objeto_raw(const char *j, const char *k)
{
    if (!j || !k) return NULL;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", k);
    const char *p = strstr(j, pat);
    if (!p) return NULL;
    p = strchr(p + strlen(pat), ':');
    if (!p) return NULL;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '{') return NULL;
    const char *fim = json_objeto_fim(p);
    if (!fim) return NULL;
    size_t n = (size_t)(fim - p);
    char *out = malloc(n + 1);
    if (!out) return NULL;
    memcpy(out, p, n);
    out[n] = '\0';
    return out;
}

static int json_bool_false(const char *j, const char *k)
{
    if (!j || !k) return 0;
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", k);
    const char *p = strstr(j, pat);
    if (!p) return 0;
    p = strchr(p + strlen(pat), ':');
    if (!p) return 0;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    return !strncmp(p, "false", 5);
}

static void json_escape(FILE *f, const char *s)
{
    fputc('"', f);
    for (const unsigned char *p = (const unsigned char *)(s ? s : ""); *p; p++) {
        switch (*p) {
            case '"': fputs("\\\"", f); break;
            case '\\': fputs("\\\\", f); break;
            case '\n': fputs("\\n", f); break;
            case '\r': fputs("\\r", f); break;
            case '\t': fputs("\\t", f); break;
            default: if (*p < 32) fprintf(f, "\\u%04x", *p); else fputc(*p, f);
        }
    }
    fputc('"', f);
}

static int json_agente_fixo(const char *j)
{
    if (!j) return 0;
    const char *p = strstr(j, "\"agenteFixo\"");
    if (!p) return 0;
    p = strchr(p, ':');
    if (!p) return 0;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    return !strncmp(p, "true", 4);
}

static int json_agente_derivado_consultor(const char *j)
{
    return j && (strstr(j, "agente-consultor-derivado-de-spec") || strstr(j, "pp --sincronizar-agente"));
}

static int gravar_agente(const char *dest, const char *slug, const char *spec_rel, const char *spec_json)
{
    char *ag = json_objeto_raw(spec_json, "agente");
    const char *scope = ag ? ag : spec_json;

    if (ag && json_bool_false(ag, "criar")) {
        fprintf(stderr, "sincronizar-agente: spec declara agente.criar=false.\n");
        free(ag);
        return -2;
    }

    char titulo_auto[512];
    snprintf(titulo_auto, sizeof(titulo_auto), "Consultor %s", slug);
    for (char *p = titulo_auto; *p; p++) if (*p == '_' || *p == '-') *p = ' ';
    if (titulo_auto[0]) titulo_auto[0] = (char)toupper((unsigned char)titulo_auto[0]);

    char *titulo = json_str_intervalo(scope, scope + strlen(scope), "titulo");
    char *persona_base = json_str_intervalo(scope, scope + strlen(scope), "persona");
    char *resumo = json_str_intervalo(scope, scope + strlen(scope), "resumoMaterial");
    if (!resumo) {
        char *mat = json_objeto_raw(spec_json, "materialTecnico");
        if (mat) { resumo = json_str(mat, "resumo"); free(mat); }
    }
    const char *ferr_padrao = "\"ler\", \"listar_arquivos\", \"procurar_arquivo\", \"diferencas\", \"sa\", \"sa-ctx\"";
    if (!titulo || !*titulo) { free(titulo); titulo = strdup(titulo_auto); }
    if (!persona_base || !*persona_base) {
        free(persona_base);
        persona_base = strdup("Consultor técnico especialista na spec informada.");
    }
    if (!resumo) resumo = strdup("");

    FILE *f = fopen(dest, "wb");
    if (!f) { free(ag); free(titulo); free(persona_base); free(resumo); return -1; }

    long agora = (long)time(NULL);
    fprintf(f, "{\n");
    fputs("  \"nome\": ", f); json_escape(f, slug); fputs(",\n", f);
    fputs("  \"arquivo\": ", f); char arq[PATH_MAX]; snprintf(arq, sizeof(arq), "agentes/%s.json", slug); json_escape(f, arq); fputs(",\n", f);
    fputs("  \"titulo\": ", f); json_escape(f, titulo); fputs(",\n", f);
    fputs("  \"descricao\": ", f); json_escape(f, "Agente consultor derivado de spec. Analisa conectores e cria relatório SA em relspec/ sem aplicar alterações diretamente."); fputs(",\n", f);
    fputs("  \"versao\": \"1.0.0\",\n", f);
    fputs("  \"ativo\": true,\n", f);
    fputs("  \"agenteFixo\": false,\n", f);
    fputs("  \"tipo\": \"agente-consultor-derivado-de-spec\",\n", f);
    fprintf(f, "  \"controlador\": {\"executavel\": \"../pp\", \"metodoLeitura\": \"--agente=%s:<conector>\", \"metodoEnvio\": \"pp --sa\", \"regraEnvio\": \"somente-relatorio-sa-em-relspec-sem-alteracao-direta\"},\n", slug);
    fputs("  \"llm\": {\"arquivoConfiguracao\": \"llm.json\", \"mapeamentoComando\": ", f); json_escape(f, slug); fputs("},\n", f);
    fprintf(f, "  \"ferramentas\": [%s],\n", ferr_padrao);
    fputs("  \"persona\": ", f);
    char persona[8192];
    if (strstr(persona_base, "Atua como consultor de conformidade")) {
        snprintf(persona, sizeof(persona), "%s", persona_base);
    } else {
        snprintf(persona, sizeof(persona), "%s Atua como consultor de conformidade, não como construtor: avalia conectores contra a spec, identifica lacunas, riscos e evidências, e orienta o operador por árvore SA.", persona_base);
    }
    json_escape(f, persona); fputs(",\n", f);
    fputs("  \"objetivo\": ", f);
    char objetivo[8192];
    snprintf(objetivo, sizeof(objetivo), "Quando acionado por pp --agente=%s:<conector>, analisar detalhadamente o conector informado contra %s e construir exclusivamente via pp --sa uma árvore em relspec/ com diagnóstico, evidências, riscos e atualizações necessárias. Não aplicar alterações diretamente; as implementações serão realizadas pelos GCs que o operador criar nessa árvore.", slug, spec_rel);
    json_escape(f, objetivo); fputs(",\n", f);
    fputs("  \"resumoMaterial\": ", f); json_escape(f, resumo); fputs(",\n", f);
    fputs("  \"diretivaOrigem\": {\"arquivo\": ", f); json_escape(f, spec_rel); fprintf(f, ", \"sincronizadoPor\": \"pp --sincronizar-agente\", \"sincronizadoEm\": %ld},\n", agora);
    fputs("  \"modoOperacao\": {\"papel\": \"consultor\", \"alvo\": \"conector\", \"saida\": \"relspec/<agente>--<conector>--<timestamp>.json\", \"alteracaoDiretaPermitida\": false, \"construcaoSaObrigatoria\": true},\n", f);
    fputs("  \"restricoes\": [\n", f);
    fputs("    \"Nunca aplicar alterações diretamente em conectores, web-api, escuta, secretos, contas-llm ou espaco.\",\n", f);
    fputs("    \"Construir o relatório exclusivamente por pp --sa no diretório relspec/.\",\n", f);
    fputs("    \"Descrever atualizações necessárias como pendências/GCs sugeridos para decisão do operador.\",\n", f);
    fputs("    \"Usar evidências de arquivos reais do conector alvo e da spec de origem.\"\n", f);
    fputs("  ],\n", f);
    fputs("  \"instrucoes\": [\n", f);
    fprintf(f, "    \"Sincronizar: pp --sincronizar-agente=%s\",\n", slug);
    fprintf(f, "    \"Executar análise: pp --agente=%s:<conector>\",\n", slug);
    fputs("    \"Revisar a árvore gerada em relspec/ e criar GCs manualmente para aplicar alterações.\"\n", f);
    fputs("  ]\n", f);
    fputs("}\n", f);

    int ok = fclose(f) == 0 ? 0 : -1;
    free(ag); free(titulo); free(persona_base); free(resumo);
    return ok;
}

int main(int argc, char **argv)
{
    if (restricao_verificar("sincronizar-agente") != 0) return 1;

    int dry = 0, force = 0;
    const char *entrada = NULL;
    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "--ajuda") || !strcmp(argv[i], "-h")) { uso(); return 0; }
        else if (!strcmp(argv[i], "--dry-run")) dry = 1;
        else if (!strcmp(argv[i], "--force")) force = 1;
        else if (argv[i][0] != '-' && !entrada) entrada = argv[i];
        else { uso(); return 1; }
    }

    if (!entrada || !*entrada) {
        uso();
        return 1;
    }

    char slug[256];
    if (normalizar_spec(entrada, slug, sizeof(slug)) != 0) {
        fprintf(stderr, "sincronizar-agente: nome de spec invalido: %s\n", entrada);
        return 1;
    }

    char cwd[PATH_MAX];
    if (!getcwd(cwd, sizeof(cwd))) { perror("sincronizar-agente: getcwd"); return 1; }

    char spec_path[PATH_MAX * 2], agentes_dir[PATH_MAX * 2], dest[PATH_MAX * 2], spec_rel[PATH_MAX * 2];
    snprintf(spec_rel, sizeof(spec_rel), "spec-diretivas/spec_%s.json", slug);
    snprintf(spec_path, sizeof(spec_path), "%s/%s", cwd, spec_rel);
    snprintf(agentes_dir, sizeof(agentes_dir), "%s/agentes", cwd);
    snprintf(dest, sizeof(dest), "%s/%s.json", agentes_dir, slug);

    if (!eh_arq(spec_path)) {
        fprintf(stderr, "sincronizar-agente: spec local nao encontrada: %s\n", spec_rel);
        fprintf(stderr, "sincronizar-agente: importe antes com pp --importar-spec=%s\n", slug);
        return 1;
    }

    char *spec_json = ler_arquivo(spec_path);
    if (!spec_json) {
        fprintf(stderr, "sincronizar-agente: nao foi possivel ler %s\n", spec_rel);
        return 1;
    }

    if (eh_arq(dest)) {
        char *existente = ler_arquivo(dest);
        if (json_agente_fixo(existente)) {
            fprintf(stderr, "sincronizar-agente: recusado sobrescrever agente fixo: agentes/%s.json\n", slug);
            free(existente); free(spec_json);
            return 1;
        }
        if (!force && !json_agente_derivado_consultor(existente)) {
            fprintf(stderr, "sincronizar-agente: agente manual existente preservado: agentes/%s.json\n", slug);
            fprintf(stderr, "sincronizar-agente: use --force para sobrescrever.\n");
            free(existente); free(spec_json);
            return 1;
        }
        free(existente);
    }

    printf("Spec:   %s\n", spec_rel);
    printf("Agente: agentes/%s.json\n", slug);
    if (dry) {
        printf("dry-run: nenhum arquivo alterado.\n");
        free(spec_json);
        return 0;
    }

    if (!eh_dir(agentes_dir) && garantir_dir(agentes_dir, 0775) != 0) {
        fprintf(stderr, "sincronizar-agente: nao foi possivel criar agentes/.\n");
        free(spec_json);
        return 1;
    }

    int r = gravar_agente(dest, slug, spec_rel, spec_json);
    free(spec_json);
    if (r == -2) return 1;
    if (r != 0) {
        fprintf(stderr, "sincronizar-agente: erro ao gravar agentes/%s.json\n", slug);
        return 1;
    }

    chmod(dest, 0644);
    printf("[ok] agente consultor sincronizado: agentes/%s.json\n", slug);
    printf("Uso: pp --agente=%s:<conector>\n", slug);
    return 0;
}
