/**
 * Verifiable Credential & DID functions, specifically for EndorserSearch.org tools
 *
 * The goal is to make this folder similar across projects, then move it to a library.
 * Other projects: endorser-ch, image-api
 *
 */

import * as didJwt from "did-jwt";
import { JWTDecoded } from "did-jwt/lib/JWT";
import { IIdentifier } from "@veramo/core";
import * as u8a from "uint8arrays";

import { createDidPeerJwt } from "@/libs/crypto/vc/passkeyDidPeer";

export const ETHR_DID_PREFIX = "did:ethr:";

/**
 * Meta info about a key
 */
export interface KeyMeta {
  /**
   * Decentralized ID for the key
   */
  did: string;
  /**
   * Stringified IIDentifier object from Veramo
   */
  identity?: string;
  /**
   * The Webauthn credential ID in hex, if this is from a passkey
   */
  passkeyCredIdHex?: string;
}

/**
 * Tell whether a key is from a passkey
 * @param keyMeta contains info about the key, whose passkeyCredIdHex determines if the key is from a passkey
 */
export function isFromPasskey(keyMeta?: KeyMeta): boolean {
  return !!keyMeta?.passkeyCredIdHex;
}

export async function createEndorserJwtForKey(
  account: KeyMeta,
  payload: object,
) {
  if (account?.identity) {
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const identity: IIdentifier = JSON.parse(account.identity!);
    const privateKeyHex = identity.keys[0].privateKeyHex;
    const signer = await SimpleSigner(privateKeyHex as string);
    return didJwt.createJWT(payload, {
      issuer: account.did,
      signer: signer,
    });
  } else if (account?.passkeyCredIdHex) {
    return createDidPeerJwt(account.did, account.passkeyCredIdHex, payload);
  } else {
    throw new Error("No identity data found to sign for DID " + account.did);
  }
}

/**
 *  Copied out of did-jwt since it's deprecated in that library.
 *
 *  The SimpleSigner returns a configured function for signing data.
 *
 *  @example
 *  const signer = SimpleSigner(privateKeyHexString)
 *  signer(data, (err, signature) => {
 *    ...
 *  })
 *
 *  @param    {String}         hexPrivateKey    a hex encoded private key
 *  @return   {Function}                     a configured signer function
 */
function SimpleSigner(hexPrivateKey: string): didJwt.Signer {
  const signer = didJwt.ES256KSigner(didJwt.hexToBytes(hexPrivateKey), true);
  return async (data) => {
    const signature = (await signer(data)) as string;
    return fromJose(signature);
  };
}

// from did-jwt/util; see SimpleSigner above
function fromJose(signature: string): {
  r: string;
  s: string;
  recoveryParam?: number;
} {
  const signatureBytes: Uint8Array = didJwt.base64ToBytes(signature);
  if (signatureBytes.length < 64 || signatureBytes.length > 65) {
    throw new TypeError(
      `Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`,
    );
  }
  const r = bytesToHex(signatureBytes.slice(0, 32));
  const s = bytesToHex(signatureBytes.slice(32, 64));
  const recoveryParam =
    signatureBytes.length === 65 ? signatureBytes[64] : undefined;
  return { r, s, recoveryParam };
}

// from did-jwt/util; see SimpleSigner above
function bytesToHex(b: Uint8Array): string {
  return u8a.toString(b, "base16");
}

export function decodeEndorserJwt(jwt: string): JWTDecoded {
  return didJwt.decodeJWT(jwt);
}
