/*
 * normalizar-spec - Normaliza um arquivo de spec para o padrao do sistema
 * Uso: pp --normalizar-spec=<arquivo>
 *
 * Le um arquivo JSON existente em spec-diretivas/ e reescreve no formato 
 * padronizado diretiva-persona-agente, preservando todo o conteudo existente
 * e preenchendo campos ausentes com valores padrao.
 */

#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 <errno.h>
#include <time.h>
#include "../restricao.h"

/* ------------------------------------------------------------------ */
/* Estruturas para armazenar conteudo extraido do JSON original       */
/* ------------------------------------------------------------------ */

#define MAX_STR 8192
#define MAX_ARR 32
#define MAX_ARR_LEN 65536
#define MAX_PATH (PATH_MAX * 2)

typedef struct {
    int    presente;
    char   valor[MAX_STR];
} CampoStr;

typedef struct {
    int    presente;
    char   conteudo[MAX_ARR_LEN];  /* entre [ e ], sem os colchetes */
} CampoArr;

/* Top-level strings */
static CampoStr g_tipo;
static CampoArr g_fonte_informacao_raw;  /* raw array text */
static CampoStr g_arquivo;
static CampoStr g_llm;

/* agente strings */
static CampoStr a_nome, a_titulo, a_persona, a_objetivo, a_resumo_material;
/* agente arrays */
static CampoArr  a_atividades, a_quando_acionar, a_entradas, a_saidas, a_palavras, a_ferramentas, a_fonte_info;

/* materialTecnico strings */
static CampoStr m_resumo;
/* materialTecnico arrays */
static CampoArr  m_escopo, m_fora_escopo, m_criterios_obrig, m_criterios_aprov;
static CampoArr  m_evidencias, m_perguntas, m_termos;

/* ------------------------------------------------------------------ */
/* Helpers de JSON simples (sem dependencia de biblioteca)            */
/* ------------------------------------------------------------------ */

static int termina_com(const char *s, const char *suf)
{
    size_t a = strlen(s), b = strlen(suf);
    return a >= b && strcmp(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 n = fread(b, 1, (size_t)t, fp);
    fclose(fp);
    b[n] = '\0';
    return b;
}

/* Extrai valor de chave string (ex: "persona": "...") */
static int extr_str(const char *json, const char *key, CampoStr *out)
{
    char pat[256];
    snprintf(pat, sizeof(pat), "\"%s\"", key);
    const char *p = strstr(json, pat);
    if (!p) return 0;
    p = strchr(p + strlen(pat), ':');
    if (!p) return 0;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '"') return 0;
    p++;
    size_t j = 0;
    while (*p && *p != '"' && j < MAX_STR - 1) {
        if (*p == '\\' && p[1]) { out->valor[j++] = *p++; out->valor[j++] = *p++; continue; }
        out->valor[j++] = *p++;
    }
    out->valor[j] = '\0';
    out->presente = 1;
    return 1;
}

/* Extrai conteudo de um array: "key": [...]  -> copia o texto interno */
static int extr_arr(const char *json, const char *key, CampoArr *out)
{
    char pat[256];
    snprintf(pat, sizeof(pat), "\"%s\"", key);
    const char *p = strstr(json, pat);
    if (!p) return 0;
    p = strchr(p + strlen(pat), ':');
    if (!p) return 0;
    p++;
    while (*p && isspace((unsigned char)*p)) p++;
    if (*p != '[') return 0;
    const char *inicio = p + 1;
    int profundidade = 1;
    int em_str = 0, esc = 0;
    for (p = inicio; *p && profundidade > 0; p++) {
        if (esc) { esc = 0; continue; }
        if (*p == '\\' && em_str) { esc = 1; continue; }
        if (*p == '"') { em_str = !em_str; continue; }
        if (em_str) continue;
        if (*p == '[') profundidade++;
        else if (*p == ']') profundidade--;
    }
    if (profundidade != 0) return 0;
    size_t tam = (size_t)(p - 1 - inicio);
    if (tam >= MAX_ARR_LEN) tam = MAX_ARR_LEN - 1;
    memcpy(out->conteudo, inicio, tam);
    out->conteudo[tam] = '\0';
    out->presente = 1;
    return 1;
}

/* ------------------------------------------------------------------ */
/* Normalizacao do nome do arquivo                                    */
/* ------------------------------------------------------------------ */

static void normalizar_nome_arquivo(const char *in, char *out, size_t tam)
{
    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) == 0) v += 15;
    if (strchr(v, '/') || strchr(v, '\\')) { out[0] = '\0'; return; }
    if (!strncmp(v, "spec_", 5) && termina_com(v, ".json")) {
        snprintf(out, tam, "%s", v);
        return;
    }
    if (termina_com(v, ".json")) {
        char base[256];
        size_t len = strlen(v) - 5;
        if (len >= sizeof(base)) len = sizeof(base) - 1;
        memcpy(base, v, len); base[len] = '\0';
        if (strncmp(base, "spec_", 5) == 0)
            snprintf(out, tam, "%s.json", base);
        else
            snprintf(out, tam, "spec_%s.json", base);
    } else {
        if (strncmp(v, "spec_", 5) == 0)
            snprintf(out, tam, "%s.json", v);
        else
            snprintf(out, tam, "spec_%s.json", v);
    }
}

/* Deriva categoria do nome do arquivo: spec_seguranca.json -> seguranca */
static void categoria_do_arquivo(const char *arq, char *out, size_t tam)
{
    snprintf(out, tam, "%s", arq);
    char *p = strrchr(out, '.');
    if (p) *p = '\0';
    if (strncmp(out, "spec_", 5) == 0)
        memmove(out, out + 5, strlen(out + 5) + 1);
}

/* Deriva titulo da categoria: seguranca -> Seguranca */
static void titulo_da_cat(const char *cat, char *out, size_t tam)
{
    snprintf(out, tam, "%s", cat);
    int cap = 1;
    for (char *p = out; *p; p++) {
        if (*p == '_' || *p == '-') { *p = ' '; cap = 1; }
        else if (cap) { *p = (char)toupper((unsigned char)*p); cap = 0; }
    }
}

/* ------------------------------------------------------------------ */
/* Funcao principal: extrai tudo do JSON original                      */
/* ------------------------------------------------------------------ */

static void extrair_campos(const char *json)
{
    memset(&g_tipo, 0, sizeof(g_tipo));
    memset(&g_fonte_informacao_raw, 0, sizeof(g_fonte_informacao_raw));
    memset(&g_arquivo, 0, sizeof(g_arquivo));
    memset(&g_llm, 0, sizeof(g_llm));

    memset(&a_nome, 0, sizeof(a_nome));
    memset(&a_titulo, 0, sizeof(a_titulo));
    memset(&a_persona, 0, sizeof(a_persona));
    memset(&a_objetivo, 0, sizeof(a_objetivo));
    memset(&a_resumo_material, 0, sizeof(a_resumo_material));

    memset(&a_atividades, 0, sizeof(a_atividades));
    memset(&a_quando_acionar, 0, sizeof(a_quando_acionar));
    memset(&a_entradas, 0, sizeof(a_entradas));
    memset(&a_saidas, 0, sizeof(a_saidas));
    memset(&a_palavras, 0, sizeof(a_palavras));
    memset(&a_ferramentas, 0, sizeof(a_ferramentas));
    memset(&a_fonte_info, 0, sizeof(a_fonte_info));

    memset(&m_resumo, 0, sizeof(m_resumo));
    memset(&m_escopo, 0, sizeof(m_escopo));
    memset(&m_fora_escopo, 0, sizeof(m_fora_escopo));
    memset(&m_criterios_obrig, 0, sizeof(m_criterios_obrig));
    memset(&m_criterios_aprov, 0, sizeof(m_criterios_aprov));
    memset(&m_evidencias, 0, sizeof(m_evidencias));
    memset(&m_perguntas, 0, sizeof(m_perguntas));
    memset(&m_termos, 0, sizeof(m_termos));

    /* Top-level */
    extr_str(json, "tipo", &g_tipo);
    extr_str(json, "llm", &g_llm);
    extr_str(json, "arquivo", &g_arquivo);
    extr_arr(json, "fonteInformacao", &g_fonte_informacao_raw);

    /* Agente */
    extr_str(json, "nome", &a_nome);
    extr_str(json, "titulo", &a_titulo);
    extr_str(json, "persona", &a_persona);
    extr_str(json, "objetivo", &a_objetivo);
    extr_str(json, "resumoMaterial", &a_resumo_material);
    extr_arr(json, "atividades", &a_atividades);
    extr_arr(json, "quandoAcionar", &a_quando_acionar);
    extr_arr(json, "entradasEsperadas", &a_entradas);
    extr_arr(json, "saidasProduzidas", &a_saidas);
    extr_arr(json, "palavrasChave", &a_palavras);
    extr_arr(json, "ferramentas", &a_ferramentas);
    extr_arr(json, "fonteInformacao", &a_fonte_info);  /* dentro de agente */

    /* materialTecnico */
    extr_str(json, "resumo", &m_resumo);
    extr_arr(json, "escopo", &m_escopo);
    extr_arr(json, "foraDeEscopo", &m_fora_escopo);
    extr_arr(json, "criteriosObrigatorios", &m_criterios_obrig);
    extr_arr(json, "criteriosDeAprovacao", &m_criterios_aprov);
    extr_arr(json, "evidenciasEsperadas", &m_evidencias);
    extr_arr(json, "perguntasParaContexto", &m_perguntas);
    extr_arr(json, "termosEssenciais", &m_termos);

    /* Se fonteInformacao do top-level nao foi encontrada, tenta a do agente */
    if (!g_fonte_informacao_raw.presente && a_fonte_info.presente) {
        memcpy(&g_fonte_informacao_raw, &a_fonte_info, sizeof(CampoArr));
    }
}

/* ------------------------------------------------------------------ */
/* Escrita do JSON normalizado                                        */
/* ------------------------------------------------------------------ */

static void esc_arr(FILE *fp, const char *campo, const CampoArr *c, const char *padrao)
{
    fprintf(fp, "        \"%s\": [", campo);
    if (c->presente && c->conteudo[0]) {
        /* Remove espacos em branco do inicio e fim */
        const char *inicio = c->conteudo;
        while (*inicio && isspace((unsigned char)*inicio)) inicio++;
        const char *fim = c->conteudo + strlen(c->conteudo);
        while (fim > inicio && isspace((unsigned char)fim[-1])) fim--;
        if (inicio < fim) {
            fprintf(fp, "\n            ");
            fwrite(inicio, 1, (size_t)(fim - inicio), fp);
            fprintf(fp, "\n        ");
        }
    } else if (padrao && padrao[0]) {
        fprintf(fp, "%s", padrao);
    }
    fprintf(fp, "]");
}

static int gravar_normalizado(const char *path, const char *nome_arquivo)
{
    FILE *fp = fopen(path, "wb");
    if (!fp) return -1;

    char cat[128], tit[128];
    categoria_do_arquivo(nome_arquivo, cat, sizeof(cat));
    titulo_da_cat(cat, tit, sizeof(tit));

    /* Nome do agente */
    char agente_nome[256];
    if (a_nome.presente && a_nome.valor[0])
        snprintf(agente_nome, sizeof(agente_nome), "%s", a_nome.valor);
    else
        snprintf(agente_nome, sizeof(agente_nome), "agente-%s", cat);

    /* Titulo do agente */
    char agente_titulo[256];
    if (a_titulo.presente && a_titulo.valor[0])
        snprintf(agente_titulo, sizeof(agente_titulo), "%s", a_titulo.valor);
    else
        snprintf(agente_titulo, sizeof(agente_titulo), "Agente de %s", tit);

    /* Persona */
    char persona[2048];
    if (a_persona.presente && a_persona.valor[0])
        snprintf(persona, sizeof(persona), "%s", a_persona.valor);
    else
        snprintf(persona, sizeof(persona), "Especialista responsavel por aplicar a spec de %s.", tit);

    /* Objetivo */
    char objetivo[2048];
    if (a_objetivo.presente && a_objetivo.valor[0])
        snprintf(objetivo, sizeof(objetivo), "%s", a_objetivo.valor);
    else
        snprintf(objetivo, sizeof(objetivo), "Garantir conformidade com spec-diretivas/%s.", nome_arquivo);

    /* Resumo do material */
    char resumo_mat[2048];
    if (a_resumo_material.presente && a_resumo_material.valor[0])
        snprintf(resumo_mat, sizeof(resumo_mat), "%s", a_resumo_material.valor);
    else
        snprintf(resumo_mat, sizeof(resumo_mat), "Usar o material tecnico desta spec como checklist normativo para avaliar tarefas, artefatos, decisoes e riscos relacionados a %s.", tit);

    /* Resumo do materialTecnico */
    char m_resumo_val[4096];
    if (m_resumo.presente && m_resumo.valor[0])
        snprintf(m_resumo_val, sizeof(m_resumo_val), "%s", m_resumo.valor);
    else
        snprintf(m_resumo_val, sizeof(m_resumo_val), "Spec de %s. Define escopo, criterios obrigatorios e evidencias esperadas.", tit);

    /* Fonte de informacao (top-level) */
    char fonte_info[4096];
    if (g_fonte_informacao_raw.presente && g_fonte_informacao_raw.conteudo[0]) {
        snprintf(fonte_info, sizeof(fonte_info), "%s", g_fonte_informacao_raw.conteudo);
    } else {
        fonte_info[0] = '\0';
    }

    /* Palavras-chave padrao */
    char palavras_default[256];
    snprintf(palavras_default, sizeof(palavras_default), "\"%s\"", cat);

    fprintf(fp,
"{\n"
"    \"tipo\": \"diretiva-persona-agente\",\n"
"    \"fonteUnicaDeDescobertaAgente\": true,\n"
"    \"fonteInformacao\": [%s],\n"
"    \"arquivo\": \"spec-diretivas/%s\",\n"
"    \"llm\": \"atual.json\",\n"
"    \"agente\": {\n"
"        \"criar\": true,\n",
    fonte_info, nome_arquivo);

    fprintf(fp, "        \"nome\": \"%s\",\n", agente_nome);
    fprintf(fp, "        \"titulo\": \"%s\",\n", agente_titulo);
    fprintf(fp, "        \"persona\": \"%s\",\n", persona);
    fprintf(fp, "        \"objetivo\": \"%s\",\n", objetivo);
    fprintf(fp, "        \"resumoMaterial\": \"%s\",\n", resumo_mat);

    /* Arrays do agente */
    esc_arr(fp, "atividades", &a_atividades, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "quandoAcionar", &a_quando_acionar, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "entradasEsperadas", &a_entradas, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "saidasProduzidas", &a_saidas, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "palavrasChave", &a_palavras, palavras_default);
    fprintf(fp, ",\n");

    /* Ferramentas */
    if (a_ferramentas.presente && a_ferramentas.conteudo[0])
        esc_arr(fp, "ferramentas", &a_ferramentas, "");
    else
        fprintf(fp, "        \"ferramentas\": [\"ler\", \"escrever\", \"deletar\", \"procurar_arquivo\", \"listar_arquivos\", \"diferencas\", \"internet\"]");
    fprintf(fp, "\n");

    fprintf(fp, "    },\n");

    /* materialTecnico */
    fprintf(fp, "    \"materialTecnico\": {\n");
    fprintf(fp, "        \"resumo\": \"%s\",\n", m_resumo_val);
    esc_arr(fp, "escopo", &m_escopo, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "foraDeEscopo", &m_fora_escopo, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "criteriosObrigatorios", &m_criterios_obrig, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "criteriosDeAprovacao", &m_criterios_aprov, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "evidenciasEsperadas", &m_evidencias, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "perguntasParaContexto", &m_perguntas, "");
    fprintf(fp, ",\n");
    esc_arr(fp, "termosEssenciais", &m_termos, "");
    fprintf(fp, "\n");

    fprintf(fp, "    },\n");

    /* sincronizacao */
    fprintf(fp, "    \"sincronizacao\": {\n");
    fprintf(fp, "        \"formatadoPor\": \"pp --normalizar-spec\",\n");
    fprintf(fp, "        \"versao\": \"1.0.0\",\n");
    fprintf(fp, "        \"atualizadoPor\": \"pp --normalizar-spec\",\n");
    fprintf(fp, "        \"atualizadoEm\": \"%ld\"\n", (long)time(NULL));
    fprintf(fp, "    }\n");

    fprintf(fp, "}\n");

    fclose(fp);
    return 0;
}

/* ------------------------------------------------------------------ */
/* Uso                                                                */
/* ------------------------------------------------------------------ */

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --normalizar-spec=<arquivo>\n"
        "  pp --normalizar-spec --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Normaliza uma spec existente em spec-diretivas/ para o formato canônico\n"
        "  diretiva-persona-agente, preservando conteúdo encontrado.\n"
        "\n"
        "Grupos de ajuda: uso, entrada, processo, saida, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>                Filtra um grupo. Ex: pp --normalizar-spec --ajuda:entrada\n"
        "\n"
        "2) Entrada [entrada]\n"
        "  --normalizar-spec=spec_x.json\n"
        "  --normalizar-spec=spec_x\n"
        "  --normalizar-spec=x            Adiciona prefixo spec_ e extensão .json\n"
        "  --normalizar-spec=spec-diretivas/spec_x.json\n"
        "  Exemplo com todas as flags do grupo:\n"
        "    pp --normalizar-spec=spec-diretivas/spec_seguranca.json  (ou formas alternativas)\n"
        "\n"
        "3) Processo [processo]\n"
        "  Lê o JSON, extrai persona/objetivo/atividades/material técnico, reescreve\n"
        "  a estrutura canônica, preenche ausentes e atualiza sincronizacao.\n"
        "\n"
        "4) Saída [saida]\n"
        "  Regrava spec-diretivas/<arquivo> e remove qualidadeSpec para recriação por\n"
        "  revisão humana da qualidade da spec.\n"
        "\n"
        "5) Exemplos [exemplos]\n"
        "  pp --normalizar-spec=spec_seguranca.json\n"
        "  pp --normalizar-spec=seguranca\n"
        "  pp --normalizar-spec=minha-spec-customizada\n",
        stdout);
}

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

/* ------------------------------------------------------------------ */
/* Main                                                               */
/* ------------------------------------------------------------------ */

int main(int argc, char **argv)
{
    if (restricao_verificar("normalizar-spec") != 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 nome do arquivo */
    char nome_arq[256];
    normalizar_nome_arquivo(argv[1], nome_arq, sizeof(nome_arq));
    if (!nome_arq[0] || strcmp(nome_arq, "spec_modelo.json") == 0) {
        fprintf(stderr, "normalizar-spec: nome de arquivo invalido: %s\n", argv[1]);
        return 1;
    }

    /* Localiza diretorio spec-diretivas */
    char cwd[PATH_MAX];
    if (!getcwd(cwd, sizeof(cwd))) { perror("getcwd"); return 1; }

    char dir_specs[PATH_MAX * 2];
    snprintf(dir_specs, sizeof(dir_specs), "%s/spec-diretivas", cwd);
    if (!eh_dir(dir_specs)) {
        fprintf(stderr, "normalizar-spec: diretorio spec-diretivas nao encontrado na raiz do sistema atual.\n");
        return 1;
    }

    char path_original[PATH_MAX * 2];
    snprintf(path_original, sizeof(path_original), "%s/%s", dir_specs, nome_arq);

    /* Le o arquivo original */
    char *json = ler_arquivo(path_original);
    if (!json) {
        fprintf(stderr, "normalizar-spec: arquivo nao encontrado ou ilegivel: spec-diretivas/%s\n", nome_arq);
        return 1;
    }

    printf("Normalizando spec-diretivas/%s ...\n", nome_arq);

    /* Extrai todos os campos */
    extrair_campos(json);

    /* Backup opcional: renomeia original para .bak */
    char path_backup[PATH_MAX * 2 + 4];
    snprintf(path_backup, sizeof(path_backup), "%s.bak", path_original);
    if (access(path_backup, F_OK) == 0) unlink(path_backup);
    rename(path_original, path_backup);

    /* Grava versao normalizada */
    if (gravar_normalizado(path_original, nome_arq) != 0) {
        fprintf(stderr, "normalizar-spec: erro ao gravar arquivo normalizado.\n");
        /* Restaura backup */
        rename(path_backup, path_original);
        free(json);
        return 1;
    }

    printf("Spec normalizada: spec-diretivas/%s\n", nome_arq);
    printf("Backup salvo em:   spec-diretivas/%s.bak\n", nome_arq);
    printf("\nCampos detectados e preservados:\n");

    if (a_persona.presente)     printf("  - persona\n");
    if (a_objetivo.presente)    printf("  - objetivo\n");
    if (a_resumo_material.presente) printf("  - resumoMaterial\n");
    if (a_atividades.presente)  printf("  - atividades\n");
    if (a_quando_acionar.presente) printf("  - quandoAcionar\n");
    if (a_entradas.presente)    printf("  - entradasEsperadas\n");
    if (a_saidas.presente)      printf("  - saidasProduzidas\n");
    if (a_palavras.presente)    printf("  - palavrasChave\n");
    if (a_ferramentas.presente) printf("  - ferramentas\n");
    if (m_escopo.presente)      printf("  - materialTecnico.escopo\n");
    if (m_fora_escopo.presente) printf("  - materialTecnico.foraDeEscopo\n");
    if (m_criterios_obrig.presente) printf("  - materialTecnico.criteriosObrigatorios\n");
    if (m_criterios_aprov.presente) printf("  - materialTecnico.criteriosDeAprovacao\n");
    if (m_evidencias.presente)  printf("  - materialTecnico.evidenciasEsperadas\n");
    if (m_perguntas.presente)   printf("  - materialTecnico.perguntasParaContexto\n");
    if (m_termos.presente)      printf("  - materialTecnico.termosEssenciais\n");

    printf("\nRevise a qualidade da spec normalizada: %s\n", nome_arq);

    free(json);
    return 0;
}
