const jose = require("node-jose");

async function encrypt(request, clientId, encryptionKey) {
    //console.log("🔒 [Encrypt] Starting...");

    const keystore = jose.JWK.createKeyStore();
    const jwk = {
        kty: "oct",
        k: Buffer.from(encryptionKey).toString('base64'),
        alg: "A256GCM"
    };
    const key = await keystore.add(jwk);
    //console.log("🔑 [Encrypt] Using encKey (base64):", jwk.k);
    const normalizedRequest = request;
    //console.log("🔑 [Encrypt] Input request:", normalizedRequest);
    const input = Buffer.from(normalizedRequest, "utf8");
    const header = {
        alg: "dir",
        enc: "A256GCM",
        clientid: clientId
    };
    const encrypted = await jose.JWE.createEncrypt(
        { format: "compact", fields: header },
        key
    ).update(input).final();
    //console.log("   ✅ Encrypted length:", encrypted.length);
    //console.log("📦 [Encrypt] Encrypted JWE:", encrypted);
    return encrypted;
}

// 🔹 DECRYPT

async function decrypt(encryptedData, encryptionKey, encryptionKeyId) {
    //console.log("🔓 [Decrypt] Starting...");
     console.log("🔓 [Decrypt] Encrypted input (truncated):", encryptedData);
    const keystore = jose.JWK.createKeyStore();
    const jwk = {
        kty: "oct",
        k: Buffer.from(encryptionKey).toString('base64'),
        alg: "A256GCM",
        kid: encryptionKeyId
    };
    const key = await keystore.add(jwk);
    const jweObject = await jose.JWE.createDecrypt(key).decrypt(encryptedData);
    const decrypted = jweObject.plaintext.toString("utf8");
    console.log("✅ Decrypted JSON:", decrypted);
    return decrypted;
}

// 🔹 SIGN

const toBase64Url = (buf) => Buffer.from(buf)
  .toString('base64')
  .replace(/\+/g, '-')
  .replace(/\//g, '_')
  .replace(/=+$/, '');
async function sign(request, clientId, signingKey) {
  console.log("✍️ [Sign] Starting...");
  //console.log("✍️ [Sign] Signing with clientId:", clientId);
  //console.log("✍️ [Sign] signingKey length:", signingKey?.length);
  const keystore = jose.JWK.createKeyStore();
  const jwk = {
    kty: "oct",
    k: Buffer.from(signingKey).toString('base64'), 
    alg: "HS256"
  };
  const key = await keystore.add(jwk);
  const header = { alg: "HS256", clientid: clientId };
  const jwsObject = await jose.JWS.createSign({ format: "compact", fields: header }, key)
    .update(request)
    .final();
  const jwsString = (typeof jwsObject === "string") ? jwsObject : JSON.stringify(jwsObject);
  //console.log("✅ [Sign] JWS length:", jwsString.length);
  //console.log("✅ [Sign] JWS:", jwsString);
  return jwsString;
}

// 🔹 VERIFY

async function verify(request, signingKey, signingKeyId) {
    //console.log("🕵️ [Verify] Starting...");
    const keystore = jose.JWK.createKeyStore();
    const jwk = {
        kty: "oct",
        k: Buffer.from(signingKey).toString('base64'),  
        alg: "HS512",                                   
        use: "sig"
    };
    const key = await keystore.add(jwk);
    const verified = await jose.JWS.createVerify(key).verify(request);
    //console.log("✅ Signature verified");
    return verified.payload.toString("utf8");
}

// 🔹 WRAPPERS

async function encryptAndSign(request, clientId, encryptionKey, signingKey) {
    //console.log("🔐 [encryptAndSign] Wrapping...");
    const encrypted = await encrypt(request, clientId, encryptionKey);
    const encryptedWithCRLF = encrypted.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n');
    const signed = await sign(encryptedWithCRLF, clientId, signingKey);
    return signed;
}

async function verifyAndDecrypt(request, encryptionKey, encryptionKeyId, signingKey, signingKeyId) {
    //console.log("🔓 [verifyAndDecrypt] Wrapping...");
    if (!request || request.length === 0 || request[0] === "{") {
        console.log("   ℹ️ Already plain JSON");
        return request;
    }
    const verified = await verify(request, signingKey, signingKeyId);
    const decrypted = await decrypt(verified, encryptionKey, encryptionKeyId);
    //console.log("Decrypted: ", decrypted);
    return decrypted;
}

module.exports = {
    encrypt,
    decrypt,
    sign,
    verify,
    encryptAndSign,
    verifyAndDecrypt
};
