content: {-# LANGUAGE DisambiguateRecordFields #-}
  {-# LANGUAGE NamedFieldPuns           #-}
  {-# LANGUAGE OverloadedStrings        #-}
  {-# LANGUAGE ScopedTypeVariables      #-}
  {-# LANGUAGE TypeApplications         #-}
  
  module Test.PlutusExample.Direct.ScriptContextEquality
    ( hprop_plutus_script_context_equality
    ) where
  
  import Control.Applicative
  import Control.Monad
  import Data.Aeson (FromJSON (..), Value, (.:))
  import Data.Eq
  import Data.Function
  import Data.Functor ((<&>))
  import Data.HashMap.Lazy (HashMap)
  import Data.Int
  import Data.Maybe
  import Data.Monoid (Last (..), (<>))
  import Data.Text (Text)
  import GHC.Num
  import GHC.Real
  import Hedgehog (Property, (/==))
  import Prelude (head)
  import System.Environment (getEnvironment)
  import System.FilePath ((</>))
  import Text.Show (Show (..))
  
  import Data.Aeson qualified as J
  import Data.HashMap.Lazy qualified as HM
  import Data.Text qualified as T
  import Hedgehog qualified as H
  import Hedgehog.Extras.Stock.IO.Network.Sprocket qualified as IO
  import Hedgehog.Extras.Test.Base qualified as H
  import Hedgehog.Extras.Test.Concurrent qualified as H
  import Hedgehog.Extras.Test.File qualified as H
  import Hedgehog.Extras.Test.Process qualified as H
  import System.Directory qualified as IO
  import Test.Base qualified as H
  import Test.Process qualified as H
  import Test.Runtime qualified as H
  import Testnet.Cardano (TestnetRuntime (..), defaultTestnetOptions, testnet)
  import Testnet.Conf qualified as H
  
  {- HLINT ignore "Redundant <&>" -}
  {- HLINT ignore "Redundant return" -}
  {- HLINT ignore "Use let" -}
  
  data Utxo = Utxo
    { address :: Text
    , value   :: HashMap Text Integer
    } deriving (Eq, Show)
  
  instance FromJSON Utxo where
    parseJSON = J.withObject "Utxo" $ \v -> Utxo
      <$> v .: "address"
      <*> v .: "value"
  
  hprop_plutus_script_context_equality :: Property
  hprop_plutus_script_context_equality = H.integration . H.runFinallies . H.workspace "chairman" $ \tempAbsBasePath' -> do
    base <- H.note =<< H.noteIO . IO.canonicalizePath =<< H.getProjectBase
    configurationTemplate <- H.noteShow $ base </> "configuration/defaults/byron-mainnet/configuration.yaml"
    conf@H.Conf { H.tempBaseAbsPath, H.tempAbsPath } <- H.noteShowM $
      H.mkConf (H.ProjectBase base) (H.YamlFilePath configurationTemplate) tempAbsBasePath' Nothing
  
    tr@Testnet.Cardano.TestnetRuntime { testnetMagic } <- testnet defaultTestnetOptions conf
  
    env <- H.evalIO getEnvironment
  
    execConfig <- H.noteShow H.ExecConfig
          { H.execConfigEnv = Last $ Just $
            [ ("CARDANO_NODE_SOCKET_PATH", IO.sprocketArgumentName $ head $ H.bftSprockets tr)
            ]
            -- The environment must be passed onto child process on Windows in order to
            -- successfully start that process.
            <> env
          , H.execConfigCwd = Last $ Just tempBaseAbsPath
          }
  
    -- First we note all the relevant files
    H.note_ base
    work <- H.note tempAbsPath
  
    -- We get our UTxOs from here
    utxoVKeyFile <- H.note $ tempAbsPath </> "shelley/utxo-keys/utxo1.vkey"
    utxoSKeyFile <- H.note $ tempAbsPath </> "shelley/utxo-keys/utxo1.skey"
    scriptDummyRedeemer <- H.note $ work </> "script-context-dummy.redeemer"
    scriptContextRedeemer <- H.note $ work </> "script-context.redeemer"
  
    plutusContextEqualityScript <- H.note $ base </> "plutus-example/plutus/scripts/context-equivalance-test.plutus"
  
    exampleStakeVKey <- H.note $ tempAbsPath </> "addresses/user1-stake.vkey"
    exampleCertificate <- H.note $ tempAbsPath </> "addresses/user1-stake.reg.cert"
  
    void $ H.execCli
             [ "stake-address", "registration-certificate"
             , "--stake-verification-key-file", exampleStakeVKey
             , "--out-file", exampleCertificate
             ]
  
    -- This datum hash is the hash of the typed 42. TODO: Have the cli create this
    let scriptDatumHash = "fcaa61fb85676101d9e3398a484674e71c45c3fd41b492682f3b0054f4cf3273"
  
    typedDatumFile <- H.note $ base </> "plutus-example/plutus/data/typed-42.datum"
  
  
    -- Step 1: Create a tx ouput with a datum hash at the script address. In order for a tx ouput to be locked
    -- by a plutus script, it must have a datahash. We also need collateral tx inputs so we split the utxo
    -- in order to accomodate this.
  
    plutusScriptAddr <- H.execCli
      [ "address", "build"
      , "--payment-script-file", plutusContextEqualityScript
      , "--testnet-magic", show @Int testnetMagic
      ]
  
    utxoAddr <- H.execCli
      [ "address", "build"
      , "--testnet-magic", show @Int testnetMagic
      , "--payment-verification-key-file", utxoVKeyFile
      ]
  
    void $ H.execCli' execConfig
      [ "query", "utxo"
      , "--address", utxoAddr
      , "--cardano-mode"
      , "--testnet-magic", show @Int testnetMagic
      , "--out-file", work </> "utxo-1.json"
      ]
  
    H.cat $ work </> "utxo-1.json"
  
    utxo1Json <- H.leftFailM . H.readJsonFile $ work </> "utxo-1.json"
    utxo1 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(HashMap Text Utxo) utxo1Json
    txin <- H.noteShow $ head $ HM.keys utxo1
    lovelaceAtTxin <- H.nothingFailM . H.noteShow $ utxo1 & HM.lookup txin <&> value >>= HM.lookup "lovelace"
    lovelaceAtTxinDiv3 <- H.noteShow $ lovelaceAtTxin `div` 3
  
    void $ H.execCli' execConfig
      [ "query", "protocol-parameters"
      , "--testnet-magic", show @Int testnetMagic
      , "--out-file", work </> "pparams.json"
      ]
  
    let dummyaddress = "addr_test1vpqgspvmh6m2m5pwangvdg499srfzre2dd96qq57nlnw6yctpasy4"
  
  
    -- STEP 1 - Create collateral and a UTxO at the script address
    void $ H.execCli' execConfig
      [ "transaction", "build"
      , "--alonzo-era"
      , "--cardano-mode"
      , "--testnet-magic", show @Int testnetMagic
      , "--change-address", utxoAddr
      , "--tx-in", T.unpack txin
      , "--tx-out", plutusScriptAddr <> "+" <> show @Integer lovelaceAtTxinDiv3
      , "--tx-out-datum-hash", scriptDatumHash
      , "--tx-out", utxoAddr <> "+" <> show @Integer lovelaceAtTxinDiv3
      , "--protocol-params-file", work </> "pparams.json"
      , "--out-file", work </> "create-datum-output.body"
      ]
  
    void $ H.execCli
      [ "transaction", "sign"
      , "--tx-body-file", work </> "create-datum-output.body"
      , "--testnet-magic", show @Int testnetMagic
      , "--signing-key-file", utxoSKeyFile
      , "--out-file", work </> "create-datum-output.tx"
      ]
  
    void $ H.execCli' execConfig
      [ "transaction", "submit"
      , "--tx-file", work </> "create-datum-output.tx"
      , "--testnet-magic", show @Int testnetMagic
      ]
  
    H.threadDelay 5000000
  
    -- STEP 2
    -- We need to create a dummy tx in order to create the script context redeemer
    -- that we want to use when attempting to spend the spending script locked UTxO
  
    void $ H.execCli' execConfig
      [ "query", "utxo"
      , "--address", utxoAddr
      , "--cardano-mode"
      , "--testnet-magic", show @Int testnetMagic
      , "--out-file", work </> "utxo-2.json"
      ]
  
    H.cat $ work </> "utxo-2.json"
  
    utxo2Json :: Value <- H.leftFailM $ H.readJsonFile $ work </> "utxo-2.json"
    utxo2 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(HashMap Text Utxo) utxo2Json
    txinCollateral <- H.noteShow $ head $ HM.keys utxo2
  
  
    void $ H.execCli' execConfig
      [ "query", "utxo"
      , "--address", plutusScriptAddr
      , "--testnet-magic", show @Int testnetMagic
      , "--out-file", work </> "plutusutxo.json"
      ]
  
    H.cat $ work </> "plutusutxo.json"
  
    plutusUtxoJson <- H.leftFailM . H.readJsonFile $ work </> "plutusutxo.json"
    plutusUtxo <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(HashMap Text Utxo) plutusUtxoJson
    plutusUtxoTxIn <- H.noteShow $ head $ HM.keys plutusUtxo
  
    void $ H.execCreateScriptContext ["--out-file", scriptDummyRedeemer]
  
    void $ H.execCli' execConfig
      [ "transaction", "build"
      , "--alonzo-era"
      , "--cardano-mode"
      , "--script-invalid"
      , "--testnet-magic", show @Int testnetMagic
      , "--change-address", utxoAddr
      , "--certificate-file", exampleCertificate
      , "--invalid-before", "1"
      , "--invalid-hereafter", "3000"
      , "--tx-in", T.unpack plutusUtxoTxIn
      , "--tx-in-collateral", T.unpack txinCollateral
      , "--tx-out", dummyaddress <> "+" <> show @Integer 10000000
      , "--tx-in-script-file", plutusContextEqualityScript
      , "--tx-in-datum-file", typedDatumFile
      , "--tx-in-redeemer-file", scriptDummyRedeemer
      , "--protocol-params-file", work </> "pparams.json"
      , "--out-file", work </> "dummy.body"
      ]
  
    void $ H.execCli
      [ "transaction", "sign"
      , "--tx-body-file", work </> "dummy.body"
      , "--testnet-magic", show @Int testnetMagic
      , "--signing-key-file", utxoSKeyFile
      , "--out-file", work </> "dummy.tx"
      ]
  
    -- Generate the redeeemer we will use in the tx!
    void $ H.execCreateScriptContext' execConfig
             [ "--generate-tx" , work </> "dummy.tx"
             , "--cardano-mode"
             , "--testnet-magic", show @Int testnetMagic
             , "--out-file", scriptContextRedeemer
             ]
  
    H.threadDelay 5000000
  
    void $ H.execCli' execConfig
      [ "transaction", "build"
      , "--alonzo-era"
      , "--cardano-mode"
      , "--script-valid"
      , "--testnet-magic", show @Int testnetMagic
      , "--change-address", utxoAddr
      , "--certificate-file", exampleCertificate
      , "--invalid-before", "1"
      , "--invalid-hereafter", "3000"
      , "--tx-in", T.unpack plutusUtxoTxIn
      , "--tx-in-collateral", T.unpack txinCollateral
      , "--tx-out", dummyaddress <> "+" <> show @Integer 10000000
      , "--tx-in-script-file", plutusContextEqualityScript
      , "--tx-in-datum-file", typedDatumFile
      , "--tx-in-redeemer-file", scriptContextRedeemer
      , "--protocol-params-file", work </> "pparams.json"
      , "--out-file", work </> "final.body"
      ]
  
    void $ H.execCli
      [ "transaction", "sign"
      , "--tx-body-file", work </> "final.body"
      , "--testnet-magic", show @Int testnetMagic
      , "--signing-key-file", utxoSKeyFile
      , "--out-file", work </> "final.tx"
      ]
  
  
    void $ H.execCli' execConfig
      [ "transaction", "submit"
      , "--tx-file", work </> "final.tx"
      , "--testnet-magic", show @Int testnetMagic
      ]
  
    H.threadDelay 5000000
  
    -- Query UTxO at dummyAddress. If there is ADA there then the script context script was successful
  
    void $ H.execCli' execConfig
      [ "query", "utxo"
      , "--address", dummyaddress
      , "--testnet-magic", show @Int testnetMagic
      , "--out-file", work </> "dummyaddress.json"
      ]
  
    H.cat $ work </> "dummyaddress.json"
  
    dummyUtxoJson <- H.leftFailM . H.readJsonFile $ work </> "dummyaddress.json"
    dummyUtxo <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(HashMap Text Utxo) dummyUtxoJson
  
    -- I.E the dummy address should have some ADA if the script context test was successful.
    dummyUtxo /== HM.empty
  
  