/*
 * vault1984 CLI — utility functions
 */

#include "util.h"
#include <string.h>
#include <ctype.h>

static const char b64_table[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int base64_encode(const unsigned char *src, size_t src_len, char *dst, size_t dst_len) {
    size_t needed = ((src_len + 2) / 3) * 4 + 1;
    if (dst_len < needed) return -1;

    size_t i = 0, j = 0;
    while (i + 2 < src_len) {
        unsigned int triple = ((unsigned int)src[i] << 16) |
                              ((unsigned int)src[i+1] << 8) |
                              (unsigned int)src[i+2];
        dst[j++] = b64_table[(triple >> 18) & 0x3F];
        dst[j++] = b64_table[(triple >> 12) & 0x3F];
        dst[j++] = b64_table[(triple >> 6) & 0x3F];
        dst[j++] = b64_table[triple & 0x3F];
        i += 3;
    }

    /* Handle remaining 1 or 2 bytes */
    size_t remaining = src_len - i;
    if (remaining == 1) {
        unsigned int a = src[i];
        dst[j++] = b64_table[(a >> 2) & 0x3F];
        dst[j++] = b64_table[(a << 4) & 0x3F];
        dst[j++] = '=';
        dst[j++] = '=';
    } else if (remaining == 2) {
        unsigned int a = src[i];
        unsigned int b = src[i + 1];
        dst[j++] = b64_table[(a >> 2) & 0x3F];
        dst[j++] = b64_table[((a << 4) | (b >> 4)) & 0x3F];
        dst[j++] = b64_table[(b << 2) & 0x3F];
        dst[j++] = '=';
    }

    dst[j] = '\0';
    return 0;
}

static int b64_val(char c) {
    if (c >= 'A' && c <= 'Z') return c - 'A';
    if (c >= 'a' && c <= 'z') return c - 'a' + 26;
    if (c >= '0' && c <= '9') return c - '0' + 52;
    if (c == '+' || c == '-') return 62;  /* - is url-safe variant */
    if (c == '/' || c == '_') return 63;  /* _ is url-safe variant */
    return -1;
}

int base64_decode(const char *src, unsigned char *dst, size_t dst_len, size_t *out_len) {
    size_t slen = strlen(src);
    /* count padding */
    int pad = 0;
    while (slen > 0 && src[slen - 1] == '=') { slen--; pad++; }
    /* strip whitespace */
    while (slen > 0 && (src[slen - 1] == '\n' || src[slen - 1] == '\r')) slen--;

    size_t needed = (slen * 3) / 4;
    if (dst_len < needed) return -1;

    size_t i = 0, j = 0;
    while (i + 3 < slen) {
        int a = b64_val(src[i++]);
        int b = b64_val(src[i++]);
        int c = b64_val(src[i++]);
        int d = b64_val(src[i++]);
        if (a < 0 || b < 0 || c < 0 || d < 0) return -1;

        dst[j++] = (unsigned char)((a << 2) | (b >> 4));
        dst[j++] = (unsigned char)(((b & 0x0F) << 4) | (c >> 2));
        dst[j++] = (unsigned char)(((c & 0x03) << 6) | d);
    }

    /* Handle remaining chars based on padding count */
    size_t rem = slen - i;
    if (rem >= 2) {
        int a = b64_val(src[i++]);
        int b = b64_val(src[i++]);
        if (a < 0 || b < 0) return -1;
        dst[j++] = (unsigned char)((a << 2) | (b >> 4));

        if (rem >= 3 && pad < 2) {
            int c = b64_val(src[i++]);
            if (c < 0) return -1;
            dst[j++] = (unsigned char)(((b & 0x0F) << 4) | (c >> 2));
        }
    }

    if (out_len) *out_len = j;
    return 0;
}

void url_encode(const char *src, char *dst, size_t dst_len) {
    static const char hex[] = "0123456789ABCDEF";
    size_t j = 0;

    for (size_t i = 0; src[i] && j + 3 < dst_len; i++) {
        unsigned char c = (unsigned char)src[i];
        if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
            dst[j++] = (char)c;
        } else {
            dst[j++] = '%';
            dst[j++] = hex[c >> 4];
            dst[j++] = hex[c & 0x0F];
        }
    }
    dst[j] = '\0';
}
