"use client";

import type { RecadoDaCaneta } from "../anotacoes";

import type { ApiDaSala } from "./api";
import type { AvisoAssinado, RiscoAssinado } from "./canal";

/*
 * A caneta de quem apresenta. O navegador cria um par de chaves só seu (a
 * privada nunca sai daqui) e pede ao servidor a licença: "esta chave pode
 * riscar nesta sala". Cada recado sai assinado; quem assiste confere a
 * licença e o recado antes de desenhar.
 */

function paraBase64(bytes: ArrayBuffer): string {
  let texto = "";
  const lista = new Uint8Array(bytes);
  for (let i = 0; i < lista.length; i += 1) texto += String.fromCharCode(lista[i]);
  return btoa(texto);
}

export class CanetaDaSala {
  private readonly api: ApiDaSala;
  private readonly diferencaDoRelogio: number;
  private pronta: Promise<{ privada: CryptoKey; licenca: AvisoAssinado }> | null = null;

  constructor(api: ApiDaSala, diferencaDoRelogio: number) {
    this.api = api;
    this.diferencaDoRelogio = diferencaDoRelogio;
  }

  private preparar() {
    if (!this.pronta) {
      this.pronta = (async () => {
        const par = (await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign", "verify"])) as CryptoKeyPair;
        const publica = (await crypto.subtle.exportKey("jwk", par.publicKey)) as JsonWebKey;
        const r = await this.api.pedir<{ licenca: AvisoAssinado }>("caneta", { chave: { x: publica.x, y: publica.y } });
        return { privada: par.privateKey, licenca: r.licenca };
      })();
      this.pronta.catch(() => {
        this.pronta = null;
      });
    }
    return this.pronta;
  }

  /* Liga antes de precisar (a primeira risca não espera a licença). */
  aquecer() {
    void this.preparar().catch(() => undefined);
  }

  async assinar(recado: RecadoDaCaneta): Promise<RiscoAssinado> {
    const { privada, licenca } = await this.preparar();
    const d = JSON.stringify({ ts: Date.now() + this.diferencaDoRelogio, r: recado });
    const s = paraBase64(await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, privada, new TextEncoder().encode(d)));
    return { l: licenca, d, s };
  }
}
