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

open import Categories.Category.Core using (Category)
open import Categories.Category.Monoidal.Core using (Monoidal)
open import Categories.Category.Monoidal.Interchange using (HasInterchange)

-- The tensor product of enriched categories.  Composition exchanges the two
-- middle hom objects once, then composes independently in each factor.

module Categories.Enriched.Category.TensorProduct
  {o  e} {V : Category o  e} {M : Monoidal V} (I : HasInterchange M) where

open import Level using (_⊔_)
open import Data.Product using (_×_; _,_; proj₁; proj₂)

open Category V renaming (id to idV)
open Monoidal M
open HasInterchange I using (module swapInner)
  renaming (natural to int-natural; assoc to int-assoc; unitˡ to int-unitˡ; unitʳ to int-unitʳ)
open import Categories.Category.Monoidal.Reasoning M
open import Categories.Category.Monoidal.Utilities M
open import Categories.Morphism.Reasoning V
import Categories.Enriched.Category as Enriched
open Shorthands

private
  i⇒ = swapInner.from

module _ {a b} (𝒜 : Enriched.Category M a) ( : Enriched.Category M b) where

  infixr 7 _⊠_

  private
    module 𝒜 = Enriched.Category 𝒜
    module  = Enriched.Category 

    variable
      A B C Z : 𝒜.Obj × ℬ.Obj -- source, intermediate, and target pairs

    hom : 𝒜.Obj × ℬ.Obj  𝒜.Obj × ℬ.Obj  Category.Obj V
    hom A B = 𝒜.hom (proj₁ A) (proj₁ B) ⊗₀ ℬ.hom (proj₂ A) (proj₂ B)

    id× : unit  hom A A
    id× = (𝒜.id ⊗₁ ℬ.id)  λ⇐

    ⊚× : hom B C ⊗₀ hom A B  hom A C
    ⊚× = (𝒜.⊚ ⊗₁ ℬ.⊚)  i⇒

  private abstract
    ⊚×-assoc : ⊚× {B} {Z} {A}  (⊚× {C} ⊗₁ idV)
                ⊚×  (idV ⊗₁ ⊚×)  α⇒
    ⊚×-assoc = begin
      ⊚×  (⊚× ⊗₁ idV)                                   ≈⟨ refl⟩∘⟨ split₁ˡ 
      ⊚×  ((𝒜.⊚ ⊗₁ ℬ.⊚) ⊗₁ idV)  (i⇒ ⊗₁ idV)          ≈˘⟨ refl⟩∘⟨ refl⟩⊗⟨ ⊗.identity ⟩∘⟨refl 
      ⊚×  ((𝒜.⊚ ⊗₁ ℬ.⊚) ⊗₁ (idV ⊗₁ idV))  (i⇒ ⊗₁ idV) ≈⟨ extend² int-natural 
      ((𝒜.⊚ ⊗₁ ℬ.⊚)  ((𝒜.⊚ ⊗₁ idV) ⊗₁ (ℬ.⊚ ⊗₁ idV)))
         (i⇒  (i⇒ ⊗₁ idV))
        ≈⟨ parallel 𝒜.⊚-assoc ℬ.⊚-assoc ⟩∘⟨refl 
      ((𝒜.⊚ ⊗₁ ℬ.⊚) 
        (((idV ⊗₁ 𝒜.⊚)  α⇒) ⊗₁ ((idV ⊗₁ ℬ.⊚)  α⇒)))
         (i⇒  (i⇒ ⊗₁ idV))
        ≈⟨ pullʳ (pushˡ ⊗.homomorphism) 
      (𝒜.⊚ ⊗₁ ℬ.⊚) 
        ((idV ⊗₁ 𝒜.⊚) ⊗₁ (idV ⊗₁ ℬ.⊚)) 
        (α⇒ ⊗₁ α⇒)  i⇒  (i⇒ ⊗₁ idV)
        ≈⟨ refl⟩∘⟨ refl⟩∘⟨ int-assoc 
      (𝒜.⊚ ⊗₁ ℬ.⊚) 
        ((idV ⊗₁ 𝒜.⊚) ⊗₁ (idV ⊗₁ ℬ.⊚)) 
        i⇒  (idV ⊗₁ i⇒)  α⇒
        ≈˘⟨ refl⟩∘⟨ extendʳ int-natural 
      (𝒜.⊚ ⊗₁ ℬ.⊚)  i⇒ 
        ((idV ⊗₁ idV) ⊗₁ (𝒜.⊚ ⊗₁ ℬ.⊚))  (idV ⊗₁ i⇒)  α⇒
        ≈⟨ refl⟩∘⟨ refl⟩∘⟨ pullˡ merge₂ʳ 
      (𝒜.⊚ ⊗₁ ℬ.⊚)  i⇒  ((idV ⊗₁ idV) ⊗₁ ⊚×)  α⇒
        ≈⟨ pushʳ (refl⟩∘⟨ ⊗.identity ⟩⊗⟨refl ⟩∘⟨refl) 
      ⊚×  (idV ⊗₁ ⊚×)  α⇒  

    ⊚×-unitˡ : ⊚× {B} {B} {A}  (id× ⊗₁ idV)  λ⇒
    ⊚×-unitˡ = begin
      ⊚×  (id× ⊗₁ idV)                                   ≈⟨ refl⟩∘⟨ split₁ˡ 
      ⊚×  ((𝒜.id ⊗₁ ℬ.id) ⊗₁ idV)  (λ⇐ ⊗₁ idV)          ≈˘⟨ refl⟩∘⟨ refl⟩⊗⟨ ⊗.identity ⟩∘⟨refl 
      ⊚×  ((𝒜.id ⊗₁ ℬ.id) ⊗₁ (idV ⊗₁ idV))  (λ⇐ ⊗₁ idV) ≈⟨ extend² int-natural 
      ((𝒜.⊚ ⊗₁ ℬ.⊚)  ((𝒜.id ⊗₁ idV) ⊗₁ (ℬ.id ⊗₁ idV)))
         (i⇒  (λ⇐ ⊗₁ idV))
        ≈˘⟨ ⊗.homomorphism ⟩∘⟨refl 
      ((𝒜.⊚  (𝒜.id ⊗₁ idV)) ⊗₁ (ℬ.⊚  (ℬ.id ⊗₁ idV)))
         (i⇒  (λ⇐ ⊗₁ idV))
        ≈⟨ 𝒜.unitˡ ⟩⊗⟨ ℬ.unitˡ ⟩∘⟨refl 
      (λ⇒ ⊗₁ λ⇒)  i⇒  (λ⇐ ⊗₁ idV)                       ≈⟨ int-unitˡ 
      λ⇒                                                  

    ⊚×-unitʳ : ⊚× {A} {B}  (idV ⊗₁ id×)  ρ⇒
    ⊚×-unitʳ = begin
      ⊚×  (idV ⊗₁ id×)                                   ≈⟨ refl⟩∘⟨ split₂ˡ 
      ⊚×  (idV ⊗₁ (𝒜.id ⊗₁ ℬ.id))  (idV ⊗₁ λ⇐)          ≈˘⟨ refl⟩∘⟨ ⊗.identity ⟩⊗⟨refl ⟩∘⟨refl 
      ⊚×  ((idV ⊗₁ idV) ⊗₁ (𝒜.id ⊗₁ ℬ.id))  (idV ⊗₁ λ⇐) ≈⟨ extend² int-natural 
      ((𝒜.⊚ ⊗₁ ℬ.⊚)  ((idV ⊗₁ 𝒜.id) ⊗₁ (idV ⊗₁ ℬ.id)))
         (i⇒  (idV ⊗₁ λ⇐))
        ≈˘⟨ ⊗.homomorphism ⟩∘⟨refl 
      ((𝒜.⊚  (idV ⊗₁ 𝒜.id)) ⊗₁ (ℬ.⊚  (idV ⊗₁ ℬ.id)))
         (i⇒  (idV ⊗₁ λ⇐))
        ≈⟨ 𝒜.unitʳ ⟩⊗⟨ ℬ.unitʳ ⟩∘⟨refl 
      (ρ⇒ ⊗₁ ρ⇒)  i⇒  (idV ⊗₁ λ⇐)  ≈⟨ int-unitʳ 
      ρ⇒                                                       

  _⊠_ : Enriched.Category M (a  b)
  _⊠_ = record
    { Obj = 𝒜.Obj × ℬ.Obj
    ; hom = hom
    ; id = id×
    ;  = ⊚×
    ; ⊚-assoc = ⊚×-assoc
    ; unitˡ = ⊚×-unitˡ
    ; unitʳ = ⊚×-unitʳ
    }