import { browser } from "$app/environment";
import { generateMnemonic, validateMnemonic } from "@scure/bip39";
import { wordlist } from "@scure/bip39/wordlists/english";
import {
  decrypt as nip49decrypt,
  encrypt as nip49encrypt,
} from "nostr-tools/nip49";

const SEED_KEY = "seed";
let cachedMnemonic = $state<{ mnemonic: string | undefined }>({
  mnemonic: undefined,
});

export const passwordRequest = $state({
  pending: false,
  resolve: undefined as ((password: string) => void) | undefined,
  reject: undefined as ((error: Error) => void) | undefined,
});

let previouslyInputPassword = $state<{ pass: string | undefined }>({
  pass: undefined,
});

function requestPassword(): Promise<string> {
  if (!browser) throw new Error("Password input only available in browser");
  if (previouslyInputPassword.pass) {
    return Promise.resolve(previouslyInputPassword.pass);
  }

  return new Promise((resolve, reject) => {
    passwordRequest.pending = true;
    passwordRequest.resolve = (pw) => {
      previouslyInputPassword.pass = pw;
      resolve(pw);
    };
    passwordRequest.reject = reject;
  });
}

export function createMnemonic(): string {
  return generateMnemonic(wordlist, 128);
}

export function isValidMnemonic(mnemonic: string): boolean {
  return validateMnemonic(mnemonic, wordlist);
}

export function isValidWord(word: string): boolean {
  return wordlist.includes(word.toLowerCase());
}

export async function encryptMnemonic(
  mnemonic: string,
  password: string,
): Promise<string> {
  const secretKey = new TextEncoder().encode(mnemonic);
  return nip49encrypt(secretKey, password);
}

export async function decryptMnemonic(
  data: string,
  password: string,
): Promise<string> {
  const decryptedBytes = nip49decrypt(data, password);
  return new TextDecoder().decode(decryptedBytes);
}

export function storeEncryptedSeed(encrypted: string) {
  if (browser) localStorage.setItem(SEED_KEY, encrypted);
}

export function hasSeed(): boolean {
  if (!browser) return false;
  return localStorage.getItem(SEED_KEY) !== null;
}

export async function getMnemonic(): Promise<string> {
  if (cachedMnemonic.mnemonic) return cachedMnemonic.mnemonic;
  if (!browser) throw new Error("Not in browser");
  const enc = localStorage.getItem(SEED_KEY);
  if (!enc) throw new Error("Seed not initialised");
  const password = await requestPassword();
  const mnemonic = await decryptMnemonic(enc, password);
  cachedMnemonic.mnemonic = mnemonic;
  return mnemonic;
}

export function cacheMnemonic(mnemonic: string) {
  cachedMnemonic.mnemonic = mnemonic;
}
