#include "utils.h"
#include "buffer.h"
#include <Windows.h>
#include <ShlObj.h>
#include <Wincrypt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma comment(lib, "Crypt32.lib")

bool get_local_appdata_path(wchar_t* path, size_t len) {
    PWSTR folder_path = NULL;
    HRESULT hr = SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &folder_path);

    if (SUCCEEDED(hr)) {
        wcsncpy_s(path, len, folder_path, _TRUNCATE);
        CoTaskMemFree(folder_path);
        return true;
    }
    return false;
}

ByteBuffer* base64_decode(const char* input) {
    if (!input) return NULL;

    DWORD size = 0;
    if (!CryptStringToBinaryA(input, 0, CRYPT_STRING_BASE64, NULL, &size, NULL, NULL)) {
        return NULL;
    }

    ByteBuffer* buf = buffer_create(size);
    if (!buf) return NULL;

    if (!CryptStringToBinaryA(input, 0, CRYPT_STRING_BASE64, buf->data, &size, NULL, NULL)) {
        buffer_destroy(buf);
        return NULL;
    }

    buf->size = size;
    return buf;
}

void bytes_to_hex(const uint8_t* bytes, size_t len, char* out, size_t out_len) {
    if (!bytes || !out || out_len < len * 2 + 1) return;

    for (size_t i = 0; i < len; i++) {
        sprintf_s(out + i * 2, out_len - i * 2, "%02x", bytes[i]);
    }
}

char* escape_json_string(const char* str) {
    if (!str) return NULL;

    size_t len = strlen(str);
    size_t escaped_len = len * 6 + 1;
    char* escaped = malloc(escaped_len);
    if (!escaped) return NULL;

    size_t j = 0;
    for (size_t i = 0; i < len && j < escaped_len - 6; i++) {
        switch (str[i]) {
        case '"':  escaped[j++] = '\\'; escaped[j++] = '"'; break;
        case '\\': escaped[j++] = '\\'; escaped[j++] = '\\'; break;
        case '\b': escaped[j++] = '\\'; escaped[j++] = 'b'; break;
        case '\f': escaped[j++] = '\\'; escaped[j++] = 'f'; break;
        case '\n': escaped[j++] = '\\'; escaped[j++] = 'n'; break;
        case '\r': escaped[j++] = '\\'; escaped[j++] = 'r'; break;
        case '\t': escaped[j++] = '\\'; escaped[j++] = 't'; break;
        default:
            if (str[i] >= 0 && str[i] <= 0x1f) {
                j += sprintf_s(escaped + j, escaped_len - j, "\\u%04x", (int)str[i]);
            }
            else {
                escaped[j++] = str[i];
            }
        }
    }
    escaped[j] = '\0';
    return escaped;
}

bool read_file_content(const wchar_t* path, char** content, size_t* size) {
    if (!path || !content || !size) return false;

    FILE* f = NULL;
    if (_wfopen_s(&f, path, L"rb") != 0 || !f) return false;

    fseek(f, 0, SEEK_END);
    long file_size = ftell(f);
    fseek(f, 0, SEEK_SET);

    if (file_size < 0) {
        fclose(f);
        return false;
    }

    *content = malloc(file_size + 1);
    if (!*content) {
        fclose(f);
        return false;
    }

    size_t read_size = fread(*content, 1, file_size, f);
    fclose(f);

    (*content)[read_size] = '\0';
    *size = read_size;
    return true;
}

bool write_file_content(const wchar_t* path, const char* content, size_t size) {
    if (!path || !content) return false;

    FILE* f = NULL;
    if (_wfopen_s(&f, path, L"wb") != 0 || !f) return false;

    size_t written = fwrite(content, 1, size, f);
    fclose(f);

    return written == size;
}