content: {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE LambdaCase #-}
  {-# LANGUAGE NamedFieldPuns #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  {-# OPTIONS_HADDOCK hide #-}
  
  -- |
  -- Copyright: 2020 Input Output (Hong Kong) Ltd., 2021-2022 Input Output Global Inc. (IOG), 2023-2025 Intersect
  -- License: Apache-2.0
  
  module Command.Key.FromRecoveryPhrase
      ( Cmd (..)
      , mod
      , run
      ) where
  
  import Prelude hiding
      ( mod )
  
  import Cardano.Address.Derivation
      ( xprvToBytes )
  import Codec.Binary.Bech32
      ( HumanReadablePart )
  import Codec.Binary.Encoding
      ( AbstractEncoding (..) )
  import Data.Text
      ( Text )
  import Options.Applicative
      ( CommandFields
      , Mod
      , command
      , footerDoc
      , helper
      , info
      , optional
      , progDesc
      )
  import Options.Applicative.Help.Pretty
      ( Doc, annotate, bold, indent, pretty, vsep )
  import Options.Applicative.Style
      ( Passphrase (..)
      , PassphraseInfo (..)
      , PassphraseInput (..)
      , PassphraseInputMode
      , Style (..)
      , fileOpt
      , generateRootKey
      , passphraseInfoOpt
      , passphraseInputModeOpt
      , styleArg
      )
  import System.IO
      ( stderr, stdin, stdout )
  import System.IO.Extra
      ( hGetPassphraseBytes
      , hGetPassphraseMnemonic
      , hGetSomeMnemonic
      , hGetSomeMnemonicInteractively
      , hPutBytes
      , progName
      )
  
  import qualified Cardano.Codec.Bech32.Prefixes as CIP5
  
  
  data Cmd = FromRecoveryPhrase
      { style :: Style
      , passphraseInfo :: Maybe PassphraseInfo
      , passphraseInputMode :: PassphraseInputMode
      , passphraseFromFile :: Maybe FilePath
      } deriving (Show)
  
  mod :: (Cmd -> parent) -> Mod CommandFields parent
  mod liftCmd = command "from-recovery-phrase" $
      info (helper <*> fmap liftCmd parser) $ mempty
          <> progDesc "Convert a recovery phrase to an extended private key"
          <> footerDoc (Just $ vsep
              [ prettyText "The recovery phrase without passphrase is read from stdin."
              , prettyText ""
              , prettyText "Example:"
              , indent 2 $ annotate bold $ pretty $ "$ "<>progName<>" recovery-phrase generate \\"
              , indent 2 $ annotate bold $ pretty $ "| "<>progName<>" key from-recovery-phrase Icarus"
              , prettyText ""
              , prettyText "The recovery phrase with passphrase can be entered interactively or from file."
              , prettyText "In both cases passhrase can take form of mnemonic, base16, base64, utf8 or octet array."
              , prettyText "In interactive case one can select explicit, sensitive or silent mode."
              , prettyText ""
              , prettyText "Example:"
              , indent 2 $ annotate bold $ pretty $ "$ "<>progName<>" key from-recovery-phrase Shelley --passphrase from-mnemonic --sensitive"
              , indent 2 $ annotate bold $ prettyText "Please enter a [9, 12, 15, 18, 21, 24] word mnemonic:"
              , indent 2 $ annotate bold $ prettyText "**********************************************************************************************************"
              , indent 2 $ annotate bold $ prettyText "Please enter a 9–12 word second factor:"
              , indent 2 $ annotate bold $ prettyText "*************************************************************"
              , prettyText ""
              , prettyText "In case of passphrase reading from file the recovery phrase is read from stdin."
              , prettyText ""
              , prettyText "Example:"
              , indent 2 $ annotate bold $ prettyText "$ echo \"Secret Secondary Phrase\" > sndfactor.prv"
              , indent 2 $ annotate bold $ pretty $ "$ "<>progName<>" recovery-phrase generate \\"
              , indent 2 $ annotate bold $ pretty $ "| "<>progName<>" key from-recovery-phrase Shelley --from-file \"./sndfactor.prv\""
              , prettyText ""
              , indent 2 $ annotate bold $ pretty $ "$ "<>progName<>" recovery-phrase generate --size 12 > sndfactor.prv"
              , indent 2 $ annotate bold $ pretty $ "$ "<>progName<>" recovery-phrase generate \\"
              , indent 2 $ annotate bold $ pretty $ "| "<>progName<>" key from-recovery-phrase Shelley --passphrase from-mnemonic --from-file \"./sndfactor.prv\""
              ])
    where
      parser = FromRecoveryPhrase
          <$> styleArg
          <*> optional passphraseInfoOpt
          <*> passphraseInputModeOpt
          <*> optional fileOpt
  
      prettyText :: Text -> Doc
      prettyText = pretty
  
  run :: Cmd -> IO ()
  run FromRecoveryPhrase{style,passphraseInfo, passphraseInputMode,passphraseFromFile} = do
      (someMnemonic, passphrase) <- case passphraseInfo of
          Nothing -> do
              mnemonic <- hGetSomeMnemonic stdin
              pure (mnemonic, Nothing)
          Just pinfo -> do
              mnemonic <- case passphraseFromFile of
                  Nothing -> do
                      let prompt = "Please enter a [9, 12, 15, 18, 21, 24] word mnemonic:"
                      hGetSomeMnemonicInteractively (stdin, stderr)
                          passphraseInputMode prompt
                  Just _ ->
                      hGetSomeMnemonic stdin
              let passphraseSrc =
                      maybe Interactive FromFile passphraseFromFile
              passwd <- handlePassphraseInfo passphraseSrc pinfo
              pure (mnemonic, Just passwd)
      rootK <- generateRootKey someMnemonic passphrase style
      hPutBytes stdout (xprvToBytes rootK) (EBech32 $ styleHrp style)
    where
      handlePassphraseInfo passphraseSrc = \case
          Mnemonic -> do
              let prompt = "Please enter a 9–12 word second factor:"
              p <- hGetPassphraseMnemonic (stdin, stderr)
                   passphraseInputMode passphraseSrc prompt
              pure $ FromMnemonic p
          Hex -> do
              let prompt = "Please enter hex-encoded passphrase:"
              p <- hGetPassphraseBytes (stdin, stderr)
                   passphraseInputMode passphraseSrc prompt Hex
              pure $ FromEncoded p
          Base64 -> do
              let prompt = "Please enter base64-encoded passphrase:"
              p <- hGetPassphraseBytes (stdin, stderr)
                   passphraseInputMode passphraseSrc prompt Base64
              pure $ FromEncoded p
          Utf8 -> do
              let prompt = "Please enter utf8-encoded passphrase:"
              p <- hGetPassphraseBytes (stdin, stderr)
                   passphraseInputMode passphraseSrc prompt Utf8
              pure $ FromEncoded p
          Octets -> do
              let prompt = "Please enter passphrase in the form of octet array:"
              p <- hGetPassphraseBytes (stdin, stderr)
                   passphraseInputMode passphraseSrc prompt Octets
              pure $ FromEncoded p
  
  styleHrp :: Style -> HumanReadablePart
  styleHrp Shared = CIP5.root_shared_xsk
  styleHrp _ = CIP5.root_xsk
  