import crypto from 'crypto';
import { CoerceToUint8ArrayInput } from '@alessiofrittoli/crypto-buffer/coercion';
import { Cph } from './types.mjs';
import 'stream';
import '@alessiofrittoli/crypto-buffer';

/**
 * Utility class for AES encryption and decryption following [NIST SP 800-38D](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) standard reccomendations.
 */
declare class Cipher {
    /**
     * Cipher default `salt` lengths.
     */
    static readonly SALT_LENGTH: {
        readonly min: 16;
        readonly max: 64;
        readonly default: 32;
    };
    /**
     * Cipher default `IV` lengths.
     */
    static readonly IV_LENGTH: {
        readonly min: 8;
        readonly max: 32;
        readonly default: 16;
    };
    /**
     * Cipher default `Auth Tag` lengths.
     */
    static readonly AUTH_TAG_LENGTH: {
        readonly min: 4;
        readonly max: 16;
        readonly default: 16;
    };
    /**
     * Cipher default `Additional Authenticated Data` lengths.
     */
    static readonly AAD_LENGTH: {
        readonly min: 16;
        readonly max: 128;
        readonly default: 32;
    };
    /**
     * Default AES algorithms used based on functionality.
     */
    static readonly DEFAULT_ALGORITHM: {
        readonly buffer: "aes-256-gcm";
        readonly stream: "aes-256-cbc";
    };
    /**
     * Supported AES algorithms.
     */
    static readonly ALGORITHMS: Cph.AesAlgorithm[];
    /**
     * Encrypt in-memory data buffer.
     *
     * ⚠️ This is not suitable for large data. Use {@link Cipher.streamEncrypt()} or {@link Cipher.hybridEncrypt()} methods for large data encryption.
     *
     * @param	data	The data to encrypt.
     * @param	secret	The secret key used to encrypt the `data`.
     * @param	options	( Optional ) Additional options.
     * @returns	The encrypted result Buffer.
     */
    static encrypt(data: CoerceToUint8ArrayInput, secret: CoerceToUint8ArrayInput, options?: Cph.Options): Buffer;
    /**
     * Decrypt in-memory data buffer.
     *
     * ⚠️ This is not suitable for large data. Use {@link Cipher.streamDecrypt()} or {@link Cipher.hybridDecrypt()} methods for large data decryption.
     *
     * @param	data	The data to decrypt.
     * @param	secret	The secret key used to decrypt the `data`.
     * @param	options	( Optional ) Additional options. Must be the same used while encrypting data.
     * @returns	The decrypted data Buffer.
     */
    static decrypt(data: CoerceToUint8ArrayInput, secret: CoerceToUint8ArrayInput, options?: Cph.Options): Buffer;
    /**
     * Encrypt a `Readable` to a `Writable` Stream.
     *
     * The `Readable` Stream could be 'in-memory buffer' or 'file' based.
     *
     * @param	secret	The secret key used to encrypt the `data`.
     * @param	options Additional options.
     * @returns An object containing:
     * 	- a new instance of `crypto.Cipher` allowing you to add listeners to the `cipher` encryption process.
     * 	- the actual `encrypt` callback that must be called and awaited in order to start the encryption process.
     */
    static streamEncrypt(secret: CoerceToUint8ArrayInput, options: Cph.Stream.Symmetric.EncryptOptions): Cph.Stream.Symmetric.EncryptReturnType;
    /**
     * Decrypt a `Readable` to a `Writable` Stream.
     *
     * The `Readable` Stream could be 'in-memory buffer' or 'file' based.
     *
     * @param	secret	The secret key used to encrypt the `data`.
     * @param	options Additional options.
     * @returns A new Promise that resolves when Key IV extraction completes returning an object containing:
     * 	- a new instance of `crypto.Decipher` allowing you to add listeners to the `decipher` decryption process.
     * 	- the actual `decrypt` callback that must be called and awaited in order to start the decryption process.
     */
    static streamDecrypt(secret: CoerceToUint8ArrayInput, options: Cph.Stream.Symmetric.DecryptOptions): Promise<Cph.Stream.Symmetric.DecryptReturnType>;
    /**
     * Encrypt a `Readable` to a `Writable` Stream with hybird Encryption.
     *
     * The `Readable` Stream could be 'in-memory buffer' or 'file' based.
     *
     * @param	secret		The secret key used to encrypt the stream.
     * @param	publicKey	The RSA public key used to encrypt the symmetric key.
     * @param	options		Options for the stream encryption.
     * @returns An object containing:
     * 	- a new instance of `crypto.Cipher` allowing you to add listeners to the `cipher` encryption process.
     * 	- the actual `encrypt` callback that must be called and awaited in order to start the encryption process.
     */
    static hybridEncrypt(secret: CoerceToUint8ArrayInput, publicKey: crypto.RsaPublicKey | crypto.RsaPrivateKey | crypto.KeyLike, options: Cph.Stream.Hybrid.EncryptOptions): Cph.Stream.Hybrid.EncryptReturnType;
    /**
     * Decrypt a `Readable` to a `Writable` Stream with hybrid Decryption.
     *
     * The `Readable` Stream could be 'in-memory buffer' or 'file' based.
     *
     * @param	privateKey	The RSA private key used to decrypt the symmetric key.
     * @param	options		Options for the stream decryption.
     * @returns A new Promise that resolves when Key IV extraction completes returning an object containing:
     * 	- a new instance of `crypto.Decipher` allowing you to add listeners to the `decipher` decryption process.
     * 	- the actual `decrypt` callback that must be called and awaited in order to start the decryption process.
     */
    static hybridDecrypt(privateKey: crypto.RsaPrivateKey | crypto.KeyLike, options: Cph.Stream.Hybrid.DecryptOptions): Promise<Cph.Stream.Hybrid.DecryptReturnType>;
    /**
     * Handle pipe flow to encrypt a Stream.
     *
     * @param	options Required parameters.
     * @returns A new Promise that resolves `void` once stream is completed.
     */
    private static stream;
    /**
     * Handle pipe flow to decrypt a Stream.
     *
     * @param	options Required parameters.
     * @returns A new Promise that resolves `void` once stream is completed.
     */
    private static decipherStream;
    /**
     * Extract the Cipher Encrypted Symmetric Key and Initialization Vector from an encrypted `Readable` Stream.
     *
     * @param	input		The `Readable` Stream.
     * @param	keyLength	The encrypted key length in bytes. This is used to properly extract the encrypted Cipher Key and Initialization Vector.
     * @returns	A new Promise that resolve a tuple containing the Cipher Encrypted Symmetric Key and Initialization Vector once fulfilled.
     */
    private static extractKeyIV;
    /**
     * Generates a `Scrypt` Symmetric Key and the Initialization Vector with the given options.
     *
     * @param	secret	The secret key.
     * @param	options	( Optional ) Additional options.
     *
     * @returns An object with the generated `Key`, `IV`, `salt` and resolved `options`.
     */
    static newKeyIV<T extends Cph.ResolvedOptions = Cph.ResolvedOptions>(secret: CoerceToUint8ArrayInput, options?: Cph.Options): {
        options: T;
        Key: Buffer<ArrayBufferLike>;
        IV: Buffer<ArrayBufferLike>;
        AAD: Buffer<ArrayBufferLike> | Uint8Array<ArrayBufferLike>;
        salt: Buffer<ArrayBufferLike>;
    };
    /**
     * Resolves the given `options` with `Cipher` defaults and constraints.
     *
     * @param options ( Optional ) Additional options.
     * @returns The given `options` with `Cipher` defaults and constraints.
     */
    static resolveOptions<T extends Cph.ResolvedOptions = Cph.ResolvedOptions>(options?: Cph.Options): T;
    /**
     * Get the Initialization Vector length based on the given algorithm.
     *
     * @param	algorithm The algorithm in use.
     * @param	options ( Optional ) Additional options.
     * @returns	The Initialization Vector length based on the given algorithm
     */
    private static getIVLength;
    /**
     * Ensure correct key length based on the given algorithm.
     *
     * @param	algorithm The AES algorithm name.
     * @returns	An object with validated `algorithm` and `keyLength`.
     */
    private static getKeyLength;
    /**
     * Check if the given algorithm is a Cipher AES-GCM algorithm.
     *
     * @param algorithm The AES Algorithm to check.
     * @returns `true` if the given algorithm is a Cipher AES-GCM algorithm. `false` otherwise.
     */
    static isGCM(algorithm: Cph.AesAlgorithm): algorithm is crypto.CipherGCMTypes;
    /**
     * Check if the given algorithm is a Cipher AES-CCM algorithm.
     *
     * @param algorithm The AES Algorithm to check.
     * @returns `true` if the given algorithm is a Cipher AES-CCM algorithm. `false` otherwise.
     */
    static isCCM(algorithm: Cph.AesAlgorithm): algorithm is crypto.CipherCCMTypes;
    /**
     * Check if the given algorithm is a Cipher AES-OCB algorithm.
     *
     * @param algorithm The AES Algorithm to check.
     * @returns `true` if the given algorithm is a Cipher AES-OCB algorithm. `false` otherwise.
     */
    static isOCB(algorithm: Cph.AesAlgorithm): algorithm is crypto.CipherOCBTypes;
    /**
     * Check if the given algorithm is a Cipher AES-CBC algorithm.
     *
     * @param algorithm The AES Algorithm to check.
     * @returns `true` if the given algorithm is a Cipher AES-CBC algorithm. `false` otherwise.
     */
    static isCBC(algorithm: Cph.AesAlgorithm): algorithm is Cph.CBCTypes;
}

export { Cipher };
