/*
 * deletar-spec - remove interativamente uma spec e o agente vinculado
 * Uso: pp --deletar-spec
 */

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

typedef struct {
    char arquivo[256];
    char caminho[PATH_MAX * 2];
    char agente[256];
} Spec;

typedef struct {
    char arquivo[256];
    char caminho[PATH_MAX * 2];
} Agente;

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --deletar-spec\n"
        "  pp --deletar-spec --ajuda:<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Remove interativamente uma spec do sistema atual e o agente vinculado,\n"
        "  quando encontrado.\n"
        "\n"
        "Grupos de ajuda: uso, processo, riscos, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>                Filtra um grupo. Ex: pp --deletar-spec --ajuda:processo\n"
        "\n"
        "2) Processo [processo]\n"
        "  Lista spec-diretivas/spec_*.json, ignora spec_modelo.json, solicita seleção,\n"
        "  remove a spec escolhida e tenta remover agentes/*.json vinculados.\n"
        "\n"
        "3) Riscos [riscos atencao]\n"
        "  Exclusão irreversível; confira o sistema antes de executar.\n"
        "\n"
        "4) Exemplos [exemplos]\n"
        "  pp --deletar-spec\n"
        "  Exemplo com todas as flags do grupo:\n"
        "    pp --deletar-spec  (interativo: lista, seleciona, remove spec e agente vinculado)\n",
        stdout);
}

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

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

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

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);
    fclose(f);
    b[n] = '\0';
    return b;
}

/* Parser simples suficiente para os manifests JSON usados pelo SISC. */
static char *json_str(const char *j, const char *k)
{
    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;
    p++;
    char *o = malloc(strlen(p) + 1);
    if (!o) return NULL;
    size_t n = 0;
    while (*p && *p != '"') {
        if (*p == '\\' && p[1]) p++;
        o[n++] = *p++;
    }
    o[n] = '\0';
    return o;
}

static void normalizar_agente(const char *in, char *out, size_t tam)
{
    size_t j = 0;
    for (size_t i = 0; in && in[i] && j + 1 < tam; i++) {
        unsigned char c = (unsigned char)in[i];
        if (isalnum(c)) out[j++] = (char)tolower(c);
        else if (c == '-' || c == '_' || isspace(c)) {
            if (j && out[j - 1] != '-') out[j++] = '-';
        }
    }
    while (j && out[j - 1] == '-') j--;
    out[j] = '\0';
}

static void agente_padrao_da_spec(const char *arquivo, char *out, size_t tam)
{
    char cat[256];
    snprintf(cat, sizeof(cat), "%s", arquivo);
    char *p = strrchr(cat, '.');
    if (p) *p = '\0';
    if (strncmp(cat, "spec_", 5) == 0) memmove(cat, cat + 5, strlen(cat + 5) + 1);
    for (char *q = cat; *q; q++) if (*q == '_') *q = '-';
    snprintf(out, tam, "agente-%s", cat);
}

static int cmp_spec(const void *a, const void *b)
{
    return strcasecmp(((const Spec *)a)->arquivo, ((const Spec *)b)->arquivo);
}

static int listar_specs(const char *dir, Spec **out, int *tot)
{
    DIR *d = opendir(dir);
    if (!d) return -1;

    int cap = 32, n = 0;
    Spec *v = calloc((size_t)cap, sizeof(Spec));
    if (!v) { closedir(d); return -1; }

    struct dirent *e;
    while ((e = readdir(d)) != NULL) {
        const char *nm = e->d_name;
        if (strncmp(nm, "spec_", 5) != 0) continue;
        if (!termina_com(nm, ".json")) continue;
        if (strcmp(nm, "spec_modelo.json") == 0) continue;

        char path[PATH_MAX * 2];
        snprintf(path, sizeof(path), "%s/%s", dir, nm);
        if (!eh_arquivo_regular(path)) continue;

        if (n >= cap) {
            cap *= 2;
            Spec *nv = realloc(v, (size_t)cap * sizeof(Spec));
            if (!nv) { free(v); closedir(d); return -1; }
            v = nv;
        }

        snprintf(v[n].arquivo, sizeof(v[n].arquivo), "%s", nm);
        snprintf(v[n].caminho, sizeof(v[n].caminho), "%s", path);

        char *json = ler_arquivo(path);
        char *nome = json ? json_str(json, "nome") : NULL;
        if (nome && *nome) normalizar_agente(nome, v[n].agente, sizeof(v[n].agente));
        if (!v[n].agente[0]) agente_padrao_da_spec(nm, v[n].agente, sizeof(v[n].agente));
        free(nome);
        free(json);

        n++;
    }
    closedir(d);

    qsort(v, (size_t)n, sizeof(Spec), cmp_spec);
    *out = v;
    *tot = n;
    return 0;
}

static int ler_numero(int total)
{
    char linha[128];
    printf("Digite o numero da spec a apagar: ");
    fflush(stdout);
    if (!fgets(linha, sizeof(linha), stdin)) return -1;

    char *p = linha;
    while (isspace((unsigned char)*p)) p++;
    if (!isdigit((unsigned char)*p)) return -1;
    int n = atoi(p);
    return (n >= 1 && n <= total) ? n : -1;
}

static int agente_ja_incluido(Agente *agentes, int total, const char *caminho)
{
    for (int i = 0; i < total; i++) {
        if (strcmp(agentes[i].caminho, caminho) == 0) return 1;
    }
    return 0;
}

static int incluir_agente(Agente **agentes, int *total, int *cap, const char *arquivo, const char *caminho)
{
    if (agente_ja_incluido(*agentes, *total, caminho)) return 0;
    if (*total >= *cap) {
        *cap = *cap ? *cap * 2 : 8;
        Agente *nv = realloc(*agentes, (size_t)*cap * sizeof(Agente));
        if (!nv) return -1;
        *agentes = nv;
    }
    snprintf((*agentes)[*total].arquivo, sizeof((*agentes)[*total].arquivo), "%s", arquivo);
    snprintf((*agentes)[*total].caminho, sizeof((*agentes)[*total].caminho), "%s", caminho);
    (*total)++;
    return 0;
}

static int encontrar_agentes_vinculados(const char *dir_agentes, const Spec *spec, Agente **out, int *tot)
{
    Agente *agentes = NULL;
    int total = 0, cap = 0;

    if (eh_dir(dir_agentes)) {
        char direto_arq[300];
        char direto_path[PATH_MAX * 2];
        snprintf(direto_arq, sizeof(direto_arq), "%s.json", spec->agente);
        snprintf(direto_path, sizeof(direto_path), "%s/%s", dir_agentes, direto_arq);
        if (eh_arquivo_regular(direto_path)) {
            if (incluir_agente(&agentes, &total, &cap, direto_arq, direto_path) != 0) {
                free(agentes); return -1;
            }
        }

        char origem[512];
        snprintf(origem, sizeof(origem), "spec-diretivas/%s", spec->arquivo);

        DIR *d = opendir(dir_agentes);
        if (d) {
            struct dirent *e;
            while ((e = readdir(d)) != NULL) {
                const char *nm = e->d_name;
                if (!termina_com(nm, ".json")) continue;
                char path[PATH_MAX * 2];
                snprintf(path, sizeof(path), "%s/%s", dir_agentes, nm);
                if (!eh_arquivo_regular(path)) continue;
                char *json = ler_arquivo(path);
                if (json && strstr(json, origem)) {
                    if (incluir_agente(&agentes, &total, &cap, nm, path) != 0) {
                        free(json); closedir(d); free(agentes); return -1;
                    }
                }
                free(json);
            }
            closedir(d);
        }
    }

    *out = agentes;
    *tot = total;
    return 0;
}

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

    if (argc > 1) {
        if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "--ajuda") || !strcmp(argv[1], "-h")) {
            uso();
            return 0;
        }
        uso();
        return 1;
    }

    char raiz[PATH_MAX];
    if (!getcwd(raiz, sizeof(raiz))) { perror("deletar-spec: getcwd"); return 1; }

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

    Spec *specs = NULL;
    int total = 0;
    if (listar_specs(dir_specs, &specs, &total) != 0 || total == 0) {
        fprintf(stderr, "deletar-spec: nenhuma spec encontrada em spec-diretivas/.\n");
        free(specs);
        return 1;
    }

    printf("Specs disponiveis:\n");
    for (int i = 0; i < total; i++) {
        printf("%2d) %s", i + 1, specs[i].arquivo);
        if (specs[i].agente[0]) printf("  -> agente: %s.json", specs[i].agente);
        printf("\n");
    }

    int esc = ler_numero(total);
    if (esc < 1) {
        fprintf(stderr, "deletar-spec: numero invalido.\n");
        free(specs);
        return 1;
    }

    Spec *sel = &specs[esc - 1];
    Agente *agentes = NULL;
    int total_agentes = 0;
    if (encontrar_agentes_vinculados(dir_agentes, sel, &agentes, &total_agentes) != 0) {
        fprintf(stderr, "deletar-spec: erro ao localizar agente vinculado.\n");
        free(specs);
        return 1;
    }

    if (unlink(sel->caminho) != 0) {
        fprintf(stderr, "deletar-spec: nao foi possivel apagar spec-diretivas/%s\n", sel->arquivo);
        free(agentes);
        free(specs);
        return 1;
    }
    printf("[apagada] spec-diretivas/%s\n", sel->arquivo);

    if (total_agentes == 0) {
        printf("[agente-nao-encontrado] agentes/%s.json\n", sel->agente);
    } else {
        for (int i = 0; i < total_agentes; i++) {
            if (unlink(agentes[i].caminho) == 0) {
                printf("[apagado] agentes/%s\n", agentes[i].arquivo);
            } else {
                fprintf(stderr, "deletar-spec: nao foi possivel apagar agentes/%s\n", agentes[i].arquivo);
            }
        }
    }

    free(agentes);
    free(specs);
    return 0;
}
