import { AxiosInstance } from 'axios';
import { IDerivedKey, IKey, KeyInformation, utils } from '../../index.js';
import { Keys } from '../types.js';

/**
 * An interface abstracting the connection between the SDK module and the actual key storage implementation.
 * In the future, a user might be able to select where they want to store their keys, such as MintBlue API vs AWS CloudHSM.
 */
export interface IDerivationService {
  /**
   * Get the master key for encryption and decryption operations.
   */
  getMasterKey(): CryptoKey;

  /**
   * Save a key with a specific name to the key storage.
   * @param derivationKey The key or derived key to be saved.
   * @param name The name associated with the key.
   */
  saveKey(derivationKey: IKey | IDerivedKey, name: string, keyProtected: boolean): Promise<void>;

  /**
   * Retrieve a map of keys from the key storage.
   * @returns A promise that resolves to a map of keys.
   */
  getKeys(): Promise<Map<string, APIDerivation>>;
}

/**
 * Serialize the key information to a string format.
 * @param keyInfo The KeyInformation object to be serialized.
 * @returns The serialized string representation of the KeyInformation.
 */
export function serialiseKeyInformation(keyInfo: KeyInformation): string {
  return JSON.stringify(keyInfo);
}

/**
 * Deserialize a serialized string to obtain the KeyInformation object.
 * @param parsed The serialized string representation of KeyInformation.
 * @returns The deserialized KeyInformation object.
 */
export function deserialiseKeyInformation(parsed: string): KeyInformation {
  return JSON.parse(parsed);
}

export interface APIDerivation {
  id: string;
  name: string;
  path: string;
  pubkey: string;
  public: boolean;
  fingerprint: string;
  user_id: string;
  account_id: string;
  created_at: string;
  updated_at: string;
}

type Fingerprint = string;

/**
 * A service class for interacting with derivations API
 */
export class APIDerivationService implements IDerivationService {
  /**
   * Constructor for the APIDerivationService class.
   * @param api The AxiosInstance used for API communication.
   * @param keys The MintBlue key storage configuration.
   */
  constructor(
    private api: AxiosInstance,
    private keys: Keys,
  ) {}

  /**
   * Get the master key for encryption and decryption operations.
   * @returns The CryptoKey representing the master key.
   */
  getMasterKey(): CryptoKey {
    return this.keys.masterKey;
  }

  /**
   * Retrieve the list of keys from the API.
   * @returns A promise that resolves to a map of APIDerivation objects by fingerprint.
   */
  async getKeys(): Promise<Map<Fingerprint, APIDerivation>> {
    const response = await this.api.get<{ [fingerprint: string]: APIDerivation }>('/derivations');
    const derivations = response.data;
    const map = new Map();

    for (const key in derivations) {
      map.set(key, derivations[key]);
    }

    return map;
  }

  /**
   * Save a key by posting it to the API.
   * @param derivationKey The key or derived key to be saved.
   * @param name The name associated with the key.
   */
  async saveKey(derivationKey: IKey | IDerivedKey, name: string, keyProtected = false) {
    const keyInfo = await derivationKey.exportKeyInformation();

    await this.api.post('/derivations', {
      name,
      path: await utils.encryptToB64String(Buffer.from(serialiseKeyInformation(keyInfo)), this.keys.masterKey),
      pubkey: keyInfo.publicKey,
      fingerprint: keyInfo.fingerprint,
      keyProtected,
    });
  }
}
