module ITyping-Subst where

open import Data.Empty
open import Data.List
open import Data.Nat
open import Data.Product
open import Data.List.Properties
open import Data.List.Any as LAny
open LAny.Membership-≡
open import Relation.Nullary
open import Relation.Binary.Core
open import Relation.Binary.PropositionalEquality using (sym)

open import Core
open import Core-Lemmas
open import Typing
open import Typed-Core
open import ITyping-Core
open import Reduction using (_↝_)

wfI-Γ-exchange :  {Γ x y τ₁ τ₂} -> Wf-ICtxt ((x , τ₁)  (y , τ₂)  Γ) ->
  Wf-ICtxt ((y , τ₂)  (x , τ₁)  Γ)
wfI-Γ-exchange (cons x∉ x₂ (cons x∉₁ x₃ wf-Γ)) = cons
  ((∉-∷ _ _  y≡x  fv-x≠y _ _ x∉ (≡-sym y≡x)) x∉₁))
  x₃
  (cons (∉-∷-elim _ x∉) x₂ wf-Γ)
------------------------------------------------------------------------------------

exchange-⊩ₗ :  {A Γ x y τ₁ τ₂ X Y δ} {m : Λ A} ->
  ((x , τ₁ , X)  (y , τ₂ , Y)  Γ) ⊩ₗ m  δ ->
  ((y , τ₂ , Y)  (x , τ₁ , X)  Γ) ⊩ₗ m  δ
exchange-⊩ₗ {_} {Γ} {x} {y} {τ₁} {τ₂} {A} {B} {_} {m} x∷y∷Γ⊩ₗm∶δ =
  subₗ x∷y∷Γ⊩ₗm∶δ
       (⊆ₗ-refl (⊩ₗ-∷'ₗ x∷y∷Γ⊩ₗm∶δ))
       (⊆Γ-⊆ (wfI-Γ-exchange (⊩ₗ-wf-Γ x∷y∷Γ⊩ₗm∶δ)) ⊆-aux)
  where
  ⊆-aux : (x , (τ₁ , A))  ((y , (τ₂ , B))  Γ)  (y , (τ₂ , B))  ((x , τ₁ , A)  Γ)
  ⊆-aux (here px) = there (here px)
  ⊆-aux (there (here px)) = here px
  ⊆-aux (there (there )) = there (there )
------------------------------------------------------------------------------------

strenghten-⊩-aux :  {A B Γ x τ τᵢ} {m : Λ B} {t} -> m ~T t -> x  ΛFV m -> ((x , τᵢ , A)  Γ)  m  τ -> Γ  m  τ
strenghten-⊩ₗ-aux :  {A B Γ x τ τᵢ} {m : Λ B} {t} -> m ~T t -> x  ΛFV m -> ((x , τᵢ , A)  Γ) ⊩ₗ m  τ -> Γ ⊩ₗ m  τ

strenghten-⊩-aux m~Tt x∉ (var (cons x∉₁ x₂ wf-Γ) (here refl) τ⊆τᵢ) =
  ⊥-elim (x∉ (here refl))
strenghten-⊩-aux m~Tt x∉ (var (cons x∉₁ x₂ wf-Γ) (there τᵢ∈Γ) τ⊆τᵢ) =
  var wf-Γ τᵢ∈Γ τ⊆τᵢ
strenghten-⊩-aux (bin u~Tt v~Tt) x∉ (app {s = s} x∷Γ⊩m∶τ x₁ x₂) =
  app (strenghten-⊩-aux u~Tt (∉-cons-l _ _ x∉) x∷Γ⊩m∶τ)
      (strenghten-⊩ₗ-aux v~Tt (∉-cons-r (ΛFV s) _ x∉) x₁)
      x₂
strenghten-⊩-aux {x = x} (un m~Tt) x∉ (abs L {m} cf) =
  abs (x  L)
       x∉L  strenghten-⊩ₗ-aux (^'-~T-inv m~Tt)
                                  (ΛFV-^ m x∉  x₂  fv-x≠y _ _ x∉L (sym x₂)))
                                  (exchange-⊩ₗ (cf (∉-∷-elim _ x∉L))))
strenghten-⊩-aux l-Y x∉ (Y (cons x∉₁ x₁ wf-Γ) x₂ x₃) = Y wf-Γ x₂ x₃
strenghten-⊩-aux m~Tt x∉ (~>∩ x∷Γ⊩m∶τ x∷Γ⊩m∶τ₁ x₁) =
  ~>∩ (strenghten-⊩-aux m~Tt x∉ x∷Γ⊩m∶τ) (strenghten-⊩-aux m~Tt x∉ x∷Γ⊩m∶τ₁) x₁

strenghten-⊩ₗ-aux m~Tt x∉ (nil (cons x∉₁ x₁ wf-Γ)) = nil wf-Γ
strenghten-⊩ₗ-aux m~Tt x∉ (cons x₁ x∷Γ⊩ₗm∶τ) =
  cons (strenghten-⊩-aux m~Tt x∉ x₁) (strenghten-⊩ₗ-aux m~Tt x∉ x∷Γ⊩ₗm∶τ)
------------------------------------------------------------------------------------

strenghten-⊩ :  {A B Γ x τ τᵢ} {m : Λ B} -> x  ΛFV m ->
  ((x , τᵢ , A)  Γ)  m  τ -> Γ  m  τ
strenghten-⊩ {m = m} x∉ x∷Γ⊩m∶τ = strenghten-⊩-aux (proj₂ (∃~T m)) x∉ x∷Γ⊩m∶τ
------------------------------------------------------------------------------------

strenghten-⊩ₗ :  {A B Γ x τ τᵢ} {m : Λ B} -> x  ΛFV m ->
  ((x , τᵢ , A)  Γ) ⊩ₗ m  τ -> Γ ⊩ₗ m  τ
strenghten-⊩ₗ {m = m} x∉ x∷Γ⊩m∶τ = strenghten-⊩ₗ-aux (proj₂ (∃~T m)) x∉ x∷Γ⊩m∶τ
------------------------------------------------------------------------------------

∈-imp-∈dom :  {Γ} {A : Type} {x : Atom} {τ : List IType} -> (x , (τ , A))  Γ ->
  x  dom Γ
∈-imp-∈dom (here refl) = here refl
∈-imp-∈dom (there x,τ,A∈Γ) = there (∈-imp-∈dom x,τ,A∈Γ)
------------------------------------------------------------------------------------

⊩bv-contr :  {A Γ τ k} -> Γ  bv {A} k  τ -> 
⊩bv-contr (~>∩ Γ⊩k Γ⊩k₁ x) = ⊩bv-contr Γ⊩k
------------------------------------------------------------------------------------

aux :  {A B Γ τ τ' x y k} {t} -> (m : Λ B) -> m ~T t ->
  y  dom Γ ->
  x  ΛFV m ->
  y  ΛFV m ->
  ¬(x  y) ->
  ((x , τ , A)  Γ)  Λ[ k >> fv {A} x ] m  τ' ->
  ((y , τ , A)  Γ)  Λ[ k >> fv {A} y ] m  τ'
auxₗ :  {A B Γ τ τ' x y k} {t} -> (m : Λ B) -> m ~T t ->
  y  dom Γ ->
  x  ΛFV m ->
  y  ΛFV m ->
  ¬(x  y) ->
  ((x , τ , A)  Γ) ⊩ₗ Λ[ k >> fv {A} x ] m  τ' ->
  ((y , τ , A)  Γ) ⊩ₗ Λ[ k >> fv {A} y ] m  τ'
aux {k = k} (bv i) l-bv y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ' with k  i
aux {A} {B} (bv k) l-bv y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ' | yes refl with B ≟T A
aux (bv k) l-bv y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ' | yes refl | yes refl =
  body y∉Γ x∷Γ⊩m^'x∶τ'

  where
  body :  {B Γ τ τ' x y} -> y  dom Γ -> ((x , τ , B)  Γ)  fv {B} x  τ' ->
    ((y , τ , B)  Γ)  fv {B} y  τ'
  body y∉ (var (cons x∉ x₁ wf-Γ) (here refl) τ⊆τᵢ) =
    var (cons y∉ x₁ wf-Γ) (here refl) τ⊆τᵢ
  body y∉ (var (cons x∉ x₁ wf-Γ) (there τᵢ∈Γ) τ⊆τᵢ) = ⊥-elim (∉-dom x∉ τᵢ∈Γ)
  body y∉ (~>∩ x∷Γ⊩x∶τ' x∷Γ⊩x∶τ'' x₁) =
    ~>∩ (body y∉ x∷Γ⊩x∶τ') (body y∉ x∷Γ⊩x∶τ'') x₁

aux (bv k) l-bv y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ' | yes refl | no ¬p =
  ⊥-elim (⊩bv-contr x∷Γ⊩m^'x∶τ')
aux (bv i) l-bv y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ' | no ¬p =
  ⊥-elim (⊩bv-contr x∷Γ⊩m^'x∶τ')
aux {A} {B} {Γ} {k = k} (fv x₁) l-fv y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ' =
  sub (strenghten-⊩ (ΛFV-^ {k = k} {A = A} {B} (fv x₁) x∉FVm x≠y) x∷Γ⊩m^'x∶τ')
      (⊆-refl (⊩-∷' x∷Γ⊩m^'x∶τ'))
      (⊆Γ-⊆ (cons y∉Γ (∷'' (⊩-wf-Γ x∷Γ⊩m^'x∶τ')) wf-Γ)  {x}  there))
  where
  cons' :  {x Γ} -> Wf-ICtxt (x  Γ) -> Wf-ICtxt Γ
  cons' (cons x∉ x₂ wf-Γ) = wf-Γ

  ∷'' :  {x τ B Γ} -> Wf-ICtxt ((x , τ , B)  Γ) -> τ ∷'ₗ B
  ∷'' (cons x∉ x₂ wf-Γ) = x₂

  wf-Γ : Wf-ICtxt Γ
  wf-Γ = cons' (⊩-wf-Γ x∷Γ⊩m^'x∶τ')

aux {A} {_} {Γ} {τ} {τ' ~> τ''} {x} {y} {k} (lam {B} A' m) (un m~Tt) y∉Γ x∉FVm y∉FVm x≠y (abs L cf) =
  abs (y  x  L) body
  where
  cf' :  {x'} -> x'  (x  L) ->
    ((x , τ , A)  (x' , τ' , A')  Γ) ⊩ₗ Λ[ suc k >> fv {A} x ](Λ[ 0 >> fv {A'} x' ] m)  τ''
  cf' {x'} x'∉L rewrite
    Λ^-^-swap {B} {A} {A'} (suc k) 0 x x' m  ())  x₂ -> fv-x≠y _ _ x'∉L (sym x₂)) =
    exchange-⊩ₗ (cf (∉-∷-elim _ x'∉L))

  ih' :  {x'} -> x'  (y  x  L) ->
    ((y , τ , A)  (x' , τ' , A')  Γ) ⊩ₗ Λ[ suc k >> fv {A} y ](Λ[ 0 >> fv {A'} x' ] m)  τ''
  ih' {x'} x'∉L = auxₗ
    (Λ[ 0 >> fv {A'} x' ] m)
    (^'-~T-inv m~Tt)
    (∉-∷ _ _  x₂  fv-x≠y _ _ x'∉L (sym x₂)) y∉Γ)
    (ΛFV-^ m x∉FVm  x₂  fv-x≠y _ _ (∉-∷-elim _ x'∉L) (sym x₂)))
    (ΛFV-^ m y∉FVm  x₂  fv-x≠y _ _ x'∉L (sym x₂)))
    x≠y
    (cf' (∉-∷-elim _ x'∉L))

  body :  {x'} -> x'  (y  x  L) ->
    ((x' , τ' , A')  (y , τ , A)  Γ) ⊩ₗ Λ[ 0 >> fv {A'} x' ](Λ[ suc k >> fv {A} y ] m)  τ''
  body {x'} x'∉L rewrite
    Λ^-^-swap {B} {A'} {A} 0 (suc k) x' y m  ())  x₂ -> fv-x≠y _ _ x'∉L x₂) =
    exchange-⊩ₗ (ih' x'∉L)

aux (lam A m) m~Tt y∉Γ x∉FVm y∉FVm x≠y (~>∩ x∷Γ⊩m^'x∶τ' x∷Γ⊩m^'x∶τ'' x₁) =
  ~>∩ (aux (lam A m) m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ')
      (aux (lam A m) m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ'')
      x₁
aux (app m m₁) (bin m~Tt m₁~Tt) y∉Γ x∉FVm y∉FVm x≠y (app x∷Γ⊩m^'x∶τ' x₁ x₂) =
  app (aux m m~Tt y∉Γ (∉-cons-l _ _ x∉FVm) (∉-cons-l _ _ y∉FVm) x≠y x∷Γ⊩m^'x∶τ')
      (auxₗ m₁ m₁~Tt y∉Γ (∉-cons-r (ΛFV m) _ x∉FVm) (∉-cons-r (ΛFV m) _ y∉FVm) x≠y x₁)
      x₂
aux (app m m₁) m~Tt y∉Γ x∉FVm y∉FVm x≠y (~>∩ x∷Γ⊩m^'x∶τ' x∷Γ⊩m^'x∶τ'' x₁) =
  ~>∩ (aux (app m m₁) m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ')
      (aux (app m m₁) m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ'')
      x₁
aux (Y _) l-Y y∉Γ x∉FVm y∉FVm x≠y(Y (cons x∉ x₁ wf-Γ) x₂ x₃) =
  Y (cons y∉Γ x₁ wf-Γ) x₂ x₃
aux {k = k} (Y B) m~Tt y∉Γ x∉FVm y∉FVm x≠y (~>∩ x∷Γ⊩m^'x∶τ' x∷Γ⊩m^'x∶τ'' x₁) =
  ~>∩ (aux {k = k} (Y B) m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ')
      (aux {k = k} (Y B) m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩m^'x∶τ'')
      x₁

auxₗ m m~Tt y∉Γ _ _ _ (nil (cons x∉ x₁ wf-Γ)) = nil (cons y∉Γ x₁ wf-Γ)
auxₗ m m~Tt y∉Γ x∉FVm y∉FVm x≠y (cons x₁ x∷Γ⊩ₗm^'x∶τ') =
  cons (aux m m~Tt y∉Γ x∉FVm y∉FVm x≠y x₁)
       (auxₗ m m~Tt y∉Γ x∉FVm y∉FVm x≠y x∷Γ⊩ₗm^'x∶τ')
------------------------------------------------------------------------------------

subst-⊩-2-aux :  {A B Γ τ x} {m : Λ A} {n : Λ B} {t} -> m ~T t ->
  ΛTerm m ->
  ΛTerm n ->
  x  dom Γ -> Γ  (m Λ[ x ::= n ])  τ ->
   τᵢ -> ( ((x , τᵢ , B)  Γ)  m  τ ) × ( Γ ⊩ₗ n  τᵢ ))
subst-⊩ₗ-2-aux :  {A B Γ τ x} {m : Λ A} {n : Λ B} {t} -> m ~T t ->
  ΛTerm m ->
  ΛTerm n ->
  x  dom Γ ->
  Γ ⊩ₗ (m Λ[ x ::= n ])  τ ->
   τᵢ -> ( ((x , τᵢ , B)  Γ) ⊩ₗ m  τ ) × ( Γ ⊩ₗ n  τᵢ ))

subst-⊩-2-aux {x = x} l-fv (var {x = y}) trm-n x∉Γ Γ⊩m[x::=n] with x  y
subst-⊩-2-aux {A} {B} l-fv var trm-n x∉Γ Γ⊩m[x::=n] | yes refl with A ≟T B
subst-⊩-2-aux {τ = τ} l-fv var trm-n x∉Γ Γ⊩m[x::=n] | yes refl | yes refl =
  (∩' τ) ,
  (var (cons x∉Γ (cons (⊩-∷' Γ⊩m[x::=n]) nil) (⊩-wf-Γ Γ⊩m[x::=n]))
       (here refl)
       (⊆ₗ-refl (cons (⊩-∷' Γ⊩m[x::=n]) nil)) ,
  (cons Γ⊩m[x::=n] (nil (⊩-wf-Γ Γ⊩m[x::=n]))))

subst-⊩-2-aux l-fv var trm-n x∉Γ Γ⊩m[x::=n] | yes refl | no _ =
  ⊥-elim (x∉Γ (contr Γ⊩m[x::=n]))

  where
  contr :  {Γ A x τ} -> Γ  (fv {A} x)  τ -> x  dom Γ
  contr (var wf-Γ τᵢ∈Γ τ⊆τᵢ) = ∈-imp-∈dom τᵢ∈Γ
  contr (~>∩ Γ⊩x∶τ Γ⊩x∶τ₁ x₁) = contr Γ⊩x∶τ₁

subst-⊩-2-aux {A} {B} l-fv var trm-n x∉Γ Γ⊩m[x::=n] | no _ =
  ω ,
  ((sub Γ⊩m[x::=n]
        (⊆-refl (⊩-∷' Γ⊩m[x::=n]))
        (⊆Γ-⊆ (cons x∉Γ nil (⊩-wf-Γ Γ⊩m[x::=n]))  {x₁}  there))) ,
  (nil (⊩-wf-Γ Γ⊩m[x::=n])))
subst-⊩-2-aux {A  B} {C} {Γ} {τ ~> τ'} {x} {_} {n} (un m~Tt) (lam L {m} cf) trm-n x∉Γ (abs L' cf') =
  τᵢ ,
  (abs
    (x'  x  dom Γ ++ ΛFV m)
     x∉ -> auxₗ
      m
      m~Tt
      (∉-∷ _
           (dom Γ)
            x₂  fv-x≠y _ _ (∉-∷-elim _ x∉) x₂)
           (∉-cons-l (dom Γ) _ (∉-∷-elim _ (∉-∷-elim _ x∉))))
      (∉-cons-l (ΛFV m)
                _
                (∉-cons-r (dom Γ) _ (∉-cons-r L' _ (∉-cons-r L _ (∉-∷-elim _ x'∉)))))
      (∉-cons-r (dom Γ) _ (∉-∷-elim _ (∉-∷-elim _ x∉)))
       x₂  fv-x≠y _ _ x∉ (sym x₂))
      x∷x'∷Γ⊩ₗm^x∶τ')) ,
  Γ⊩ₗn∶τᵢ
  where
  x' = ∃fresh-impl (x  L ++ L' ++ dom Γ ++ ΛFV m ++ ΛFV n)

  x'∉ : x'  (x  L ++ L' ++ dom Γ ++ ΛFV m ++ ΛFV n)
  x'∉ = ∃fresh-impl-spec (x  L ++ L' ++ dom Γ ++ ΛFV m ++ ΛFV n)

  ih' : ((x' , τ , A)  Γ) ⊩ₗ (Λ[ 0 >> fv {A} x' ] m) Λ[ x ::= n ]  τ'
  ih' rewrite Λsubst-open-var2 {τ'' = A} x' x n m (fv-x≠y _ _ x'∉) trm-n =
    cf' (∉-cons-l L' _ (∉-cons-r L _ (∉-∷-elim _ x'∉)))

  ih :  τᵢ₁ ->
    (((x , τᵢ₁ , C)  (x' , τ , A)  Γ) ⊩ₗ Λ[ 0 >> fv x' ] m  τ') × (((x' , τ , A)  Γ) ⊩ₗ n  τᵢ₁))
  ih = subst-⊩ₗ-2-aux (^'-~T-inv m~Tt)
                      (cf (∉-cons-l L _ (∉-∷-elim _ x'∉)))
                      trm-n
                      (∉-∷ _ (dom Γ)  x₂ -> fv-x≠y _ _ x'∉ (sym x₂)) x∉Γ)
                      ih'

  τᵢ = proj₁ ih

  x∷x'∷Γ⊩ₗm^x∶τ' : ((x' , τ , A)  (x , τᵢ , C)  Γ) ⊩ₗ Λ[ 0 >> fv x' ] m  τ'
  x∷x'∷Γ⊩ₗm^x∶τ' = exchange-⊩ₗ (proj₁ (proj₂ ih))

  Γ⊩ₗn∶τᵢ : Γ ⊩ₗ n  τᵢ
  Γ⊩ₗn∶τᵢ = strenghten-⊩ₗ
    (∉-cons-r (ΛFV m) _
      (∉-cons-r (dom Γ) _ (∉-cons-r L' _ (∉-cons-r L _ (∉-∷-elim _ x'∉)))))
    (proj₂ (proj₂ ih))

subst-⊩-2-aux {B = B} {Γ} {x = x} m~Tt (lam L {e = m} cf) trm-n x∉Γ (~>∩ Γ⊩m[x::=n] Γ⊩m[x::=n]₁ x₁) = τₗ ++ τᵣ ,
  sub
    (~>∩
      (sub {Γ' = ((x , τₗ ++ τᵣ , B)  Γ)} x∷Γ⊩m∶τₗ (⊆-refl (⊩-∷' Γ⊩m[x::=n])) ⊆Γₗ)
      (sub x∷Γ⊩m∶τᵣ (⊆-refl (⊩-∷' Γ⊩m[x::=n]₁)) ⊆Γᵣ)
      (⊆ₗ-refl (++-∷'ₗ (proj₂ (arr' (⊩-∷' Γ⊩m[x::=n])))
                       (proj₂ (arr' (⊩-∷' Γ⊩m[x::=n]₁))))))
    (arr (⊆ₗ-refl τ∷) x₁)
    (⊆Γ-⊆ wf-x∷Γ  x₃  x₃)) ,
  ⊩ₗ-++ Γ⊩n∶τₗ Γ⊩n∶τᵣ
  where
  ihₗ = subst-⊩-2-aux m~Tt (lam L {e = m} cf) trm-n x∉Γ Γ⊩m[x::=n]
  ihᵣ = subst-⊩-2-aux m~Tt (lam L {e = m} cf) trm-n x∉Γ Γ⊩m[x::=n]₁
  τₗ = proj₁ ihₗ
  τᵣ = proj₁ ihᵣ
  x∷Γ⊩m∶τₗ = proj₁ (proj₂ ihₗ)
  x∷Γ⊩m∶τᵣ = proj₁ (proj₂ ihᵣ)
  Γ⊩n∶τₗ = proj₂ (proj₂ ihₗ)
  Γ⊩n∶τᵣ = proj₂ (proj₂ ihᵣ)

  τₗ++τᵣ∷' : (τₗ ++ τᵣ) ∷'ₗ B
  τₗ++τᵣ∷' = ++-∷'ₗ (⊩ₗ-∷'ₗ Γ⊩n∶τₗ) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)

  wf-x∷Γ : Wf-ICtxt ((x , τₗ ++ τᵣ , B)  Γ)
  wf-x∷Γ = cons x∉Γ τₗ++τᵣ∷' (⊩-wf-Γ Γ⊩m[x::=n]₁)

  ⊆Γₗ : ((x , τₗ , B)  Γ) ⊆Γ ((x , τₗ ++ τᵣ , B)  Γ)
  ⊆Γₗ = cons
    ((τₗ ++ τᵣ) , ((here refl) , ⊆ₗ++-l' (⊩ₗ-∷'ₗ Γ⊩n∶τₗ) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

  ⊆Γᵣ : ((x , τᵣ , B)  Γ) ⊆Γ ((x , τₗ ++ τᵣ , B)  Γ)
  ⊆Γᵣ = cons
    ((τₗ ++ τᵣ) , ((here refl) , ⊆ₗ++-r (nil (⊩ₗ-∷'ₗ Γ⊩n∶τₗ)) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

  τ∷ = proj₁ (arr' (⊩-∷' Γ⊩m[x::=n]₁))

subst-⊩-2-aux {B = B} {Γ} {x = x} (bin m~Tt m₁~Tt) (app trm-m trm-m₁) trm-n x∉Γ (app Γ⊩e₁[x::=n] Γ⊩ₗe₂[x::=n] x₂) =
  τₗ ++ τᵣ ,
  (app (sub x∷Γ⊩m∶τₗ (⊆-refl (⊩-∷' Γ⊩e₁[x::=n])) ⊆Γₗ)
       (subₗ x∷Γ⊩m∶τᵣ (⊆ₗ-refl (⊩ₗ-∷'ₗ Γ⊩ₗe₂[x::=n])) ⊆Γᵣ)
       x₂) ,
  (⊩ₗ-++ Γ⊩n∶τₗ Γ⊩n∶τᵣ)
  where
  ihₗ = subst-⊩-2-aux m~Tt trm-m trm-n x∉Γ Γ⊩e₁[x::=n]
  ihᵣ = subst-⊩ₗ-2-aux m₁~Tt trm-m₁ trm-n x∉Γ Γ⊩ₗe₂[x::=n]
  τₗ = proj₁ ihₗ
  τᵣ = proj₁ ihᵣ
  x∷Γ⊩m∶τₗ = proj₁ (proj₂ ihₗ)
  x∷Γ⊩m∶τᵣ = proj₁ (proj₂ ihᵣ)
  Γ⊩n∶τₗ = proj₂ (proj₂ ihₗ)
  Γ⊩n∶τᵣ = proj₂ (proj₂ ihᵣ)

  τₗ++τᵣ∷' : (τₗ ++ τᵣ) ∷'ₗ B
  τₗ++τᵣ∷' = ++-∷'ₗ (⊩ₗ-∷'ₗ Γ⊩n∶τₗ) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)

  wf-x∷Γ : Wf-ICtxt ((x , τₗ ++ τᵣ , B)  Γ)
  wf-x∷Γ = cons x∉Γ τₗ++τᵣ∷' (⊩-wf-Γ Γ⊩e₁[x::=n])

  ⊆Γₗ : ((x , τₗ , B)  Γ) ⊆Γ ((x , τₗ ++ τᵣ , B)  Γ)
  ⊆Γₗ = cons
    ((τₗ ++ τᵣ) , ((here refl) , ⊆ₗ++-l' (⊩ₗ-∷'ₗ Γ⊩n∶τₗ) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

  ⊆Γᵣ : ((x , τᵣ , B)  Γ) ⊆Γ ((x , τₗ ++ τᵣ , B)  Γ)
  ⊆Γᵣ = cons
    ((τₗ ++ τᵣ) , ((here refl) , ⊆ₗ++-r (nil (⊩ₗ-∷'ₗ Γ⊩n∶τₗ)) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

subst-⊩-2-aux {B = B} {Γ} {x = x} m~Tt (app trm-m trm-m₁) trm-n x∉Γ (~>∩ Γ⊩m[x::=n] Γ⊩m[x::=n]₁ x₁) =
  τₗ ++ τᵣ ,
  sub
    (~>∩
      (sub {Γ' = ((x , τₗ ++ τᵣ , B)  Γ)} x∷Γ⊩m∶τₗ (⊆-refl (⊩-∷' Γ⊩m[x::=n])) ⊆Γₗ)
      (sub x∷Γ⊩m∶τᵣ (⊆-refl (⊩-∷' Γ⊩m[x::=n]₁)) ⊆Γᵣ)
      (⊆ₗ-refl (++-∷'ₗ (proj₂ (arr' (⊩-∷' Γ⊩m[x::=n])))
                       (proj₂ (arr' (⊩-∷' Γ⊩m[x::=n]₁))))))
    (arr (⊆ₗ-refl τ∷) x₁)
    (⊆Γ-⊆ wf-x∷Γ  x₃  x₃)) ,
  ⊩ₗ-++ Γ⊩n∶τₗ Γ⊩n∶τᵣ
  where
  ihₗ = subst-⊩-2-aux m~Tt (app trm-m trm-m₁) trm-n x∉Γ Γ⊩m[x::=n]
  ihᵣ = subst-⊩-2-aux m~Tt (app trm-m trm-m₁) trm-n x∉Γ Γ⊩m[x::=n]₁
  τₗ = proj₁ ihₗ
  τᵣ = proj₁ ihᵣ
  x∷Γ⊩m∶τₗ = proj₁ (proj₂ ihₗ)
  x∷Γ⊩m∶τᵣ = proj₁ (proj₂ ihᵣ)
  Γ⊩n∶τₗ = proj₂ (proj₂ ihₗ)
  Γ⊩n∶τᵣ = proj₂ (proj₂ ihᵣ)

  τₗ++τᵣ∷' : (τₗ ++ τᵣ) ∷'ₗ B
  τₗ++τᵣ∷' = ++-∷'ₗ (⊩ₗ-∷'ₗ Γ⊩n∶τₗ) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)

  wf-x∷Γ : Wf-ICtxt ((x , τₗ ++ τᵣ , B)  Γ)
  wf-x∷Γ = cons x∉Γ τₗ++τᵣ∷' (⊩-wf-Γ Γ⊩m[x::=n]₁)

  ⊆Γₗ : ((x , τₗ , B)  Γ) ⊆Γ ((x , τₗ ++ τᵣ , B)  Γ)
  ⊆Γₗ = cons ((τₗ ++ τᵣ) , ((here refl) , ⊆ₗ++-l' (⊩ₗ-∷'ₗ Γ⊩n∶τₗ) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ))) (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

  ⊆Γᵣ : ((x , τᵣ , B)  Γ) ⊆Γ ((x , τₗ ++ τᵣ , B)  Γ)
  ⊆Γᵣ = cons
    ((τₗ ++ τᵣ) , ((here refl) , ⊆ₗ++-r (nil (⊩ₗ-∷'ₗ Γ⊩n∶τₗ)) (⊩ₗ-∷'ₗ Γ⊩n∶τᵣ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

  τ∷ = proj₁ (arr' (⊩-∷' Γ⊩m[x::=n]₁))

subst-⊩-2-aux l-Y Y trm-n x∉Γ Γ⊩m[x::=n] =
  ω ,
  (sub Γ⊩m[x::=n]
       (⊆-refl (⊩-∷' Γ⊩m[x::=n]))
       (⊆Γ-⊆ (cons x∉Γ nil (⊩-wf-Γ Γ⊩m[x::=n]))  {x}  there))) ,
  (nil (⊩-wf-Γ Γ⊩m[x::=n]))

subst-⊩ₗ-2-aux m~Tt trm-m trm-n x∉Γ (nil wf-Γ) =
  ω , ((nil (cons x∉Γ nil wf-Γ)) , (nil wf-Γ))
subst-⊩ₗ-2-aux {B = B} {Γ} {x = x} m~Tt trm-m trm-n x∉Γ (cons x' Γ⊩ₗm∶τ) =
  τ ++ τₗ ,
  (cons (sub x∷Γ⊩m∶τ (⊆-refl (⊩-∷' x')) ⊆Γ')
        (subₗ x∷Γ⊩m∶τₗ (⊆ₗ-refl (⊩ₗ-∷'ₗ Γ⊩ₗm∶τ)) ⊆Γₗ)) ,
  ⊩ₗ-++ Γ⊩n∶τ Γ⊩n∶τₗ

  where
  ih = subst-⊩-2-aux m~Tt trm-m trm-n x∉Γ x'
  ihₗ = subst-⊩ₗ-2-aux m~Tt trm-m trm-n x∉Γ Γ⊩ₗm∶τ
  τ = proj₁ ih
  τₗ = proj₁ ihₗ
  x∷Γ⊩m∶τ = proj₁ (proj₂ ih)
  x∷Γ⊩m∶τₗ = proj₁ (proj₂ ihₗ)
  Γ⊩n∶τ = proj₂ (proj₂ ih)
  Γ⊩n∶τₗ = proj₂ (proj₂ ihₗ)

  τ++τₗ∷' : (τ ++ τₗ) ∷'ₗ B
  τ++τₗ∷' = ++-∷'ₗ (⊩ₗ-∷'ₗ Γ⊩n∶τ) (⊩ₗ-∷'ₗ Γ⊩n∶τₗ)

  wf-x∷Γ : Wf-ICtxt ((x , τ ++ τₗ , B)  Γ)
  wf-x∷Γ = cons x∉Γ τ++τₗ∷' (⊩-wf-Γ x')

  ⊆Γ' : ((x , τ , B)  Γ) ⊆Γ ((x , τ ++ τₗ , B)  Γ)
  ⊆Γ' = cons
    ((τ ++ τₗ) , ((here refl) , ⊆ₗ++-l' (⊩ₗ-∷'ₗ Γ⊩n∶τ) (⊩ₗ-∷'ₗ Γ⊩n∶τₗ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))

  ⊆Γₗ : ((x , τₗ , B)  Γ) ⊆Γ ((x , τ ++ τₗ , B)  Γ)
  ⊆Γₗ = cons
    ((τ ++ τₗ) , ((here refl) , ⊆ₗ++-r (nil (⊩ₗ-∷'ₗ Γ⊩n∶τ)) (⊩ₗ-∷'ₗ Γ⊩n∶τₗ)))
    (⊆Γ-⊆ wf-x∷Γ  x₃  there x₃))
------------------------------------------------------------------------------------

subst-⊩-2 :  {A B Γ τ x} {m : Λ A} {n : Λ B} -> ΛTerm m -> ΛTerm n -> x  dom Γ ->
  Γ  (m Λ[ x ::= n ])  τ ->
   τᵢ -> ( ((x , τᵢ , B)  Γ)  m  τ ) × ( Γ ⊩ₗ n  τᵢ ))
subst-⊩-2 {m = m} trm-m trm-n x∉Γ Γ⊩m[x::=n] =
  subst-⊩-2-aux (proj₂ (∃~T m)) trm-m trm-n x∉Γ Γ⊩m[x::=n]
------------------------------------------------------------------------------------

subst-⊩ₗ-2 :  {A B Γ τ x} {m : Λ A} {n : Λ B} ->
  ΛTerm m ->
  ΛTerm n ->
  x  dom Γ ->
  Γ ⊩ₗ (m Λ[ x ::= n ])  τ ->
   τᵢ -> ( ((x , τᵢ , B)  Γ) ⊩ₗ m  τ ) × ( Γ ⊩ₗ n  τᵢ ))
subst-⊩ₗ-2 {m = m} trm-m trm-n x∉Γ Γ⊩ₗm[x::=n] =
  subst-⊩ₗ-2-aux (proj₂ (∃~T m)) trm-m trm-n x∉Γ Γ⊩ₗm[x::=n]