import { finalizeEvent, getPublicKey, nip04, nip44 } from "nostr-tools";
import { encrypt, decrypt } from "nostr-tools/nip49";
import { createDefer } from "applesauce-core/promise";
import { normalizeToSecretKey } from "applesauce-core/helpers";
/** A NIP-49 (Private Key Encryption) signer */
export class PasswordSigner {
    key = null;
    ncryptsec;
    nip04;
    nip44;
    get unlocked() {
        return !!this.key;
    }
    constructor() {
        this.nip04 = {
            encrypt: this.nip04Encrypt.bind(this),
            decrypt: this.nip04Decrypt.bind(this),
        };
        this.nip44 = {
            encrypt: this.nip44Encrypt.bind(this),
            decrypt: this.nip44Decrypt.bind(this),
        };
    }
    unlockPromise;
    requestUnlock() {
        if (this.key)
            return;
        if (this.unlockPromise)
            return this.unlockPromise;
        const p = createDefer();
        this.unlockPromise = p;
        return p;
    }
    /** Sets the ncryptsec from the key and password */
    async setPassword(password) {
        if (!this.key)
            throw new Error("Cant set password until unlocked");
        this.ncryptsec = encrypt(this.key, password);
    }
    /** Tests if the provided password is correct by decrypting the ncryptsec */
    async testPassword(password) {
        if (this.ncryptsec) {
            const key = decrypt(this.ncryptsec, password);
            if (!key)
                throw new Error("Failed to decrypt key");
        }
        else
            throw new Error("Missing ncryptsec");
    }
    /** Unlocks the signer by decrypting the ncryptsec using the provided password */
    async unlock(password) {
        if (this.key)
            return;
        if (this.ncryptsec) {
            try {
                this.key = decrypt(this.ncryptsec, password);
                if (!this.key)
                    throw new Error("Failed to decrypt key");
            }
            catch (error) {
                throw new Error("failed to decrypt key: " + (error instanceof Error ? error.message : String(error)));
            }
        }
        else
            throw new Error("Missing ncryptsec");
    }
    /** Locks the signer by removing the unencrypted key from memory */
    lock() {
        this.key = null;
    }
    // public methods
    async getPublicKey() {
        await this.requestUnlock();
        return getPublicKey(this.key);
    }
    async signEvent(event) {
        await this.requestUnlock();
        return finalizeEvent(event, this.key);
    }
    // NIP-04
    async nip04Encrypt(pubkey, plaintext) {
        await this.requestUnlock();
        return nip04.encrypt(this.key, pubkey, plaintext);
    }
    async nip04Decrypt(pubkey, ciphertext) {
        await this.requestUnlock();
        return nip04.decrypt(this.key, pubkey, ciphertext);
    }
    // NIP-44
    async nip44Encrypt(pubkey, plaintext) {
        await this.requestUnlock();
        return nip44.v2.encrypt(plaintext, nip44.v2.utils.getConversationKey(this.key, pubkey));
    }
    async nip44Decrypt(pubkey, ciphertext) {
        await this.requestUnlock();
        return nip44.v2.decrypt(ciphertext, nip44.v2.utils.getConversationKey(this.key, pubkey));
    }
    /** Creates a PasswordSigner from a hex private key or NIP-19 nsec and password */
    static async fromPrivateKey(privateKey, password) {
        const signer = new PasswordSigner();
        signer.key = normalizeToSecretKey(privateKey);
        await signer.setPassword(password);
        return signer;
    }
    /** Creates a PasswordSigner from a ncryptsec and unlocks it with the provided password */
    static async fromNcryptsec(ncryptsec, password) {
        const signer = new PasswordSigner();
        signer.ncryptsec = ncryptsec;
        if (password)
            await signer.unlock(password);
        return signer;
    }
}
