#include "odincipher.h"
#include "utilities.h"

Napi::FunctionReference *OdinCipherWrapper::constructor;

OdinCipherWrapper::OdinCipherWrapper(const Napi::CallbackInfo& info) : Napi::ObjectWrap<OdinCipherWrapper>(info) {
    _cipher = nullptr;
}

void OdinCipherWrapper::Finalize(Napi::Env env) {
    if (_cipher) {
        if (_cipher->free) {
            _cipher->free(_cipher);
        }
        _cipher = nullptr;
    }
}

Napi::Object OdinCipherWrapper::Init(Napi::Env env, Napi::Object exports) {
    Napi::Function func = DefineClass(env, "OdinCipher", {
        InstanceMethod<&OdinCipherWrapper::SetPassword>("setPassword", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
        InstanceMethod<&OdinCipherWrapper::GetPeerStatus>("getPeerStatus", static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
    });
    
    constructor = new Napi::FunctionReference();
    *constructor = Napi::Persistent(func);
    exports.Set("OdinCipher", func);
    return exports;
}

Napi::Value OdinCipherWrapper::CreateNewItem(const Napi::CallbackInfo& info) {
    return constructor->New({});
}

void OdinCipherWrapper::SetPassword(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    if (info.Length() < 1 || !info[0].IsTypedArray()) {
        Napi::TypeError::New(env, "Password as Uint8Array required").ThrowAsJavaScriptException();
        return;
    }
    Napi::Uint8Array data = info[0].As<Napi::Uint8Array>();
    
    // Lazy initialization: create cipher only when setPassword is called
    if (!_cipher) {
        _cipher = odin_crypto_create(ODIN_CRYPTO_VERSION);
        if (!_cipher) {
            Napi::TypeError::New(env, "Failed to create OdinCipher").ThrowAsJavaScriptException();
            return;
        }
    }
    
    odin_crypto_set_password(_cipher, data.Data(), (uint32_t)data.ByteLength());
}

/**
 * Get the encryption status of a specific peer.
 * 
 * @param info[0] Peer ID (number) - The peer to check status for
 * @returns {string} One of:
 *   - 'unknown': Peer status not yet determined
 *   - 'unencrypted': Peer is not using encryption
 *   - 'encrypted': Peer is using encryption with matching password
 *   - 'password_mismatch': Peer is encrypted but password doesn't match
 */
Napi::Value OdinCipherWrapper::GetPeerStatus(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    
    if (info.Length() < 1 || !info[0].IsNumber()) {
        Napi::TypeError::New(env, "Peer ID (number) required").ThrowAsJavaScriptException();
        return env.Undefined();
    }
    
    if (!_cipher) {
        // Cipher not initialized yet
        return Napi::String::New(env, "unknown");
    }
    
    uint64_t peerId = static_cast<uint64_t>(info[0].As<Napi::Number>().DoubleValue());
    OdinCryptoPeerStatus status = odin_crypto_get_peer_status(_cipher, peerId);
    
    switch (status) {
        case ODIN_CRYPTO_PEER_STATUS_ENCRYPTED:
            return Napi::String::New(env, "encrypted");
        case ODIN_CRYPTO_PEER_STATUS_UNENCRYPTED:
            return Napi::String::New(env, "unencrypted");
        case ODIN_CRYPTO_PEER_STATUS_PASSWORD_MISSMATCH:
            return Napi::String::New(env, "password_mismatch");
        case ODIN_CRYPTO_PEER_STATUS_UNKNOWN:
        default:
            return Napi::String::New(env, "unknown");
    }
}
