import fs from "node:fs";
import {Keystore} from "@chainsafe/bls-keystore";
import {CliCommand} from "@lodestar/utils";
import {YargsError, getPubkeyHexFromKeystore} from "../../util/index.js";
import {getBeaconConfigFromArgs} from "../../config/beaconParams.js";
import {GlobalArgs} from "../../options/index.js";
import {validatorOptions, IValidatorCliArgs} from "./options.js";
import {getAccountPaths} from "./paths.js";
import {importKeystoreDefinitionsFromExternalDir, readPassphraseOrPrompt} from "./signers/importExternalKeystores.js";
import {PersistedKeysBackend} from "./keymanager/persistedKeys.js";

/* eslint-disable no-console */

type ValidatorImportArgs = Pick<IValidatorCliArgs, "importKeystores" | "importKeystoresPassword">;

const {importKeystores, importKeystoresPassword} = validatorOptions;

export const importCmd: CliCommand<ValidatorImportArgs, IValidatorCliArgs & GlobalArgs> = {
  command: "import",

  describe:
    "Imports one or more EIP-2335 keystores into a Lodestar validator client directory, \
requesting passwords interactively. The directory flag provides a convenient \
method for importing a directory of keys generated by the eth2-deposit-cli \
Ethereum Foundation utility.",

  examples: [
    {
      command: "validator import --network holesky --importKeystores $HOME/staking-deposit-cli/validator_keys",
      description: "Import validator keystores generated with the Ethereum Foundation Staking Launchpad",
    },
  ],

  // Note: re-uses `--importKeystores` and `--importKeystoresPassword` from root validator command options

  options: {
    importKeystores: {
      ...importKeystores,
      requiresArg: true,
    },
    importKeystoresPassword,
  },

  handler: async (args) => {
    const {network} = getBeaconConfigFromArgs(args);

    // This command takes: importKeystores, importKeystoresPassword
    //
    // - recursively finds keystores in importKeystores
    // - validates keystores can decrypt
    // - writes them in persisted form - do not lock

    if (!args.importKeystores) {
      throw new YargsError("Must set importKeystores");
    }

    // Collect same password for all keystores
    // If importKeystoresPassword is not provided, interactive prompt for it

    const keystoreDefinitions = importKeystoreDefinitionsFromExternalDir({
      keystoresPath: args.importKeystores,
      password: await readPassphraseOrPrompt(args),
    });

    if (keystoreDefinitions.length === 0) {
      throw new YargsError("No keystores found");
    }

    console.log(
      `Importing ${keystoreDefinitions.length} keystores:\n ${keystoreDefinitions
        .map((def) => def.keystorePath)
        .join("\n")}`
    );

    const accountPaths = getAccountPaths(args, network);
    const persistedKeystoresBackend = new PersistedKeysBackend(accountPaths);
    let importedCount = 0;

    for (const {keystorePath, password} of keystoreDefinitions) {
      const keystoreStr = fs.readFileSync(keystorePath, "utf8");
      const keystore = Keystore.parse(keystoreStr);
      const pubkeyHex = getPubkeyHexFromKeystore(keystore);

      // Check if keystore can decrypt
      if (!(await keystore.verifyPassword(password))) {
        throw Error(`Invalid password for keystore ${keystorePath}`);
      }

      const didImportKey = persistedKeystoresBackend.writeKeystore({
        keystoreStr,
        password,
        // Not used immediately
        lockBeforeWrite: false,
        // Return duplicate status if already found
        persistIfDuplicate: false,
      });

      if (didImportKey) {
        console.log(`Imported keystore ${pubkeyHex} ${keystorePath}`);
        importedCount++;
      } else {
        console.log(`Duplicate keystore ${pubkeyHex} ${keystorePath}`);
      }
    }

    console.log(`\nSuccessfully imported ${importedCount}/${keystoreDefinitions.length} keystores`);

    console.log(`
  DO NOT USE THE ORIGINAL KEYSTORES TO VALIDATE WITH
  ANOTHER CLIENT, OR YOU WILL GET SLASHED.
  `);
  },
};
