{-# OPTIONS --type-in-type --without-K #-}

open import Core.Functor
open import Core.Functor.Monad
open import Core.Ternary
open import Core.Logic

open import Core.MonotonePredicate 
open import Core.Signature
open import Core.Container
open import Core.Extensionality

open import Effect.Base
open import Effect.Syntax.Free
open import Effect.Syntax.Hefty

open import Effect.Handle
open import Effect.Elaborate

open import Effect.Theory.FirstOrder
open import Effect.Theory.HigherOrder

open import Effect.Relation.Binary.FirstOrderInclusion
open import Effect.Relation.Binary.HigherOrderInclusion
open import Effect.Relation.Ternary.FirstOrderSeparation
open import Effect.Relation.Ternary.HigherOrderSeparation

open import Effect.Instance.Empty.Syntax

open import Data.Product
open import Data.Maybe hiding (_>>=_)
open import Data.Sum
open import Data.Unit
open import Data.Empty
open import Data.Fin

open import Data.List.Relation.Unary.Any
open import Relation.Binary.PropositionalEquality renaming ([_] to ≡[_])
open import Function
open import Data.Vec hiding ([_])
open import Relation.Unary hiding (Empty ; _⊆_)

module Effect.Instance.LocalReader.Elaboration (R : Set) where

open import Effect.Instance.Reader.Syntax R
import Effect.Instance.Reader.Theory R as RT
open import Effect.Instance.Reader.Handler R

open import Effect.Instance.LocalReader.Syntax R
open import Effect.Instance.LocalReader.Theory R



open Handler ReaderHandler

coherence :  _ : Reader  ε   (m : Free ε A) (k : A  Free ε B)  ℋ⟦ m >>= k   ℋ⟦ m  >>= ℋ⟪ k 
coherence {ε = ε}  i  m k =
  begin
    ℋ⟦ m >>= k 
  ≡⟨⟩ 
      handle (i .proj₂) (m >>= k)
  ≡⟨ cong ( ∘_) (Properties.coherent (i .proj₂) m k)  
      (handle (i .proj₂) m >>=  x  handle (i .proj₂) (k x))) 
  ≡⟨ extensionality  r  ♯-coherent (handle (i .proj₂) m r) λ x  handle (i .proj₂) (k x) r) 
      handle (i .proj₂) m >>=  x    handle (i .proj₂) (k x)) 
  ≡⟨⟩ 
    ℋ⟦ m  >>= ℋ⟪ k 
  
  where
    open ≡-Reasoning
    instance inst : proj₁ i  ε
    inst = Reader , (union-comm $ i .proj₂)


-- TODO: we can (and should) prove this as a general lemma for all handlers 
handle-merge :  _ : Reader  ε   (m : Free ε A)  (r r′ : R)  ℋ⟦ ℋ⟦ m  r  r′  ℋ⟦ m  r
handle-merge  i  m r r′ =
  begin
    ℋ⟦ ℋ⟦ m  r  r′
  ≡⟨⟩
     (handle (i .proj₂) ( (handle (i .proj₂) m r)) r′)
  ≡⟨ cong  (cong (_$ r′) $ handle-modular (handle (i .proj₂) m r) (i .proj₂))  
     (( handle (i .proj₂) m r) r′)
  ≡⟨⟩ 
     (handle (i .proj₂) m r >>= flip return r′)
  ≡⟨ cong  (>>=-idʳ (handle (i .proj₂) m r))  
     (handle (i .proj₂) m r) 
  ≡⟨⟩
    ℋ⟦ m  r
  
  where
    open ≡-Reasoning
    instance inst : proj₁ i  _
    inst = Reader , union-comm (i .proj₂) 

ReaderElab : Elaboration (LocalReader) Reader
ReaderElab .Elaboration.elab = necessary λ i  readerElab  i 
  where
    readerElab :  Reader  ε   Algebra (LocalReader ε) (Free ε)
    readerElab .α  `ask ,       k , s  = ask >>= k
    readerElab .α  `local _ f , k , s  = do
      r  ask 
      v  ℋ⟦ s tt  (f r)
      k v

ReaderElab .Elaboration.commutes {f = f}  `ask , k , s   i  =
  begin
    elab  `ask , fmap f  k , s 
  ≡⟨⟩ 
    ask >>= fmap f  k 
  ≡⟨ (sym $ fmap->>= f ask k ) 
    fmap f (ask >>= k) 
  ≡⟨⟩ 
    fmap f (elab  (`ask , k , s) )
  
  where
    open ≡-Reasoning
    elab = (□⟨ Elaboration.elab ReaderElab  i) .α
ReaderElab .Elaboration.commutes {f = f}  `local t g , k , s   i  =
  begin
    elab  `local t g , fmap f  k , s 
  ≡⟨⟩
    (ask >>= λ r  ℋ⟦ s tt  (g r) >>= fmap f  k)
  ≡⟨ cong (ask >>=_) (extensionality λ r  sym $ fmap->>= f (ℋ⟦ s tt  (g r)) k)  
    ask >>=  r  fmap f (ℋ⟦ s tt  (g r) >>= k)) 
  ≡⟨ sym (fmap->>= f ask  r  ℋ⟦ s tt  (g r) >>= k)) 
    fmap f (ask >>= λ r  ℋ⟦ s tt  (g r) >>= k) 
  ≡⟨⟩ 
    fmap f (elab  `local t g , k , s )
  
  where
    open ≡-Reasoning
    elab = (□⟨ Elaboration.elab ReaderElab  i) .α

ReaderElab .Elaboration.coherent {c = `ask} k₁ k₂ = sym $ >>=-assoc k₁ k₂ ask
ReaderElab .Elaboration.coherent {c = `local t f} {s = s}  i  k₁ k₂ =
  begin
    elab  `local t f , (k₁ >=> k₂) , s 
  ≡⟨⟩
    (ask >>= λ r  ℋ⟦ s tt  (f r) >>= (k₁ >=> k₂))
  ≡⟨ (sym $ >>=-assoc  r  ℋ⟦ s tt  (f r)) (k₁ >=> k₂) ask) 
    (ask >>= λ r  ℋ⟦ s tt  (f r)) >>= (k₁ >=> k₂) 
  ≡⟨ sym $ >>=-assoc k₁ k₂ (ask >>= λ r  ℋ⟦ s tt  (f r))  
    ((ask >>= λ r  ℋ⟦ s tt  (f r)) >>= k₁) >>= k₂ 
  ≡⟨ cong     >>= k₂) ((>>=-assoc  r  ℋ⟦ s tt  (f r)) k₁ ask))  
    (ask >>= λ r  ℋ⟦ s tt  (f r) >>= k₁) >>= k₂ 
  ≡⟨⟩ 
    elab  `local t f , k₁ , s  >>= k₂
  
  where
    open ≡-Reasoning 
    elab = (□⟨ Elaboration.elab ReaderElab  i) .α

instance refl-inst : ε  ε
refl-inst = ≲-refl 

ReaderElabCorrect : Correctᴴ LocalReaderTheory RT.ReaderTheory ReaderElab

ReaderElabCorrect e′ (zero , refl) {γ = m} = 
  begin
    ℰ⟦ askl >> m 
  ≈⟪ ≡-to-≈ $ elab-∘′ askl  _  m) 
    ℰ⟦ askl  >> ℰ⟦ m 
  ≈⟪ >>=-resp-≈ˡ ℰ⟪  _  m)  (≡-to-≈ (use-elab-def _)) 
    (ask >>= pure) >> ℰ⟦ m  
  ≈⟪ >>=-resp-≈ˡ _ (>>=-idʳ-≈ ask ) 
    ask >> ℰ⟦ m  
  ≈⟪ ≈-eq′ (nec RT.ask-query) (zero , refl) 
    ℰ⟦ m 
  
  where
    open ≈-Reasoning _
    open Elaboration e′
ReaderElabCorrect e′ (suc zero , refl) {γ = f , x} = 
  begin
    ℰ⟦ local f (return x) 
  ≈⟪ ≡-to-≈ (use-elab-def _)  
    (ask >>= λ r  ℋ⟦ return x  (f r))
  ≈⟪⟫
    ask >> return x
  ≈⟪ ≈-eq′ (nec RT.ask-query) (zero , refl) 
    return x
  
  where
    open ≈-Reasoning _
    open Elaboration e′
ReaderElabCorrect e′ (suc (suc zero) , refl) {γ = k} =
  begin 
    ℰ⟦ askl >>=  r  askl >>= k r) 
  ≈⟪ ≡-to-≈ $ elab-∘′ askl  r  askl >>= k r) 
    ℰ⟦ askl  >>= ℰ⟪  r  askl >>= k r)  
  ≈⟪ >>=-resp-≈ˡ _ (≡-to-≈ (use-elab-def _)) 
    (ask >>= pure) >>= ℰ⟪  r  askl >>= k r)  
  ≈⟪ >>=-resp-≈ˡ _ (>>=-idʳ-≈ ask)  
    ask >>= ℰ⟪  r  askl >>= k r)  
  ≈⟪ >>=-resp-≈ʳ ask (≡-to-≈  elab-∘′ askl  k) 
    ask >>=  r  ℰ⟦ askl  >>= ℰ⟪ k r )
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-resp-≈ˡ ℰ⟪ k r  (≡-to-≈ (use-elab-def _)))  
    ask >>=  r  (ask >>= pure) >>= ℰ⟪ k r )
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-resp-≈ˡ ℰ⟪ k r  (>>=-idʳ-≈ ask))  
    ask >>=  z  ask >>= ℰ⟪ k z ) 
  ≈⟪ ≈-eq′ (nec RT.ask-ask) (suc zero , refl) 
    ask >>=  r  ℰ⟦ k r r ) 
  ≈⟪ >>=-resp-≈ˡ  r  ℰ⟦ k r r ) (≈-sym $ >>=-idʳ-≈ ask) 
    (ask >>= pure) >>=  r  ℰ⟦ k r r )
  ≈⟪ >>=-resp-≈ˡ  r  ℰ⟦ k r r ) ((≡-to-≈ (sym $ use-elab-def _))) 
    (ℰ⟦ askl  >>= λ r  ℰ⟦ k r r ) 
  ≈⟪ ≈-sym (≡-to-≈ (elab-∘′ askl  r  k r r)))  
    ℰ⟦ askl >>=  r  k r r)  
  
  where
    open ≈-Reasoning _
    open Elaboration e′
ReaderElabCorrect e′ (suc (suc (suc zero)) , refl) {γ = m , k} =
  begin
    ℰ⟦ m >>=  x  askl >>= k x) 
  ≈⟪ ≡-to-≈ (elab-∘′ m _) 
    ℰ⟦ m  >>=  x  ℰ⟦ askl >>= k x )
  ≈⟪ >>=-resp-≈ʳ ℰ⟦ m   x  ≡-to-≈ (elab-∘′ askl (k x)))  
    ℰ⟦ m  >>=  x  ℰ⟦ askl  >>= ℰ⟪ k x )
  ≈⟪ >>=-resp-≈ʳ ℰ⟦ m   x  >>=-resp-≈ˡ ℰ⟪ k x  (≈-trans (≡-to-≈ $ use-elab-def _) (>>=-idʳ-≈ ask))) 
    ℰ⟦ m  >>=  x  ask >>= ℰ⟪ k x )
  ≈⟪ ≈-eq′ (nec RT.ask-bind) ((suc (suc zero)) , refl) {γ = ℰ⟦ m  , _} 
    ask >>=  r  ℰ⟦ m  >>= λ x  ℰ⟦ k x r  )
  ≈⟪ >>=-resp-≈ˡ _ (≈-sym (≈-trans (≡-to-≈ $ use-elab-def _) (>>=-idʳ-≈ ask)))  
    ℰ⟦ askl  >>= ((λ r  ℰ⟦ m  >>= λ x  ℰ⟦ k x r  )) 
  ≈⟪ >>=-resp-≈ʳ ℰ⟦ askl   r  ≈-sym $ ≡-to-≈ $ elab-∘′ m λ x  k x r) 
    ℰ⟦ askl  >>=  r  ℰ⟦ m >>=  x  k x r) ) 
  ≈⟪ ≈-sym (≡-to-≈ (elab-∘′ askl _))  
    ℰ⟦ (askl >>= λ r  m >>= λ x  k x r) 
  
  where
    open ≈-Reasoning _
    open Elaboration e′
ReaderElabCorrect e′ (suc (suc (suc (suc zero))) , refl) {γ = m , k , f} =
  begin
    ℰ⟦ local f (m >>= k) 
  ≈⟪ ≡-to-≈ (use-elab-def _) 
    ask >>=  r  ℋ⟦ ℰ⟦ m >>= k   (f r) >>= pure) 
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-idʳ-≈ _) 
    ask >>=  r  ℋ⟦ ℰ⟦ m >>= k   (f r))
  ≈⟪ >>=-resp-≈ʳ ask  r  ≡-to-≈ (cong    ℋ⟦   (f r)) (elab-∘′ m k))) 
    ask >>=  r  ℋ⟦ (ℰ⟦ m  >>= ℰ⟪ k )  (f r))  
  ≈⟪ >>=-resp-≈ʳ ask  r  ≡-to-≈ $ cong (_$ (f r)) (coherence ℰ⟦ m  ℰ⟪ k ))  
    ask >>=  r  ℋ⟦ ℰ⟦ m   (f r) >>= λ x  ℋ⟦ ℰ⟦ k x   (f r))
  ≈⟪ ≈-sym (≈-eq′ (nec RT.ask-ask) (suc zero , refl))  
    (ask >>= λ r  ask >>= λ r′  ℋ⟦ ℰ⟦ m   (f r) >>= λ x  ℋ⟦ ℰ⟦ k x   (f r′)) 
  ≈⟪ >>=-resp-≈ʳ ask  r  ≈-sym $ ≈-eq′ (nec RT.ask-bind) (suc (suc zero) , refl) {γ = (ℋ⟦ ℰ⟦ m   (f r)) , _}) 
    (ask >>= λ r  ℋ⟦ ℰ⟦ m   (f r) >>= λ x  ask >>= λ r′  ℋ⟦ ℰ⟦ k x   (f r′))
  ≈⟪ ≈-sym (>>=-assoc-≈ _ _ ask) 
    (ask >>= λ r  ℋ⟦ ℰ⟦ m   (f r)) >>=  x  ask >>= λ r′  ℋ⟦ ℰ⟦ k x   (f r′)) 
  ≈⟪ (>>=-resp-≈ʳ (((ask >>= λ r  ℋ⟦ ℰ⟦ m   (f r)))) λ x  >>=-resp-≈ʳ ask λ r′  ≈-sym $ >>=-idʳ-≈ (ℋ⟦ ℰ⟦ k x   (f r′))) 
    ((ask >>= λ r  ℋ⟦ ℰ⟦ m   (f r))) >>=  x  ask >>= λ r′  ℋ⟦ ℰ⟦ k x   (f r′) >>= pure)
  ≈⟪ >>=-resp-≈ˡ ((λ x  ask >>= λ r′  ℋ⟦ ℰ⟦ k x   (f r′) >>= pure)) (>>=-resp-≈ʳ ask  r  ≈-sym (>>=-idʳ-≈ _))) 
    (ask >>=  r  ℋ⟦ ℰ⟦ m   (f r) >>= pure)) >>=  x  ask >>= λ r′  ℋ⟦ ℰ⟦ k x   (f r′) >>= pure) 
  ≈⟪ >>=-resp-≈ʳ (ask >>=  r  ℋ⟦ ℰ⟦ m   (f r) >>= pure))  x  ≈-sym (≡-to-≈ (use-elab-def _))) 
    (ask >>=  r  ℋ⟦ ℰ⟦ m   (f r) >>= pure)) >>= ℰ⟪ local f  k  
  ≈⟪ >>=-resp-≈ˡ ℰ⟪ local f  k  (≈-sym (≡-to-≈ (use-elab-def _)))  
    ℰ⟦ local f m  >>= ℰ⟪ local f  k 
  ≈⟪ ≈-sym (≡-to-≈ (elab-∘′ (local f m) (local f  k)))  
    ℰ⟦ local f m >>= local f  k 
  
  where
    open ≈-Reasoning _
    open Elaboration e′

ReaderElabCorrect e′  ζ  (suc (suc (suc (suc (suc zero)))) , refl) {γ = f} = 
  begin
    ℰ⟦ local f askl 
  ≈⟪ ≡-to-≈ (use-elab-def _) 
    ask >>=  r  ℋ⟦ ℰ⟦ askl   (f r) >>= pure)
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-idʳ-≈ _) 
    ask >>=  r  ℋ⟦ ℰ⟦ askl   (f r))
  ≈⟪ >>=-resp-≈ʳ ask  r  ≡-to-≈ (cong    ℋ⟦   (f r)) (use-elab-def _)))  
    ask >>=  r  ℋ⟦ ask >>= pure  (f r))
  ≈⟪ >>=-resp-≈ʳ ask  r  ≡-to-≈ (cong     (f r)) (coherence ask pure)))  
    ask >>=  r  ℋ⟦ ask  (f r) >>= λ r′  ℋ⟦ pure r′  (f r))
  ≈⟪⟫ 
    ask >>=  r  ℋ⟦ ask  (f r) >>= pure)
  ≈⟪ >>=-resp-≈ʳ ask  r  ≡-to-≈ (Properties.handle-ask (ζ .≲-eff .proj₂) pure)) 
    ask >>= pure  f
  ≈⟪ >>=-resp-≈ˡ (pure  f) (≈-sym (>>=-idʳ-≈ ask))  
    (ask >>= pure) >>= pure  f 
  ≈⟪ >>=-resp-≈ˡ (pure  f) (≈-sym (≡-to-≈ (use-elab-def _)))  
    ℰ⟦ askl  >>= pure  f 
  ≈⟪ ≈-sym (≡-to-≈ (elab-∘′ askl (return  f)))  
    ℰ⟦ askl >>= return  f  
  
  where
    open ≈-Reasoning _
    open Elaboration e′

ReaderElabCorrect e′  ζ  (suc (suc (suc (suc (suc (suc zero))))) , refl) {γ = f , g , m} =
  begin
    ℰ⟦ local (f  g) m 
  ≈⟪ ≡-to-≈ (use-elab-def _)  
    ask >>=  r  ℋ⟦ ℰ⟦ m   (f (g r)) >>= pure)
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-resp-≈ˡ pure (≡-to-≈ (sym $ handle-merge ℰ⟦ m  (f (g r)) (g r)))) 
    ask >>=  r  ℋ⟦ ℋ⟦ ℰ⟦ m   (f (g r))  (g r) >>= pure)
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-resp-≈ˡ pure (≈-sym (≡-to-≈ (Properties.handle-ask (ζ .≲-eff .proj₂) _))))  
    ask >>=  r  (ℋ⟦ ask  (g r) >>= λ r′  ℋ⟦ ℋ⟦ ℰ⟦ m   (f r′)  (g r)) >>= pure)  
  ≈⟪ >>=-resp-≈ʳ ask
        r  >>=-resp-≈ˡ pure
         (>>=-resp-≈ʳ (ℋ⟦ ask  (g r)) λ r′ 
           ≡-to-≈ (cong    ℋ⟦   (g r)) (sym $ >>=-idʳ (ℋ⟦ ℰ⟦ m   (f r′))))))
    
    ask >>=  r  (ℋ⟦ ask  (g r) >>= λ r′  ℋ⟦ ℋ⟦ ℰ⟦ m   (f r′) >>= pure  (g r)) >>= pure) 
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-resp-≈ˡ pure (≈-sym (≡-to-≈ (cong (_$ (g r)) (coherence ask _)))))  
    ask >>=  r  ℋ⟦ ask >>=  r′  ℋ⟦ ℰ⟦ m   (f r′) >>= pure)  (g r) >>= pure)
  ≈⟪ >>=-resp-≈ʳ ask  r  >>=-resp-≈ˡ pure (≡-to-≈ (cong    ℋ⟦   (g r) ) (sym $ use-elab-def _)))) 
    ask >>=  r  ℋ⟦ ℰ⟦ local f m   (g r) >>= pure)
  ≈⟪ ≈-sym (≡-to-≈ (use-elab-def _))  
    ℰ⟦ local g (local f m) 
  
  where
    open ≈-Reasoning _
    open Elaboration e′