import { SupportedKeyDerivationTypes } from '../module.js';
import { JWK } from 'jose';
import * as utils from '../../utils.js';

// A Key that has been imported into or created by MintBlue.
// This could be a key derived from the masterKey or just any raw key
export interface IKey {
  getFingerprint(): Promise<Uint8Array>;
  getPublicKey(): Promise<IPublicKey>;

  exportKeyInformation(): Promise<KeyInformation>;
  // This return type returns a normal JWK object but kid is required, no longer optional.
  toJWK({ includePrivate }: { includePrivate: boolean }): Promise<JWK & Required<Pick<JWK, 'kid'>>>;
  toCryptoKey(): Promise<CryptoKey>;
}

export interface IPublicKey {
  verify(signature: Uint8Array, message: Uint8Array): Promise<boolean>;
  getPublicKeyHex(): Promise<string>;
  getPublicKeyBytes(): Promise<Uint8Array>;
}

// TODO: Remove encrypt and decrypt methods to reduce confusion
export interface IEncryptableKey {
  encrypt(message: Uint8Array): Promise<Uint8Array>;
  decrypt(encryptedMessage: Uint8Array): Promise<Uint8Array>;
}

export interface IDerivedKey extends IKey {
  getPath(): DerivationNonce;
}

// TODO: Remove sign and verify methods to reduce confusion
export interface IAsymmetricKey extends IKey {
  getPublicKeyBytes(): Promise<Uint8Array>;
  getPrivateKeyBytes(): Promise<Uint8Array>;

  sign(message: Uint8Array): Promise<Uint8Array>;
  verify(signature: Uint8Array, message: Uint8Array): Promise<boolean>;
}

export class DerivationNonce {
  static fromString(nonce: string): DerivationNonce {
    const recovered = JSON.parse(nonce);

    return new DerivationNonce(recovered.salt, recovered.iterations);
  }

  constructor(
    public salt: string,
    public iterations: number,
  ) {}

  public toString() {
    return JSON.stringify({ salt: this.salt, iterations: this.iterations });
  }

  static fromRandom(): DerivationNonce {
    return new DerivationNonce(utils.hex.bufToHex(crypto.getRandomValues(new Uint8Array(32))), Math.random() * 1000);
  }
}

export interface IKeyDerivationStrategy {
  /**
   * Imports a buffer to be interpreted as raw key material
   */
  fromRaw(rawKey: Uint8Array): Promise<IKey>;

  /**
   * Creates a new key, derived from the masterKey, salted with the nonce
   */
  create(masterKey: CryptoKey, salt: string): Promise<IDerivedKey>;

  /**
   * Recovers a derived key created from the key derivation strategy
   */
  hydrate(masterKey: CryptoKey, nonce: DerivationNonce): Promise<IDerivedKey>;
}

export interface KeyInformation {
  version: number;
  curve: SupportedKeyDerivationTypes;
  key: {
    type: 'raw' | 'derived';
    // Derivation Path if derived, otherwise hex string
    rawKeyHex?: string;
    // From & Nonce are defined
    from?: string;
    nonce?: string;
  };
  fingerprint: string;
  publicKey: string;
}
