{-# OPTIONS --without-K --safe #-}

open import Definition.Typed.EqualityRelation
open import Tools.Relation

module Definition.LogicalRelation.Properties.Transitivity {a } (M′ : Setoid a )
                                                          {{eqrel : EqRelSet M′}} where
open EqRelSet {{...}}
open Setoid M′ using () renaming (Carrier to M)

open import Definition.Untyped M hiding (_∷_)
import Definition.Untyped.BindingType M′ as BT
open import Definition.Typed M′
open import Definition.Typed.Properties M′
import Definition.Typed.Weakening M′ as Weak
open import Definition.LogicalRelation M′
open import Definition.LogicalRelation.ShapeView M′
open import Definition.LogicalRelation.Irrelevance M′
open import Definition.LogicalRelation.Properties.Conversion M′

open import Tools.Nat
open import Tools.Product
import Tools.PropositionalEquality as PE

private
  variable
    n : Nat
    Γ : Con Term n

mutual
  -- Helper function for transitivity of type equality using shape views.
  transEqT :  {n} {Γ : Con Term n} {A B C l l′ l″}
             {[A] : Γ ⊩⟨ l  A} {[B] : Γ ⊩⟨ l′  B} {[C] : Γ ⊩⟨ l″  C}
            ShapeView₃ Γ l l′ l″ A B C [A] [B] [C]
            Γ ⊩⟨ l   A  B / [A]
            Γ ⊩⟨ l′  B  C / [B]
            Γ ⊩⟨ l   A  C / [A]
  transEqT (ℕᵥ D D′ D″) A≡B B≡C = B≡C
  transEqT (Emptyᵥ D D′ D″) A≡B B≡C = B≡C
  transEqT (Unitᵥ D D′ D″) A≡B B≡C = B≡C
  transEqT (ne (ne K [ ⊢A , ⊢B , D ] neK K≡K) (ne K₁ D₁ neK₁ _)
               (ne K₂ D₂ neK₂ _))
           (ne₌ M D′ neM K≡M) (ne₌ M₁ D″ neM₁ K≡M₁)
           rewrite whrDet* (red D₁ , ne neK₁) (red D′ , ne neM)
                 | whrDet* (red D₂ , ne neK₂) (red D″ , ne neM₁) =
    ne₌ M₁ D″ neM₁
        (~-trans K≡M K≡M₁)
  transEqT {n = n} {Γ = Γ} {l = l} {l′ = l′} {l″ = l″}
           (Bᵥ W W′ W″ (Bᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                 (Bᵣ F₁ G₁ D₁ ⊢F₁ ⊢G₁ A≡A₁ [F]₁ [G]₁ G-ext₁)
                 (Bᵣ F₂ G₂ D₂ ⊢F₂ ⊢G₂ A≡A₂ [F]₂ [G]₂ G-ext₂))
           (B₌ F′ G′ W‴ D′ W≋W′ A≡B [F≡F′] [G≡G′])
           (B₌ F″ G″ W⁗ D″ W′≋W″ A≡B₁ [F≡F′]₁ [G≡G′]₁) =
    let ΠF₁G₁≡ΠF′G′       = whrDet* (red D₁ ,  W′ ⟧ₙ) (D′  ,  W‴ ⟧ₙ)
        F₁≡F′ , G₁≡G′ , W′≡W‴ = B-PE-injectivity W′ W‴ ΠF₁G₁≡ΠF′G′
        F₂≡F″ , G₂≡G″ , _ = B-PE-injectivity W″ W⁗ (whrDet* (red D₂ ,  W″ ⟧ₙ) (D″ ,  W⁗ ⟧ₙ))
        substLift :  {m n Δ l a} (ρ : Wk m n) x  Set _
        substLift {_} {_} {Δ} {l} {a} ρ x = Δ ⊩⟨ l  wk (lift ρ) x [ a ]
        [F′] :  {m} {ρ : Wk m n} {Δ} [ρ] ⊢Δ  Δ ⊩⟨ l′  wk ρ F′
        [F′] {_} {ρ} [ρ] ⊢Δ = PE.subst  x  _ ⊩⟨ _  wk ρ x) F₁≡F′ ([F]₁ [ρ] ⊢Δ)
        [F″] :  {m} {ρ : Wk m n} {Δ} [ρ] ⊢Δ  Δ ⊩⟨ l″  wk ρ F″
        [F″] {_} {ρ} [ρ] ⊢Δ = PE.subst  x  _ ⊩⟨ _  wk ρ x) F₂≡F″ ([F]₂ [ρ] ⊢Δ)
        [F′≡F″] :  {m} {ρ : Wk m n} {Δ} [ρ] ⊢Δ  Δ ⊩⟨ l′  wk ρ F′  wk ρ F″ / [F′] [ρ] ⊢Δ
        [F′≡F″] {_} {ρ} [ρ] ⊢Δ = irrelevanceEq′ (PE.cong (wk ρ) F₁≡F′)
                                      ([F]₁ [ρ] ⊢Δ) ([F′] [ρ] ⊢Δ) ([F≡F′]₁ [ρ] ⊢Δ)
        [G′] :  {m} {ρ : Wk m n} {Δ a} [ρ] ⊢Δ
              Δ ⊩⟨ l′  a  wk ρ F′ / [F′] [ρ] ⊢Δ
              Δ ⊩⟨ l′  wk (lift ρ) G′ [ a ]
        [G′] {m} {ρ} [ρ] ⊢Δ [a] =
          let [a′] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₁≡G′ ([G]₁ [ρ] ⊢Δ [a′])
        [G″] :  {m} {ρ : Wk m n} {Δ a} [ρ] ⊢Δ
              Δ ⊩⟨ l″  a  wk ρ F″ / [F″] [ρ] ⊢Δ
              Δ ⊩⟨ l″  wk (lift ρ) G″ [ a ]
        [G″] {_} {ρ} [ρ] ⊢Δ [a] =
          let [a″] = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₂≡F″))
                                      ([F″] [ρ] ⊢Δ) ([F]₂ [ρ] ⊢Δ) [a]
          in  PE.subst (substLift ρ) G₂≡G″ ([G]₂ [ρ] ⊢Δ [a″])
        [G′≡G″] :  {m} {ρ : Wk m n} {Δ a} [ρ] ⊢Δ
                  ([a] : Δ ⊩⟨ l′  a  wk ρ F′ / [F′] [ρ] ⊢Δ)
                 Δ ⊩⟨ l′  wk (lift ρ) G′  [ a ]
                           wk (lift ρ) G″ [ a ] / [G′] [ρ] ⊢Δ [a]
        [G′≡G″] {_} {ρ} [ρ] ⊢Δ [a′] =
          let [a]₁ = irrelevanceTerm′ (PE.cong (wk ρ) (PE.sym F₁≡F′))
                                      ([F′] [ρ] ⊢Δ) ([F]₁ [ρ] ⊢Δ) [a′]
          in  irrelevanceEq′ (PE.cong  x  wk (lift ρ) x [ _ ]) G₁≡G′)
                             ([G]₁ [ρ] ⊢Δ [a]₁) ([G′] [ρ] ⊢Δ [a′])
                             ([G≡G′]₁ [ρ] ⊢Δ [a]₁)
    in  B₌ F″ G″ W⁗ D″ (BT.trans W≋W′ (PE.subst  W  W BT.≋ W⁗) W′≡W‴ W′≋W″))
           (≅-trans A≡B (PE.subst  x  Γ  x   W⁗  F″  G″) ΠF₁G₁≡ΠF′G′ A≡B₁))
            ρ ⊢Δ  transEq ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ)
                              ([F≡F′] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ))
           λ ρ ⊢Δ [a] 
             let [a′] = convTerm₁ ([F] ρ ⊢Δ) ([F′] ρ ⊢Δ) ([F≡F′] ρ ⊢Δ) [a]
                 [a″] = convTerm₁ ([F′] ρ ⊢Δ) ([F″] ρ ⊢Δ) ([F′≡F″] ρ ⊢Δ) [a′]
             in  transEq ([G] ρ ⊢Δ [a]) ([G′] ρ ⊢Δ [a′]) ([G″] ρ ⊢Δ [a″])
                         ([G≡G′] ρ ⊢Δ [a]) ([G′≡G″] ρ ⊢Δ [a′])
  transEqT (Uᵥ ⊢Γ ⊢Γ₁ ⊢Γ₂) A≡B B≡C = A≡B
  transEqT (emb⁰¹¹ AB) A≡B B≡C = transEqT AB A≡B B≡C
  transEqT (emb¹⁰¹ AB) A≡B B≡C = transEqT AB A≡B B≡C
  transEqT (emb¹¹⁰ AB) A≡B B≡C = transEqT AB A≡B B≡C

  -- Transitivty of type equality.
  transEq :  {A B C l l′ l″}
            ([A] : Γ ⊩⟨ l  A) ([B] : Γ ⊩⟨ l′  B) ([C] : Γ ⊩⟨ l″  C)
           Γ ⊩⟨ l   A  B / [A]
           Γ ⊩⟨ l′  B  C / [B]
           Γ ⊩⟨ l   A  C / [A]
  transEq [A] [B] [C] A≡B B≡C =
    transEqT (combine (goodCases [A] [B] A≡B) (goodCases [B] [C] B≡C)) A≡B B≡C

  -- Transitivty of type equality with some propositonally equal types.
  transEq′ :  {A B B′ C C′ l l′ l″}  B PE.≡ B′  C PE.≡ C′
            ([A] : Γ ⊩⟨ l  A) ([B] : Γ ⊩⟨ l′  B) ([C] : Γ ⊩⟨ l″  C)
            Γ ⊩⟨ l   A  B′ / [A]
            Γ ⊩⟨ l′  B  C′ / [B]
            Γ ⊩⟨ l   A  C  / [A]
  transEq′ PE.refl PE.refl [A] [B] [C] A≡B B≡C = transEq [A] [B] [C] A≡B B≡C


transEqTermNe :  {n n′ n″ A}
               Γ ⊩neNf n   n′   A
               Γ ⊩neNf n′  n″  A
               Γ ⊩neNf n   n″  A
transEqTermNe (neNfₜ₌ neK neM k≡m) (neNfₜ₌ neK₁ neM₁ k≡m₁) =
  neNfₜ₌ neK neM₁ (~-trans k≡m k≡m₁)

mutual
  transEqTermℕ :  {n n′ n″}
                Γ ⊩ℕ n   n′  ∷ℕ
                Γ ⊩ℕ n′  n″ ∷ℕ
                Γ ⊩ℕ n   n″ ∷ℕ
  transEqTermℕ (ℕₜ₌ k k′ d d′ t≡u prop)
               (ℕₜ₌ k₁ k″ d₁ d″ t≡u₁ prop₁) =
    let k₁Whnf = naturalWhnf (proj₁ (split prop₁))
        k′Whnf = naturalWhnf (proj₂ (split prop))
        k₁≡k′ = whrDet*Term (redₜ d₁ , k₁Whnf) (redₜ d′ , k′Whnf)
        prop′ = PE.subst  x  [Natural]-prop _ x _) k₁≡k′ prop₁
    in  ℕₜ₌ k k″ d d″ (≅ₜ-trans t≡u (PE.subst  x  _  x  _  _) k₁≡k′ t≡u₁))
            (transNatural-prop prop prop′)

  transNatural-prop :  {k k′ k″}
                     [Natural]-prop Γ k k′
                     [Natural]-prop Γ k′ k″
                     [Natural]-prop Γ k k″
  transNatural-prop (sucᵣ x) (sucᵣ x₁) = sucᵣ (transEqTermℕ x x₁)
  transNatural-prop (sucᵣ x) (ne (neNfₜ₌ () neM k≡m))
  transNatural-prop zeroᵣ prop₁ = prop₁
  transNatural-prop prop zeroᵣ = prop
  transNatural-prop (ne (neNfₜ₌ neK () k≡m)) (sucᵣ x₃)
  transNatural-prop (ne [k≡k′]) (ne [k′≡k″]) =
    ne (transEqTermNe [k≡k′] [k′≡k″])

-- Empty
transEmpty-prop :  {k k′ k″}
   [Empty]-prop Γ k k′
   [Empty]-prop Γ k′ k″
   [Empty]-prop Γ k k″
transEmpty-prop (ne [k≡k′]) (ne [k′≡k″]) =
  ne (transEqTermNe [k≡k′] [k′≡k″])

transEqTermEmpty :  {n n′ n″}
   Γ ⊩Empty n   n′ ∷Empty
   Γ ⊩Empty n′  n″ ∷Empty
   Γ ⊩Empty n   n″ ∷Empty
transEqTermEmpty (Emptyₜ₌ k k′ d d′ t≡u prop)
             (Emptyₜ₌ k₁ k″ d₁ d″ t≡u₁ prop₁) =
  let k₁Whnf = ne (proj₁ (esplit prop₁))
      k′Whnf = ne (proj₂ (esplit prop))
      k₁≡k′ = whrDet*Term (redₜ d₁ , k₁Whnf) (redₜ d′ , k′Whnf)
      prop′ = PE.subst  x  [Empty]-prop _ x _) k₁≡k′ prop₁
  in Emptyₜ₌ k k″ d d″ (≅ₜ-trans t≡u (PE.subst  x  _  x  _  _) k₁≡k′ t≡u₁))
     (transEmpty-prop prop prop′)

-- Transitivty of term equality.
transEqTerm :  {l A t u v}
              ([A] : Γ ⊩⟨ l  A)
             Γ ⊩⟨ l  t  u  A / [A]
             Γ ⊩⟨ l  u  v  A / [A]
             Γ ⊩⟨ l  t  v  A / [A]
transEqTerm (Uᵣ′ . 0<1 ⊢Γ)
            (Uₜ₌ A B d d′ typeA typeB t≡u [t] [u] [t≡u])
            (Uₜ₌ A₁ B₁ d₁ d₁′ typeA₁ typeB₁ t≡u₁ [t]₁ [u]₁ [t≡u]₁)
            rewrite whrDet*Term (redₜ d′ , typeWhnf typeB) (redₜ d₁ , typeWhnf typeA₁) =
  Uₜ₌ A B₁ d d₁′ typeA typeB₁ (≅ₜ-trans t≡u t≡u₁) [t] [u]₁
      (transEq [t] [u] [u]₁ [t≡u] (irrelevanceEq [t]₁ [u] [t≡u]₁))
transEqTerm (ℕᵣ D) [t≡u] [u≡v] = transEqTermℕ [t≡u] [u≡v]
transEqTerm (Emptyᵣ D) [t≡u] [u≡v] = transEqTermEmpty [t≡u] [u≡v]
transEqTerm (Unitᵣ D) (Unitₜ₌ ⊢t _) (Unitₜ₌ _ ⊢v) = Unitₜ₌ ⊢t ⊢v
transEqTerm (ne′ K D neK K≡K) (neₜ₌ k m d d′ (neNfₜ₌ neK₁ neM k≡m))
                              (neₜ₌ k₁ m₁ d₁ d″ (neNfₜ₌ neK₂ neM₁ k≡m₁)) =
  let k₁≡m = whrDet*Term (redₜ d₁ , ne neK₂) (redₜ d′ , ne neM)
  in  neₜ₌ k m₁ d d″
           (neNfₜ₌ neK₁ neM₁
                   (~-trans k≡m (PE.subst  x  _  x ~ _  _) k₁≡m k≡m₁)))
transEqTerm (Bᵣ′ BΠ! F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Πₜ₌ f g d d′ funcF funcG f≡g [f] [g] [f≡g])
            (Πₜ₌ f₁ g₁ d₁ d₁′ funcF₁ funcG₁ f≡g₁ [f]₁ [g]₁ [f≡g]₁)
            rewrite whrDet*Term (redₜ d′ , functionWhnf funcG)
                            (redₜ d₁ , functionWhnf funcF₁) =
  Πₜ₌ f g₁ d d₁′ funcF funcG₁ (≅ₜ-trans f≡g f≡g₁) [f] [g]₁
                 ρ ⊢Δ [a] p≈p₁ p≈p₂  transEqTerm ([G] ρ ⊢Δ [a])
                                                ([f≡g] ρ ⊢Δ [a] p≈p₁ p≈p₁)
                                                ([f≡g]₁ ρ ⊢Δ [a] p≈p₁ p≈p₂))
transEqTerm (Bᵣ′ ( _ m) F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Σₜ₌ p r d d′ pProd rProd p≅r [t] [u] prop) (Σₜ₌ p₁ r₁ d₁ d₁′ pProd₁ rProd₁ p≅r₁ [t]₁ [u]₁ prop₁)
            with whrDet*Term (redₜ d′ , productWhnf rProd) (redₜ d₁ , productWhnf pProd₁)
transEqTerm (Bᵣ′ BΣₚ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Σₜ₌ p r d d′ pProd rProd p≅r [t] [u] ([fstp] , [fstr] , [fst≡] , [snd≡]))
            (Σₜ₌ r r₁ d₁ d₁′ pProd₁ rProd₁ p≅r₁ [t]₁ [u]₁ ([fstp]₁ , [fstr]₁ , [fst≡]₁ , [snd≡]₁))
            | PE.refl =
  let ⊢Γ = wf ⊢F
      p≅r₁ = ≅ₜ-trans p≅r p≅r₁
      [F]′ = [F] Weak.id ⊢Γ
      [fst≡]′ = transEqTerm [F]′ [fst≡] [fst≡]₁
      [Gfstp≡Gfstp₁] = G-ext Weak.id ⊢Γ [fstp] [fstr] [fst≡]
      [Gfstp] = [G] Weak.id ⊢Γ [fstp]
      [Gfstp₁] = [G] Weak.id ⊢Γ [fstp]₁
      [snd≡]₁′ = convEqTerm₂ [Gfstp] [Gfstp₁] [Gfstp≡Gfstp₁] [snd≡]₁
      [snd≡]′ = transEqTerm [Gfstp] [snd≡] [snd≡]₁′
  in  Σₜ₌ p r₁ d d₁′ pProd rProd₁ p≅r₁ [t] [u]₁ ([fstp] , [fstr]₁ , [fst≡]′ , [snd≡]′)
transEqTerm (Bᵣ′ BΣᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Σₜ₌ p r d d′ prodₙ prodₙ p≅r [t] [u] ([p₁] , [r₁] , [p₂] , [r₂] , [p₁≡r₁] , [p₂≡r₂]))
            (Σₜ₌ r r₁ d₁ d₁′ prodₙ prodₙ p≅r₁ [t]₁ [u]₁ ([p₁]₁ , [r₁]₁ , [p₂]₁ , [r₂]₁ , [p₁≡r₁]₁ , [p₂≡r₂]₁))
            | PE.refl =
  let ⊢Γ = wf ⊢F
      p≅r₁ = ≅ₜ-trans p≅r p≅r₁
      [F]′ = [F] Weak.id ⊢Γ
      [p₁≡r₁]′ = transEqTerm [F]′ [p₁≡r₁] [p₁≡r₁]₁
      [Gp≡Gp₁] = G-ext Weak.id ⊢Γ [p₁] [p₁]₁ [p₁≡r₁]
      [Gp] = [G] Weak.id ⊢Γ [p₁]
      [Gp]₁ = [G] Weak.id ⊢Γ [p₁]₁
      [p₂≡r₂]₁′ = convEqTerm₂ [Gp] [Gp]₁ [Gp≡Gp₁] [p₂≡r₂]₁
      [p₂≡r₂]′ = transEqTerm [Gp] [p₂≡r₂] [p₂≡r₂]₁′
  in  Σₜ₌ p r₁ d d₁′ prodₙ prodₙ p≅r₁ [t] [u]₁ ([p₁] , [r₁]₁ , [p₂] , [r₂]₁ , [p₁≡r₁]′ , [p₂≡r₂]′)
transEqTerm (Bᵣ′ BΣᵣ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
            (Σₜ₌ p r d d′ (ne x) (ne y) p≅r [t] [u] p~r)
            (Σₜ₌ r r₁ d₁ d₁′ (ne x₁) (ne y₁) p≅r₁ [t]₁ [u]₁ p₁~r₁)
            | PE.refl =
            Σₜ₌ p r₁ d d₁′ (ne x) (ne y₁) (≅ₜ-trans p≅r p≅r₁) [t] [u]₁ (~-trans p~r p₁~r₁)
transEqTerm (emb 0<1 x) t≡u u≡v = transEqTerm x t≡u u≡v