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

open import Categories.Category

module Categories.Category.Complete.Properties.Construction {o  e} (C : Category o  e) where

open import Level
open import Data.Product using (∃₂; _,_; -,_)

open import Categories.Category.Complete
open import Categories.Diagram.Equalizer C
open import Categories.Diagram.Limit as Lim
open import Categories.Object.Product.Indexed C
open import Categories.Functor

import Categories.Category.Construction.Cones as Co
import Categories.Morphism.Reasoning as MR

private
  variable
    o′ ℓ′ e′ o″ ℓ″ e″ : Level
  module C = Category C

module _ (prods : AllProductsOf (o′  ℓ′)) (equalizer :  {A B} (f g : A C.⇒ B)  Equalizer f g) where
  private
    module Prods {I} (P : I  C.Obj) = IndexedProductOf (prods P)
    open C.HomReasoning

    module _ {J : Category o′ ℓ′ e′} (F : Functor J C) where
      private
        module J  = Category J
        open Functor F
        open MR C
        module OP = Prods {Lift ℓ′ J.Obj}  j  F₀ (lower j))
        module MP = Prods {∃₂ J._⇒_}  { (_ , B , _)  F₀ B })

      src : C.Obj
      src = OP.X

      dst : C.Obj
      dst = MP.X

      ϕ⇒ : (i : ∃₂ J._⇒_)  let (_ , B , _) = i in src C.⇒ F₀ B
      ϕ⇒ (_ , B , _) = OP.π (lift B)

      ϕ : src C.⇒ dst
      ϕ = MP.⟨ ϕ⇒ 

      ψ⇒ : (i : ∃₂ J._⇒_)  let (_ , B , _) = i in src C.⇒ F₀ B
      ψ⇒ (A , B , f) = F₁ f C.∘ OP.π (lift A)

      ψ : src C.⇒ dst
      ψ = MP.⟨ ψ⇒ 

      module eq = Equalizer (equalizer ϕ ψ)

       : Co.Cone F
       = record
        { N    = eq.obj
        ; apex = record
          { ψ       = λ X  OP.π (lift X) C.∘ eq.arr
          ; commute = λ {X Y} f  begin
            F₁ f C.∘ OP.π (lift X) C.∘ eq.arr ≈˘⟨ pushˡ (MP.commute ψ⇒ _) 
            (MP.π (-, -, f) C.∘ ψ) C.∘ eq.arr ≈˘⟨ pushʳ eq.equality 
            MP.π (-, -, f) C.∘ ϕ C.∘ eq.arr   ≈⟨ pullˡ (MP.commute ϕ⇒ _ ) 
            OP.π (lift Y) C.∘ eq.arr          
          }
        }

      module _ {K : Co.Cone F} where
        private
          module K = Co.Cone F K

        K⇒ : K.N C.⇒ src
        K⇒ = OP.⟨  j  K.ψ (lower j)) 

        Keq : (i : ∃₂ J._⇒_)  ϕ⇒ i C.∘ K⇒ C.≈ ψ⇒ i C.∘ K⇒
        Keq i@(A , B , f) = begin
          ϕ⇒ i C.∘ K⇒    ≈⟨ OP.commute _ _ 
          K.ψ B          ≈˘⟨ K.commute _ 
          F₁ f C.∘ K.ψ A ≈˘⟨ pullʳ (OP.commute _ _) 
          ψ⇒ i C.∘ K⇒    

        !-eq : ϕ C.∘ K⇒ C.≈ ψ C.∘ K⇒
        !-eq = begin
          ϕ C.∘ K⇒                   ≈⟨ MP.⟨⟩∘ _ _ 
          MP.⟨  i  ϕ⇒ i C.∘ K⇒)  ≈⟨ MP.⟨⟩-cong _ _ Keq 
          MP.⟨  i  ψ⇒ i C.∘ K⇒)  ≈˘⟨ MP.⟨⟩∘ _ _ 
          ψ C.∘ K⇒                   

        ! : Co.Cones F [ K ,  ]
        ! = record
          { arr     = eq.equalize {h = K⇒} !-eq
          ; commute = λ {j}  begin
            (OP.π (lift j) C.∘ eq.arr) C.∘ eq.equalize !-eq ≈˘⟨ pushʳ eq.universal 
            OP.π (lift j) C.∘ K⇒                            ≈⟨ OP.commute _ _ 
            K.ψ j                                           
          }

        !-unique : (f : Co.Cones F [ K ,  ])  Co.Cones F [ !  f ]
        !-unique f =  (eq.unique eq)
          where module f = Co.Cone⇒ F f
                eq : K⇒ C.≈ eq.arr C.∘ f.arr
                eq = OP.unique′ _ _ λ i  begin
                  OP.π i C.∘ K⇒                 ≈⟨ OP.commute _ _ 
                  K.ψ (lower i)                 ≈˘⟨ f.commute 
                  (OP.π i C.∘ eq.arr) C.∘ f.arr ≈⟨ C.assoc 
                  OP.π i C.∘ eq.arr C.∘ f.arr   

      complete : Limit F
      complete = record
        { terminal = record
          {         = 
          ; ⊤-is-terminal = record
            { !        = !
            ; !-unique = !-unique
            }
          }
        }

  AllProducts×Equalizer⇒Complete : Complete o′ ℓ′ e′ C
  AllProducts×Equalizer⇒Complete = complete