{-# OPTIONS --safe #-}

open import Ledger.Conway.Abstract
open import Ledger.Conway.Transaction

module Ledger.Conway.Utxo.Properties.MinSpend
  (txs : _) (open TransactionStructure txs)
  (abs : AbstractFunctions txs) (open AbstractFunctions abs)
  where


open import Ledger.Conway.Certs govStructure
open import Ledger.Conway.Chain txs abs
open import Ledger.Conway.Enact govStructure
open import Ledger.Conway.Epoch txs abs
open import Ledger.Conway.Ledger txs abs
open import Ledger.Prelude hiding (≤-trans; ≤-antisym; All)
open import Ledger.Conway.Properties txs abs using (validTxIn₂)
open import Ledger.Conway.Utxo txs abs

open import Data.List.Relation.Unary.All  using (All)
open import Data.Nat.Properties           hiding (_≟_)

isRefundCert : DCert  Bool
isRefundCert (dereg c _) = true
isRefundCert (deregdrep c _) = true
isRefundCert _ = false

noRefundCert : List DCert  Type _
noRefundCert l = All  cert  isRefundCert cert  false) l

opaque
  unfolding List-Model
  unfolding finiteness
  fin∘list[] : {A : Type}  proj₁ (finiteness{A = A} )  []
  fin∘list[] = refl
  fin∘list∷[] : {A : Type} {a : A}  proj₁ (finiteness  a )  [ a ]
  fin∘list∷[] = refl

coin∅ : getCoin{A = DepositPurpose  Coin}   0
coin∅ = begin
  foldr  x  (proj₂ x) +_) 0 (deduplicate _≟_ (proj₁ (finiteness )))
    ≡⟨ cong  u  (foldr  x  (proj₂ x) +_) 0 (deduplicate _≟_ u))) fin∘list[] 
  foldr  (x : DepositPurpose × Coin)  (proj₂ x) +_) 0 (deduplicate _≟_ [])
    ≡⟨ cong  u  (foldr  (x : DepositPurpose × Coin)  (proj₂ x) +_) 0  u))
            {x = deduplicate _≟_ []} {y = []} refl 
  foldr  (x : DepositPurpose × Coin)  (proj₂ x) +_) 0 []
    ≡⟨ refl 
  0 
  where open ≡-Reasoning

getCoin-singleton : ((dp , c) : DepositPurpose × Coin)  indexedSumᵛ' id  (dp , c)   c
getCoin-singleton _ = indexedSum-singleton' {A = DepositPurpose × Coin} {f = proj₂} (finiteness _)

module _ -- ASSUMPTION --
         (gc-hom : (d₁ d₂ : DepositPurpose  Coin)  getCoin (d₁ ∪⁺ d₂)  getCoin d₁ + getCoin d₂)
  where

  ∪⁺singleton≡ : {deps : DepositPurpose  Coin} {(dp , c) : DepositPurpose × Coin}
                  getCoin (deps ∪⁺  (dp , c) ❵ᵐ)  getCoin deps + c
  ∪⁺singleton≡ {deps} {(dp , c)} = begin
    getCoin (deps ∪⁺  (dp , c) )
      ≡⟨ gc-hom deps  (dp , c)  
    getCoin deps + getCoin{A = DepositPurpose  Coin}  (dp , c) 
      ≡⟨ cong (getCoin deps +_) (getCoin-singleton (dp , c)) 
    getCoin deps + c
      
    where open ≡-Reasoning

  module _ {deposits : DepositPurpose  Coin} {txid : TxId} {gaDep : Coin} where

    ≤updatePropDeps : (props : List GovProposal)
       getCoin deposits  getCoin (updateProposalDeposits props txid gaDep deposits)
    ≤updatePropDeps [] = ≤-reflexive refl
    ≤updatePropDeps (x  props) = ≤-trans (≤updatePropDeps props)
                                          (≤-trans (m≤m+n _ _)
                                                   (≤-reflexive $ sym $ ∪⁺singleton≡))
    updatePropDeps≡ : (ps : List GovProposal)
       getCoin (updateProposalDeposits ps txid gaDep deposits) - getCoin deposits  (length ps) * gaDep
    updatePropDeps≡ [] = n∸n≡0 (getCoin deposits)
    updatePropDeps≡ (_  ps) = let
      upD = updateProposalDeposits ps txid gaDep deposits in
      begin
        getCoin (upD ∪⁺  GovActionDeposit (txid , length ps) , gaDep ❵ᵐ)  getCoin deposits
          ≡⟨ cong (_∸ getCoin deposits) ∪⁺singleton≡ 
        getCoin upD + gaDep  getCoin deposits
          ≡⟨ +-∸-comm _ (≤updatePropDeps ps) 
        (getCoin upD  getCoin deposits) + gaDep
          ≡⟨ cong (_+ gaDep) (updatePropDeps≡ ps) 
        (length ps) * gaDep + gaDep
          ≡⟨ +-comm ((length ps) * gaDep) gaDep 
        gaDep + (length ps) * gaDep
          
        where open ≡-Reasoning

  ≤certDeps  :  {d : DepositPurpose  Coin} {(dp , c) : DepositPurpose × Coin}
               getCoin d  getCoin (d ∪⁺  (dp , c) )

  ≤certDeps {d} = begin
    getCoin d                      ≤⟨ m≤m+n (getCoin d) _ 
    getCoin d + _                  ≡⟨ sym ∪⁺singleton≡ 
    getCoin (d ∪⁺  _ )           
    where open ≤-Reasoning


  ≤updateCertDeps : (cs : List DCert) {pp : PParams} {deposits :  DepositPurpose  Coin}
     noRefundCert cs
     getCoin deposits  getCoin (updateCertDeposits pp cs deposits)
  ≤updateCertDeps [] nrf = ≤-reflexive refl
  ≤updateCertDeps (reg c v  cs) {pp} {deposits} (_ All.∷ nrf) =
    ≤-trans ≤certDeps (≤updateCertDeps cs {pp} {deposits ∪⁺  CredentialDeposit c , pp .PParams.keyDeposit } nrf)
  ≤updateCertDeps (delegate c _ _ v  cs) {pp} {deposits} (_ All.∷ nrf) =
    ≤-trans ≤certDeps (≤updateCertDeps cs {pp} {deposits ∪⁺  CredentialDeposit c , v } nrf)
  ≤updateCertDeps (regpool _ _  cs)       (_ All.∷ nrf) = ≤-trans ≤certDeps (≤updateCertDeps cs nrf)
  ≤updateCertDeps (retirepool _ _  cs)    (_ All.∷ nrf) = ≤updateCertDeps cs nrf
  ≤updateCertDeps (regdrep _ _ _  cs)     (_ All.∷ nrf) = ≤-trans ≤certDeps (≤updateCertDeps cs nrf)
  ≤updateCertDeps (ccreghot _ _  cs)      (_ All.∷ nrf) = ≤updateCertDeps cs nrf



  utxoMinSpend : {Γ : UTxOEnv} {tx : Tx} {s s' : UTxOState}
     Γ  s ⇀⦇ tx ,UTXO⦈ s'
     noRefundCert (txcertsOf tx)
     coin (consumed _ s (TxBodyOf tx))  length (txpropOf tx) * govActionDepositOf Γ


  utxoMinSpend step@(UTXO-inductive⋯ tx Γ utxoSt _ _ _ _ _ c≡p cmint≡0 _ _ _ _ _ _ _ _ _ _) nrf =
    begin
    length txprop * govActionDepositOf Γ
      ≡˘⟨ updatePropDeps≡ txprop 
    getCoin (updateProposalDeposits txprop txid (govActionDepositOf Γ) deposits)  getCoin deposits
      ≤⟨ ∸-monoˡ-≤ (getCoin deposits) (≤updateCertDeps txcerts nrf) 
    getCoin (updateDeposits (PParamsOf Γ) txb deposits) - getCoin deposits
      ≡⟨ ∸≡posPart⊖ {getCoin (updateDeposits (PParamsOf Γ) txb deposits)} {getCoin deposits} 
    newDeps
      ≤⟨ m≤n+m newDeps (coin balOut + txfee + txdonation) 
    coin balOut + txfee + txdonation + newDeps
      ≡⟨ +-assoc (coin balOut + txfee) txdonation newDeps 
    coin balOut + txfee + (txdonation + newDeps)
      ≡⟨ cong (coin balOut + txfee +_) (+-comm txdonation newDeps) 
    coin balOut + txfee + (newDeps + txdonation)
      ≡˘⟨ +-assoc (coin balOut + txfee) newDeps txdonation 
    coin balOut + txfee + newDeps + txdonation
      ≡˘⟨ cong  x  x + newDeps + txdonation) coin-inject-lemma 
    coin (balOut + inject txfee) + newDeps + txdonation
      ≡˘⟨ cong (_+ txdonation) coin-inject-lemma 
    coin (balOut + inject txfee + inject newDeps) + txdonation
      ≡˘⟨ coin-inject-lemma 
    coin (balOut + inject txfee + inject newDeps + inject txdonation)
      ≡˘⟨ cong coin c≡p 
    coin (balIn + mint + inject refunds + inject wdrls) 
    where
    open ≤-Reasoning
    open Tx tx renaming (body to txb); open TxBody txb
    open UTxOState utxoSt

    newDeps refunds wdrls : Coin
    newDeps = newDeposits (PParamsOf Γ) utxoSt txb
    refunds = depositRefunds (PParamsOf Γ) utxoSt txb
    wdrls = getCoin (wdrlsOf tx)

    balIn balOut : Value
    balIn = balance (utxo  txins)
    balOut = balance (outs txb)


module _
    ( indexedSum-∪⁺-hom :  {A V : Type}
                            _ : DecEq A   _ : DecEq V 
                            _ : CommutativeMonoid 0ℓ 0ℓ V 
                           (d₁ d₂ : A  V)
       


                           ∑[ x  d₁ ∪⁺ d₂ ] x  ∑[ x  d₁ ] x  ∑[ x  d₂ ] x


    )


  where
  open import Ledger.Conway.Utxow txs abs
  open ChainState; open NewEpochState; open EpochState
  open LState; open EnactState;  open PParams


  propose-minSpend :  {slot : Slot} {tx : Tx} {cs : ChainState}
                      ( let  pp      = PParamsOf cs
                             utxoSt  = UTxOStateOf cs )


    ( open Tx tx )
    ( open TxBody body )


     noRefundCert txcerts
     validTxIn₂ cs slot tx
     coin (consumed pp utxoSt body)  length txprop * pp .govActionDeposit 


  propose-minSpend noRef valid = case valid of λ where
    (_ , LEDGER-V (_ , UTXOW⇒UTXO x , _ , _))  utxoMinSpend indexedSum-∪⁺-hom x noRef
    (_ , LEDGER-I (_ , UTXOW⇒UTXO x))          utxoMinSpend indexedSum-∪⁺-hom x noRef