/*
 * importar-conta-llm - Importa contas LLM de outro sistema
 * Uso: pp --importar-conta-llm=<sistema_fonte>
 *
 * Lista as contas do sistema fonte, aceita selecao multipla (ex: 1,3,5)
 * ou selecao total ('todas', 'todos' ou '*') e copia os presets JSON
 * e respectivos secretos.
 */

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

typedef struct {
    char nome[PATH_MAX];
    char arquivo[PATH_MAX];
    char caminho[PATH_MAX];
} Conta;

static void uso(void)
{
    fputs(
        "Uso:\n"
        "  pp --importar-conta-llm=<sistema_fonte>\n"
        "  pp --importar-conta-llm --ajuda:<grupo>\n"
        "  pp --importar-conta-llm --ajuda=<grupo>\n"
        "\n"
        "Descrição:\n"
        "  Importa contas LLM de outro sistema irmão em sisc/ ou da biblioteca interna para o sistema atual.\n"
        "\n"
        "Grupos de ajuda: uso, fonte, selecao, arquivos, exemplos.\n"
        "\n"
        "1) Uso e ajuda [uso ajuda]\n"
        "  --ajuda, --help, -h            Exibe esta ajuda\n"
        "  --ajuda:<grupo>, --ajuda=<grupo>  Filtra um grupo via pp. Ex: pp --importar-conta-llm --ajuda:selecao\n"
        "\n"
        "2) Fonte [fonte entrada]\n"
        "  --importar-conta-llm=<sistema_fonte>        Sistema irmão usado como origem\n"
        "  Origem comum: <raiz_sisc>/<sistema_fonte>/contas-llm/ e /secretos/.\n"
        "  Fonte especial: biblioteca usa core/biblioteca-contas-llm/ e core/biblioteca-secretos/.\n"
        "  Exemplo com todas as flags do grupo:\n"
        "    pp --importar-conta-llm=biblioteca\n"
        "\n"
        "3) Seleção [selecao]\n"
        "  Após listar os itens, aceita números separados por vírgula ou espaço (ex: 1,3,5).\n"
        "  Para importar tudo, use 'todas', 'todos' ou '*'. Duplicatas são ignoradas.\n"
        "\n"
        "4) Arquivos copiados [arquivos]\n"
        "  Destino: contas-llm/ e secretos/ no sistema atual; os diretórios são criados se faltarem.\n"
        "  Copia secretos referenciados por apiKey, arquivo ou conexão tipo key; se já existirem, pergunta antes.\n"
        "\n"
        "5) Exemplos [exemplos]\n"
        "  pp --importar-conta-llm=biblioteca\n"
        "  pp --importar-conta-llm=siscore\n"
        "  pp --importar-conta-llm=outro-projeto\n",
        stdout);
}

static void resolver_dir_sistema(const char *raiz_sisc, const char *sistema, const char *subdir, char *out, size_t tam)
{
    snprintf(out, tam, "%s/%s/%s", raiz_sisc, sistema, subdir);
}

static void resolver_dir_biblioteca(const char *raiz_sisc, const char *nome, char *out, size_t tam)
{
    snprintf(out, tam, "%s/core/biblioteca-%s", raiz_sisc, nome);
}

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 int cmp_conta(const void *a, const void *b)
{
    const Conta *ca = (const Conta *)a, *cb = (const Conta *)b;
    return strcasecmp(ca->nome, cb->nome);
}

static int listar_contas(const char *dir_contas, Conta **out, int *total_out)
{
    DIR *d = opendir(dir_contas);
    if (!d) return -1;
    int cap = 32, total = 0;
    Conta *v = calloc((size_t)cap, sizeof(Conta));
    if (!v) { closedir(d); return -1; }
    struct dirent *e;
    while ((e = readdir(d))) {
        const char *n = e->d_name;
        if (n[0] == '.' || strcmp(n, "llm.json") == 0 || !termina_com(n, ".json")) continue;
        char path[PATH_MAX * 2];
        snprintf(path, sizeof(path), "%s/%s", dir_contas, n);
        struct stat st;
        if (stat(path, &st) != 0 || !S_ISREG(st.st_mode)) continue;
        if (total >= cap) { cap *= 2; Conta *nv = realloc(v, (size_t)cap * sizeof(Conta)); if (!nv) { free(v); closedir(d); return -1; } v = nv; }
        snprintf(v[total].arquivo, sizeof(v[total].arquivo), "%s", n);
        snprintf(v[total].caminho, sizeof(v[total].caminho), "%s", path);
        snprintf(v[total].nome, sizeof(v[total].nome), "%s", n);
        v[total].nome[strlen(v[total].nome) - 5] = '\0';
        total++;
    }
    closedir(d);
    qsort(v, (size_t)total, sizeof(Conta), cmp_conta);
    *out = v;
    *total_out = total;
    return 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;
}

static int escrever_arquivo(const char *p, const char *conteudo, mode_t modo)
{
    FILE *fp = fopen(p, "wb");
    if (!fp) return -1;
    size_t len = strlen(conteudo);
    size_t n = fwrite(conteudo, 1, len, fp);
    if (len == 0 || conteudo[len - 1] != '\n') fputc('\n', fp);
    fclose(fp);
    chmod(p, modo);
    return n == len ? 0 : -1;
}

static int copiar_arquivo(const char *origem, const char *destino, mode_t modo)
{
    char *conteudo = ler_arquivo(origem);
    if (!conteudo) return -1;
    int r = escrever_arquivo(destino, conteudo, modo);
    free(conteudo);
    return r;
}

static char *json_get_string(const char *json, const char *key)
{
    char pat[128];
    snprintf(pat, sizeof(pat), "\"%s\"", key);
    const char *p = strstr(json, 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 *out = malloc(strlen(p) + 1);
    if (!out) return NULL;
    size_t j = 0;
    while (*p && *p != '"') {
        if (*p == '\\' && p[1]) p++;
        out[j++] = *p++;
    }
    out[j] = '\0';
    return out;
}

static int nome_seguro(const char *s)
{
    if (!s || !*s || strstr(s, "..") || strchr(s, '/') || strchr(s, '\\')) return 0;
    for (const char *p = s; *p; p++)
        if (!(isalnum((unsigned char)*p) || *p == '.' || *p == '_' || *p == '-')) return 0;
    return 1;
}

typedef struct { char itens[8][PATH_MAX]; int total; } Segredos;

static void seg_add(Segredos *s, const char *nome, const char *ext)
{
    if (!nome_seguro(nome) || s->total >= 8) return;
    char arq[PATH_MAX];
    if (termina_com(nome, ".txt") || termina_com(nome, ".json"))
        snprintf(arq, sizeof(arq), "%s", nome);
    else
        snprintf(arq, sizeof(arq), "%s%s", nome, ext);
    for (int i = 0; i < s->total; i++)
        if (strcmp(s->itens[i], arq) == 0) return;
    snprintf(s->itens[s->total++], PATH_MAX, "%s", arq);
}

static Segredos segredos_conta(const char *path)
{
    Segredos s;
    memset(&s, 0, sizeof(s));
    char *json = ler_arquivo(path);
    if (!json) return s;
    char *tipo = json_get_string(json, "tipo");
    char *apiKey = json_get_string(json, "apiKey");
    char *arquivo = json_get_string(json, "arquivo");
    char *conexao = json_get_string(json, "conexao");
    if (apiKey) seg_add(&s, apiKey, ".txt");
    if (arquivo) seg_add(&s, arquivo, ".json");
    if (!apiKey && tipo && strcmp(tipo, "key") == 0 && conexao) seg_add(&s, conexao, ".txt");
    free(tipo); free(apiKey); free(arquivo); free(conexao);
    free(json);
    return s;
}

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 int confirmar_sobrescrita(const char *arquivo)
{
    char linha[16];
    printf("  Arquivo '%s' ja existe. Sobrescrever? [s/N]: ", arquivo);
    fflush(stdout);
    if (!fgets(linha, sizeof(linha), stdin)) return 0;
    return (linha[0] == 's' || linha[0] == 'S' || linha[0] == 'y' || linha[0] == 'Y');
}

/* Analisa entrada como "1,3,5", "todas", "todos", "*" ou numero unico.
 * Preenche o array 'selecionados' com indices 0-based.
 * Retorna o numero de itens selecionados ou -1 se invalido. */
static int parse_selecao(const char *entrada, int total, int *selecionados, int max_sel)
{
    char *dup = strdup(entrada);
    if (!dup) return -1;

    /* Trim */
    char *p = dup;
    while (isspace((unsigned char)*p)) p++;
    char *fim = p + strlen(p);
    while (fim > p && isspace((unsigned char)fim[-1])) *--fim = '\0';
    if (!*p) { free(dup); return -1; }

    /* 'todas' */
    if (strcasecmp(p, "todas") == 0 || strcasecmp(p, "todos") == 0 || strcmp(p, "*") == 0) {
        int n = 0;
        for (int i = 0; i < total && n < max_sel; i++) selecionados[n++] = i;
        free(dup);
        return n;
    }

    /* Lista separada por virgula */
    int n = 0;
    char *save = NULL;
    char *tok = strtok_r(p, ", \t", &save);
    while (tok && n < max_sel) {
        while (isspace((unsigned char)*tok)) tok++;
        if (isdigit((unsigned char)*tok)) {
            int num = atoi(tok);
            if (num >= 1 && num <= total) {
                /* Verifica duplicata */
                int dup_found = 0;
                for (int i = 0; i < n; i++)
                    if (selecionados[i] == num - 1) { dup_found = 1; break; }
                if (!dup_found) selecionados[n++] = num - 1;
            }
        }
        tok = strtok_r(NULL, ", \t", &save);
    }
    free(dup);
    return n > 0 ? n : -1;
}

int main(int argc, char **argv)
{
    if (restricao_verificar("importar-conta-llm") != 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);
    }

    const char *sistema_fonte = argv[1];

    /* Valida nome do sistema fonte */
    if (sistema_fonte[0] == '-' || strchr(sistema_fonte, '/') || strchr(sistema_fonte, '\\') ||
        strstr(sistema_fonte, "..") || strlen(sistema_fonte) > 128) {
        fprintf(stderr, "importar-conta-llm: nome de sistema fonte invalido.\n");
        return 1;
    }

    /* Obtem raiz do sisc (pai do diretorio atual) */
    char cwd[PATH_MAX];
    if (!getcwd(cwd, sizeof(cwd))) { perror("getcwd"); return 1; }

    /* Diretorio atual deve ser subdiretorio direto de sisc */
    char raiz_sisc[PATH_MAX];
    snprintf(raiz_sisc, sizeof(raiz_sisc), "%s", cwd);
    char *slash = strrchr(raiz_sisc, '/');
    if (!slash || slash == raiz_sisc) {
        if (raiz_sisc_por_executavel(raiz_sisc, sizeof(raiz_sisc)) != 0) {
            fprintf(stderr, "importar-conta-llm: nao foi possivel determinar raiz sisc.\n");
            return 1;
        }
    } else {
        *slash = '\0';
    }

    /* Localiza contas-llm e secretos do sistema fonte ou da biblioteca interna */
    int fonte_biblioteca = (strcasecmp(sistema_fonte, "biblioteca") == 0);
    char src_contas[PATH_MAX * 2], src_secretos[PATH_MAX * 2];
    if (fonte_biblioteca) {
        resolver_dir_biblioteca(raiz_sisc, "contas-llm", src_contas, sizeof(src_contas));
        resolver_dir_biblioteca(raiz_sisc, "secretos", src_secretos, sizeof(src_secretos));
    } else {
        resolver_dir_sistema(raiz_sisc, sistema_fonte, "contas-llm", src_contas, sizeof(src_contas));
        resolver_dir_sistema(raiz_sisc, sistema_fonte, "secretos", src_secretos, sizeof(src_secretos));
    }

    /* Lista contas do sistema fonte */
    Conta *contas = NULL;
    int total = 0;
    if (listar_contas(src_contas, &contas, &total) != 0 || total == 0) {
        fprintf(stderr, "importar-conta-llm: nenhuma conta encontrada em %s.\n", src_contas);
        free(contas);
        return 1;
    }

    printf("%s: %s\n", fonte_biblioteca ? "Fonte" : "Sistema fonte", sistema_fonte);
    printf("Contas disponiveis em %s:\n", src_contas);
    for (int i = 0; i < total; i++) {
        printf("%2d) %s\n", i + 1, contas[i].nome);
    }
    printf("\n");

    /* Le selecao do usuario */
    printf("Digite os numeros das contas (ex: 1,3,5) ou 'todas': ");
    fflush(stdout);
    char entrada[4096];
    if (!fgets(entrada, sizeof(entrada), stdin)) {
        fprintf(stderr, "importar-conta-llm: entrada invalida.\n");
        free(contas);
        return 1;
    }
    /* Remove newline */
    entrada[strcspn(entrada, "\r\n")] = '\0';

    int selecionados[256];
    int nsel = parse_selecao(entrada, total, selecionados, 256);
    if (nsel <= 0) {
        fprintf(stderr, "importar-conta-llm: selecao invalida.\n");
        free(contas);
        return 1;
    }

    printf("\nImportando %d conta(s)...\n\n", nsel);

    /* Prepara diretorios de destino: sempre contas-llm/ e secretos/ na raiz do sistema atual */
    char dst_contas[PATH_MAX * 2], dst_secretos[PATH_MAX * 2];
    snprintf(dst_contas, sizeof(dst_contas), "%s/contas-llm", cwd);
    snprintf(dst_secretos, sizeof(dst_secretos), "%s/secretos", cwd);

    if (garantir_dir(dst_contas, 0775) != 0) {
        fprintf(stderr, "importar-conta-llm: nao foi possivel criar %s\n", dst_contas);
        free(contas);
        return 1;
    }
    if (garantir_dir(dst_secretos, 0700) != 0) {
        fprintf(stderr, "importar-conta-llm: nao foi possivel criar %s\n", dst_secretos);
        free(contas);
        return 1;
    }

    int importados = 0, pulados = 0, erros = 0;

    for (int i = 0; i < nsel; i++) {
        int idx = selecionados[i];
        Conta *c = &contas[idx];

        printf("[%d/%d] %s\n", i + 1, nsel, c->nome);

        /* Copia o preset JSON */
        char dst_json[PATH_MAX * 2];
        snprintf(dst_json, sizeof(dst_json), "%s/%s", dst_contas, c->arquivo);

        if (access(dst_json, F_OK) == 0) {
            if (!confirmar_sobrescrita(c->arquivo)) {
                printf("  -> pulado (ja existe)\n");
                pulados++;
                continue;
            }
        }

        if (copiar_arquivo(c->caminho, dst_json, 0644) != 0) {
            fprintf(stderr, "  [erro] nao foi possivel copiar %s\n", c->arquivo);
            erros++;
            continue;
        }
        printf("  [ok] contas-llm/%s\n", c->arquivo);

        /* Copia secretos vinculados */
        Segredos segs = segredos_conta(c->caminho);
        for (int j = 0; j < segs.total; j++) {
            char src_sec[PATH_MAX * 2], dst_sec[PATH_MAX * 2];
            snprintf(src_sec, sizeof(src_sec), "%s/%s", src_secretos, segs.itens[j]);
            snprintf(dst_sec, sizeof(dst_sec), "%s/%s", dst_secretos, segs.itens[j]);

            if (access(src_sec, F_OK) != 0) {
                printf("  [aviso] secreto nao encontrado na origem: %s\n", segs.itens[j]);
                continue;
            }

            if (access(dst_sec, F_OK) == 0) {
                if (!confirmar_sobrescrita(segs.itens[j])) {
                    printf("  -> secreto mantido: %s\n", segs.itens[j]);
                    continue;
                }
            }

            if (copiar_arquivo(src_sec, dst_sec, 0600) != 0) {
                fprintf(stderr, "  [erro] nao foi possivel copiar secreto: %s\n", segs.itens[j]);
            } else {
                printf("  [ok] secretos/%s\n", segs.itens[j]);
            }
        }
        importados++;
    }

    printf("\nImportacao concluida.\n");
    printf("Contas importadas: %d\n", importados);
    printf("Contas puladas (ja existiam): %d\n", pulados);
    if (erros > 0) printf("Erros: %d\n", erros);

    free(contas);
    return erros > 0 ? 1 : 0;
}
