{-# OPTIONS --safe #-}
--------------------------------------------------------------------------------
-- NOTE: Everything in this module is part of TransactionStructure
--------------------------------------------------------------------------------
module Ledger.Conway.Transaction where

import Data.Maybe.Base as M

open import Ledger.Prelude renaming (filterᵐ to filter)

open import Ledger.Conway.Crypto
open import Ledger.Conway.Types.Epoch
open import Ledger.Conway.Types.GovStructure
import Ledger.Conway.PParams
import Ledger.Conway.Script
import Ledger.Conway.GovernanceActions
import Ledger.Conway.Certs
import Ledger.Conway.TokenAlgebra
import Ledger.Conway.Address

open import Tactic.Derive.DecEq
open import Relation.Nullary.Decidable using (⌊_⌋)

data Tag : Type where
  Spend Mint Cert Rewrd Vote Propose : Tag
unquoteDecl DecEq-Tag = derive-DecEq ((quote Tag , DecEq-Tag)  [])



record TransactionStructure : Type₁ where
  field


    Ix TxId AuxiliaryData : Type



     DecEq-Ix    : DecEq Ix
     DecEq-TxId  : DecEq TxId
    adHashingScheme : isHashableSet AuxiliaryData
  open isHashableSet adHashingScheme renaming (THash to ADHash) public

  field globalConstants : _
  open GlobalConstants globalConstants public

  field crypto : _
  open Crypto crypto public
  open Ledger.Conway.TokenAlgebra ScriptHash public
  open Ledger.Conway.Address Network KeyHash ScriptHash  it   it   it  public

  field epochStructure : _
  open EpochStructure epochStructure public
  open Ledger.Conway.Script crypto epochStructure public

  field scriptStructure : _
  open ScriptStructure scriptStructure public
  open Ledger.Conway.PParams crypto epochStructure scriptStructure public

  field govParams : _
  open GovParams govParams public

  field tokenAlgebra : TokenAlgebra
  open TokenAlgebra tokenAlgebra public

  field txidBytes : TxId  Ser

  govStructure : GovStructure
  govStructure = record
    -- TODO: figure out what to do with the hash
    { TxId = TxId; DocHash = ADHash
    ; crypto = crypto
    ; epochStructure = epochStructure
    ; scriptStructure = scriptStructure
    ; govParams = govParams
    ; globalConstants = globalConstants
    }

  module GovernanceActions = Ledger.Conway.GovernanceActions govStructure
  open GovernanceActions hiding (Vote; yes; no; abstain) public

  open import Ledger.Conway.Certs govStructure


  TxIn     = TxId × Ix
  TxOut    = Addr × Value × Maybe (Datum  DataHash) × Maybe Script
  UTxO     = TxIn  TxOut
  Wdrl     = RwdAddr  Coin
  RdmrPtr  = Tag × Ix

  ProposedPPUpdates  = KeyHash  PParamsUpdate
  Update             = ProposedPPUpdates × Epoch


  record HasUTxO {a} (A : Type a) : Type a where
    field UTxOOf : A  UTxO
  open HasUTxO ⦃...⦄ public


  record TxBody : Type where
    field
      txins          :  TxIn
      refInputs      :  TxIn
      txouts         : Ix  TxOut
      txfee          : Coin
      mint           : Value
      txvldt         : Maybe Slot × Maybe Slot
      txcerts        : List DCert
      txwdrls        : Wdrl
      txvote         : List GovVote
      txprop         : List GovProposal
      txdonation     : Coin
      txup           : Maybe Update
      txADhash       : Maybe ADHash
      txNetworkId    : Maybe Network
      curTreasury    : Maybe Coin
      txsize         : 
      txid           : TxId
      collateral     :  TxIn
      reqSigHash     :  KeyHash
      scriptIntHash  : Maybe ScriptHash


  record HasTxBody {a} (A : Type a) : Type a where
    field TxBodyOf : A  TxBody
  open HasTxBody  ⦃...⦄ public

  record Hastxfee {a} (A : Type a) : Type a where
    field txfeeOf : A  Coin
  open Hastxfee   ⦃...⦄ public

  record Hastxcerts {a} (A : Type a) : Type a where
    field txcertsOf : A  List DCert
  open Hastxcerts ⦃...⦄ public

  record Hastxprop {a} (A : Type a) : Type a where
    field txpropOf  : A  List GovProposal
  open Hastxprop  ⦃...⦄ public

  record Hastxid    {a} (A : Type a) : Type a where 
    field txidOf    : A  TxId
  open Hastxid    ⦃...⦄ public


  record TxWitnesses : Type where
    field
      vkSigs   : VKey  Sig
      scripts  :  Script
      txdats   : DataHash  Datum
      txrdmrs  : RdmrPtr   Redeemer × ExUnits

    scriptsP1 :  P1Script
    scriptsP1 = mapPartial isInj₁ scripts

  record Tx : Type where
    field
      body     : TxBody
      wits     : TxWitnesses
      isValid  : Bool
      txAD     : Maybe AuxiliaryData


  instance
    HasTxBody-Tx : HasTxBody Tx
    HasTxBody-Tx .TxBodyOf = Tx.body

    Hastxfee-Tx : Hastxfee Tx
    Hastxfee-Tx .txfeeOf = TxBody.txfee  TxBodyOf
    
    Hastxcerts-Tx : Hastxcerts Tx
    Hastxcerts-Tx .txcertsOf = TxBody.txcerts  TxBodyOf

    Hastxprop-Tx : Hastxprop Tx
    Hastxprop-Tx .txpropOf = TxBody.txprop  TxBodyOf

    HasWdrls-TxBody : HasWdrls TxBody
    HasWdrls-TxBody .wdrlsOf = TxBody.txwdrls

    HasWdrls-Tx : HasWdrls Tx
    HasWdrls-Tx .wdrlsOf = wdrlsOf  TxBodyOf

    Hastxid-Tx : Hastxid Tx
    Hastxid-Tx .txidOf = TxBody.txid  TxBodyOf


  getValue : TxOut  Value
  getValue (_ , v , _) = v

  TxOutʰ = Addr × Value × Maybe (Datum  DataHash) × Maybe ScriptHash

  txOutHash : TxOut  TxOutʰ
  txOutHash (a , v , d , s) = a , (v , (d , M.map hash s))

  getValueʰ : TxOutʰ  Value
  getValueʰ (_ , v , _) = v

  txinsVKey :  TxIn  UTxO   TxIn
  txinsVKey txins utxo = txins  dom (utxo ∣^' (isVKeyAddr  proj₁))

  scriptOuts : UTxO  UTxO
  scriptOuts utxo = filter  (_ , addr , _)  isScriptAddr addr) utxo

  txinsScript :  TxIn  UTxO   TxIn
  txinsScript txins utxo = txins  dom (proj₁ (scriptOuts utxo))

  refScripts : Tx  UTxO  List Script
  refScripts tx utxo =
    mapMaybe (proj₂  proj₂  proj₂) $ setToList (range (utxo  (txins  refInputs)))
    where open Tx; open TxBody (tx .body)

  txscripts : Tx  UTxO   Script
  txscripts tx utxo = scripts (tx .wits)  fromList (refScripts tx utxo)
    where open Tx; open TxWitnesses

  lookupScriptHash : ScriptHash  Tx  UTxO  Maybe Script
  lookupScriptHash sh tx utxo =
    if sh  mapˢ proj₁ (m ˢ) then
      just (lookupᵐ m sh)
    else
      nothing
    where m = setToMap (mapˢ < hash , id > (txscripts tx utxo))


  isP2Script : Script  Type
  isP2Script = T  is-just  isInj₂

  isP2Script? :  {s}  isP2Script s 
  isP2Script? {inj₁ x} .dec = no λ ()
  isP2Script? {inj₂ y} .dec = yes tt

  instance
    HasCoin-TxOut : HasCoin TxOut
    HasCoin-TxOut .getCoin = coin  proj₁  proj₂