import * as Schema from 'effect/Schema';
import { privateKeyToAccount } from 'viem/accounts';
import { proveIdentityOwnership } from '../identity/prove-ownership.js';
import * as Messages from '../messages/index.js';
import { store } from '../store-connect.js';
import { loadAccountAddress, storeAccountAddress, storeKeys } from './auth-storage.js';
import { createIdentityKeys } from './create-identity-keys.js';
import { decryptIdentity, encryptIdentity } from './identity-encryption.js';
import { addSmartAccountOwner, getSmartAccountWalletClient, isSmartAccountDeployed, smartAccountNeedsUpdate, updateLegacySmartAccount } from './smart-account.js';
export async function identityExists(accountAddress, syncServerUri) {
  const res = await fetch(new URL(`/connect/identity?accountAddress=${accountAddress}`, syncServerUri), {
    method: 'GET'
  });
  return res.status === 200;
}
export async function signup(signer, _walletClient, smartAccountClient, accountAddress, syncServerUri, addressStorage, keysStorage, identityToken, chain, rpcUrl) {
  const keys = createIdentityKeys();
  const {
    ciphertext,
    nonce
  } = await encryptIdentity(signer, keys);
  const localAccount = privateKeyToAccount(keys.signaturePrivateKey);
  // This will deploy the smart account if it's not deployed
  await addSmartAccountOwner(smartAccountClient, localAccount.address, chain, rpcUrl);
  const localSmartAccountClient = await getSmartAccountWalletClient({
    owner: localAccount,
    address: accountAddress,
    rpcUrl,
    chain
  });
  const {
    accountProof,
    keyProof
  } = await proveIdentityOwnership(localSmartAccountClient, accountAddress, keys);
  const req = {
    keyBox: {
      signer: await signer.getAddress(),
      accountAddress,
      ciphertext,
      nonce
    },
    accountProof,
    keyProof,
    signaturePublicKey: keys.signaturePublicKey,
    encryptionPublicKey: keys.encryptionPublicKey
  };
  const res = await fetch(new URL('/connect/identity', syncServerUri), {
    method: 'POST',
    headers: {
      'privy-id-token': identityToken,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(req)
  });
  if (res.status !== 200) {
    // TODO: handle this better?
    throw new Error(`Error creating identity: ${res.status}`);
  }
  const decoded = Schema.decodeUnknownSync(Messages.ResponseConnectCreateIdentity)(await res.json());
  if (!decoded.success) {
    throw new Error('Error creating identity');
  }
  storeKeys(keysStorage, accountAddress, keys);
  storeAccountAddress(addressStorage, accountAddress);
  return {
    accountAddress,
    keys
  };
}
export async function restoreKeys(signer, accountAddress, syncServerUri, addressStorage, keysStorage, identityToken) {
  const res = await fetch(new URL('/connect/identity/encrypted', syncServerUri), {
    method: 'GET',
    headers: {
      'privy-id-token': identityToken,
      'account-address': accountAddress,
      'Content-Type': 'application/json'
    }
  });
  if (res.status === 200) {
    const decoded = Schema.decodeUnknownSync(Messages.ResponseIdentityEncrypted)(await res.json());
    const {
      keyBox
    } = decoded;
    const {
      ciphertext,
      nonce
    } = keyBox;
    const keys = await decryptIdentity(signer, ciphertext, nonce);
    storeKeys(keysStorage, accountAddress, keys);
    storeAccountAddress(addressStorage, accountAddress);
    return {
      accountAddress,
      keys
    };
  }
  throw new Error(`Error fetching identity ${res.status}`);
}
const getAndUpdateSmartAccount = async (walletClient, rpcUrl, chain, addressStorage) => {
  const accountAddressFromStorage = loadAccountAddress(addressStorage);
  const smartAccountParams = {
    owner: walletClient,
    rpcUrl,
    chain
  };
  if (accountAddressFromStorage) {
    smartAccountParams.address = accountAddressFromStorage;
  }
  const smartAccountWalletClient = await getSmartAccountWalletClient(smartAccountParams);
  if (!smartAccountWalletClient.account) {
    throw new Error('Smart account wallet client not found');
  }
  console.log('smartAccountWalletClient', smartAccountWalletClient);
  console.log('address', smartAccountWalletClient.account.address);
  console.log('is deployed', await isSmartAccountDeployed(smartAccountWalletClient));
  // This will prompt the user to sign a user operation to update the smart account
  if (await smartAccountNeedsUpdate(smartAccountWalletClient, chain, rpcUrl)) {
    console.log('updating smart account');
    await updateLegacySmartAccount(smartAccountWalletClient, chain, rpcUrl);
    smartAccountParams.address = smartAccountWalletClient.account.address;
    // Create the client again to ensure we have the 7579 config now
    return getSmartAccountWalletClient(smartAccountParams);
  }
  console.log('leaving getAndUpdateSmartAccount');
  return smartAccountWalletClient;
};
export async function login({
  walletClient,
  signer,
  syncServerUri,
  addressStorage,
  keysStorage,
  identityToken,
  rpcUrl,
  chain
}) {
  const smartAccountWalletClient = await getAndUpdateSmartAccount(walletClient, rpcUrl, chain, addressStorage);
  if (!smartAccountWalletClient.account) {
    throw new Error('Smart account wallet client account not found');
  }
  const accountAddress = smartAccountWalletClient.account.address;
  let authData;
  const exists = await identityExists(accountAddress, syncServerUri);
  if (!exists) {
    authData = await signup(signer, walletClient, smartAccountWalletClient, accountAddress, syncServerUri, addressStorage, keysStorage, identityToken, chain, rpcUrl);
  } else {
    authData = await restoreKeys(signer, accountAddress, syncServerUri, addressStorage, keysStorage, identityToken);
  }
  store.send({
    type: 'reset'
  });
  store.send({
    ...authData,
    sessionToken: 'dummy',
    // not needed in the connect app
    type: 'setAuth'
  });
}
//# sourceMappingURL=login.js.map