// secret_store.cpp — implementation of fn_secret (issue 0129).
//
// See secret_store.h for API docs and platform notes.

#include "infra/secret_store.h"

#include <algorithm>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>

#ifdef _WIN32
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>
#  include <wincrypt.h>
#  pragma comment(lib, "crypt32.lib")
#endif

namespace fn_secret {

// ---------------------------------------------------------------------------
// Base64 helpers (no external deps, RFC 4648 alphabet)
// ---------------------------------------------------------------------------

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

static std::string base64_encode(const uint8_t* data, size_t len) {
    std::string out;
    out.reserve(((len + 2) / 3) * 4);
    for (size_t i = 0; i < len; i += 3) {
        uint32_t b = (uint32_t)data[i] << 16;
        if (i + 1 < len) b |= (uint32_t)data[i + 1] << 8;
        if (i + 2 < len) b |= (uint32_t)data[i + 2];
        out += kB64Chars[(b >> 18) & 63];
        out += kB64Chars[(b >> 12) & 63];
        out += (i + 1 < len) ? kB64Chars[(b >> 6) & 63] : '=';
        out += (i + 2 < len) ? kB64Chars[(b)     & 63] : '=';
    }
    return out;
}

static std::vector<uint8_t> base64_decode(const std::string& s) {
    auto decode_char = [](char c) -> int {
        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 == '+') return 62;
        if (c == '/') return 63;
        return -1;
    };
    std::vector<uint8_t> out;
    out.reserve(s.size() * 3 / 4);
    for (size_t i = 0; i + 3 < s.size(); i += 4) {
        int a = decode_char(s[i]);
        int b = decode_char(s[i + 1]);
        int c = decode_char(s[i + 2]);
        int d = decode_char(s[i + 3]);
        if (a < 0 || b < 0) break;
        out.push_back((uint8_t)((a << 2) | (b >> 4)));
        if (c >= 0) out.push_back((uint8_t)((b << 4) | (c >> 2)));
        if (d >= 0) out.push_back((uint8_t)((c << 2) | d));
    }
    return out;
}

// ---------------------------------------------------------------------------
// Linux fallback: XOR with a stable per-user key
// ---------------------------------------------------------------------------

#ifndef _WIN32
static std::vector<uint8_t> linux_key() {
    // Key = first 32 bytes of SHA-256-like mixing of LOGNAME + HOSTNAME.
    // Good enough to prevent casual plaintext inspection; NOT crypto-secure.
    const char* user = getenv("LOGNAME");
    const char* host = getenv("HOSTNAME");
    if (!user) user = "user";
    if (!host) host = "localhost";
    std::string seed = std::string(user) + "@" + host + ":fn_agents_dashboard_key_v1";
    std::vector<uint8_t> key(32, 0);
    for (size_t i = 0; i < seed.size(); i++) {
        key[i % 32] ^= (uint8_t)seed[i];
        key[(i + 7) % 32] += (uint8_t)(seed[i] * 31 + i);
        key[(i + 13) % 32] ^= (uint8_t)(seed[i] + i * 7);
    }
    return key;
}
#endif

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

bool is_strong() {
#ifdef _WIN32
    return true;
#else
    return false;
#endif
}

std::vector<uint8_t> encrypt(const std::string& plaintext) {
#ifdef _WIN32
    DATA_BLOB in_blob  { (DWORD)plaintext.size(),
                          (BYTE*)const_cast<char*>(plaintext.data()) };
    DATA_BLOB out_blob {};
    if (!CryptProtectData(&in_blob, L"fn_agents_dashboard", nullptr,
                          nullptr, nullptr, 0, &out_blob)) {
        return {};
    }
    std::vector<uint8_t> result(out_blob.pbData,
                                out_blob.pbData + out_blob.cbData);
    LocalFree(out_blob.pbData);
    return result;
#else
    // Linux: 1-byte magic + XOR
    std::vector<uint8_t> key = linux_key();
    std::vector<uint8_t> out;
    out.reserve(1 + plaintext.size());
    out.push_back(0xAF);  // magic marker
    for (size_t i = 0; i < plaintext.size(); i++) {
        out.push_back((uint8_t)plaintext[i] ^ key[i % key.size()]);
    }
    return out;
#endif
}

std::string decrypt(const std::vector<uint8_t>& blob) {
    if (blob.empty()) return {};
#ifdef _WIN32
    DATA_BLOB in_blob  { (DWORD)blob.size(),
                          (BYTE*)const_cast<uint8_t*>(blob.data()) };
    DATA_BLOB out_blob {};
    if (!CryptUnprotectData(&in_blob, nullptr, nullptr,
                            nullptr, nullptr, 0, &out_blob)) {
        return {};
    }
    std::string result(reinterpret_cast<char*>(out_blob.pbData),
                       out_blob.cbData);
    LocalFree(out_blob.pbData);
    return result;
#else
    // Linux: check magic, XOR decode
    if (blob[0] != 0xAF) return {};
    std::vector<uint8_t> key = linux_key();
    std::string out;
    out.reserve(blob.size() - 1);
    for (size_t i = 1; i < blob.size(); i++) {
        out += (char)(blob[i] ^ key[(i - 1) % key.size()]);
    }
    return out;
#endif
}

std::string encrypt_b64(const std::string& plaintext) {
    auto blob = encrypt(plaintext);
    if (blob.empty()) return {};
    return base64_encode(blob.data(), blob.size());
}

std::string decrypt_b64(const std::string& b64) {
    auto blob = base64_decode(b64);
    return decrypt(blob);
}

} // namespace fn_secret
