{-# OPTIONS --without-K --safe #-}
module Cats.Category.Cat where

open import Cats.Functor public using (Functor ; _∘_ ; id)

open import Data.Product using (_,_)
open import Data.Unit using ( ; tt)
open import Level
open import Relation.Binary using
  (IsEquivalence ; _Preserves_⟶_ ; _Preserves₂_⟶_⟶_)
open import Relation.Binary.PropositionalEquality as  using (_≡_)

open import Cats.Category.Base
open import Cats.Category.Zero
open import Cats.Category.One
open import Cats.Trans.Iso as NatIso using (NatIso ; iso ; forth-natural)

import Cats.Category.Constructions.Iso as Iso

open Functor
open Iso.Build._≅_


private
  module  {lo la l≈} (C : Category lo la l≈) = Iso.Build.≅ C


_⇒_ :  {lo la l≈ lo′ la′ l≈′}
   Category lo la l≈  Category lo′ la′ l≈′  Set _
C  D = Functor C D


module _ {lo la l≈ lo′ la′ l≈′}
  {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
  where

  infixr 4 _≈_


  _≈_ : (F G : C  D)  Set (lo  la  lo′  la′  l≈′)
  F  G = NatIso F G


  equiv : IsEquivalence _≈_
  equiv = record
      { refl = NatIso.id
      ; sym = NatIso.sym
      ; trans = λ eq₁ eq₂  eq₂ NatIso.∘ eq₁
      }


module _ {lo la l≈ lo′ la′ l≈′}
  {C : Category lo la l≈} {D : Category lo′ la′ l≈′}
  where

  ∘-resp :  {lo″ la″ l≈″} {E : Category lo″ la″ l≈″}
     {F G : D  E} {H I : C  D}
     F  G
     H  I
     F  H  G  I
  ∘-resp {E = E} {F} {G} {H} {I}
    record { iso = F≅G ; forth-natural = fnat-GH }
    record { iso = H≅I ; forth-natural = fnat-HI }
      = record
      { iso = ≅.trans E F≅G (fobj-resp G H≅I)
      ; forth-natural = λ {_} {_} {f} 
          begin
            (fmap G (forth H≅I) E.∘ forth F≅G) E.∘ fmap F (fmap H f)
          ≈⟨ E.assoc 
            fmap G (forth H≅I) E.∘ forth F≅G E.∘ fmap F (fmap H f)
          ≈⟨ E.∘-resp-r fnat-GH 
            fmap G (forth H≅I) E.∘ fmap G (fmap H f) E.∘ forth F≅G
          ≈⟨ E.unassoc 
            (fmap G (forth H≅I) E.∘ fmap G (fmap H f)) E.∘ forth F≅G
          ≈⟨ E.∘-resp-l (fmap-∘ G) 
            fmap G (forth H≅I D.∘ fmap H f) E.∘ forth F≅G
          ≈⟨ E.∘-resp-l (fmap-resp G fnat-HI) 
            fmap G (fmap I f D.∘ forth H≅I) E.∘ forth F≅G
          ≈⟨ E.∘-resp-l (E.≈.sym (fmap-∘ G)) 
            (fmap G (fmap I f) E.∘ fmap G (forth H≅I)) E.∘ forth F≅G
          ≈⟨ E.assoc 
            fmap G (fmap I f) E.∘ fmap G (forth H≅I) E.∘ forth F≅G
          
      }
    where
      module D = Category D
      module E = Category E
      open E.≈-Reasoning


  id-r : {F : C  D}  F  id  F
  id-r {F} = record
      { iso = ≅.refl D
      ; forth-natural = D.≈.trans D.id-l (D.≈.sym D.id-r)
      }
    where
      module D = Category D


  id-l : {F : C  D}  id  F  F
  id-l {F} = record
      { iso = ≅.refl D
      ; forth-natural = D.≈.trans D.id-l (D.≈.sym D.id-r)
      }
    where
      module D = Category D


  assoc :  {lo″ la″ l≈″ lo‴ la‴ l≈‴}
     {E : Category lo″ la″ l≈″} {X : Category lo‴ la‴ l≈‴}
     (F : E  X) (G : D  E) (H : C  D)
     (F  G)  H  F  (G  H)
  assoc {E = E} {X} _ _ _ = record
      { iso = ≅.refl X
      ; forth-natural = X.≈.trans X.id-l (X.≈.sym X.id-r)
      }
    where
      module X = Category X


Cat :  lo la l≈
   Category (suc (lo  la  l≈)) (lo  la  l≈) (lo  la  l≈)
Cat lo la l≈ = record
    { Obj = Category lo la l≈
    ; _⇒_ = _⇒_
    ; _≈_ = _≈_
    ; id = id
    ; _∘_ = _∘_
    ; equiv = equiv
    ; ∘-resp = ∘-resp
    ; id-r = id-r
    ; id-l = id-l
    ; assoc = λ {_} {_} {_} {_} {F} {G} {H}  assoc F G H
    }