{-# OPTIONS --safe --lossy-unification #-}
module Cubical.Tactics.CategorySolver.Solver where

open import Cubical.Foundations.Prelude

open import Cubical.Data.Quiver.Base
open import Cubical.Categories.Category
open import Cubical.Categories.Constructions.Free.Category.Quiver as FreeCat
open import Cubical.Categories.Constructions.Power
open import Cubical.Categories.Functor.Base
open import Cubical.Categories.Instances.Sets
open import Cubical.Categories.UnderlyingGraph

private
  variable
    β„“ β„“' : Level
open Category
open Functor

module Eval (𝓒 : Category β„“ β„“') where
  -- Semantics in 𝓒 itself, tautologically
  private
    W𝓒 = FreeCat (Catβ†’Quiver 𝓒)
  sem𝓒 = Ξ΅ {𝓒 = 𝓒}
  ⟦_⟧c = sem𝓒 .F-hom
  π“Ÿ = PowerCategory (W𝓒 .ob) (SET (β„“-max β„“ β„“'))
  π“˜ : Functor W𝓒 π“Ÿ
  π“˜ = PseudoYoneda {C = W𝓒}

  -- Semantics in π“Ÿ (𝓒 .ob), interpreting fun symbols using Yoneda
  semYo : Functor W𝓒 π“Ÿ
  semYo = rec _ (record {
    _$g_ = π“˜ .F-ob
    ; _<$g>_ = Ξ» e β†’ π“˜ .F-hom (FreeCat.Ξ· (Catβ†’Quiver 𝓒) <$g> e)
    })

  -- | Evaluate by taking the semantics in π“Ÿ (𝓒 .ob)
  eval : βˆ€ {A B} β†’ W𝓒 [ A , B ] β†’ _
  eval {A}{B} e = semYo .F-hom e

--   -- Evaluation agrees with the Yoneda embedding, and so is fully faithful
  Yo-YoSem-agree : π“˜ ≑ semYo
  Yo-YoSem-agree = FreeCatFunctor≑ _ _ _ (record
    { _$gα΄°_ = Ξ» _ β†’ refl
    ; _<$g>α΄°_ = Ξ» _ β†’ refl
    })

  -- If two expressions in the free category are equal when evaluated
  -- in π“Ÿ (𝓒 .ob), then they are equal, and so are equal when
  -- evaluated in 𝓒.
  solve : βˆ€ {A B} β†’ (e₁ eβ‚‚ : W𝓒 [ A , B ])
        β†’ eval e₁ ≑ eval eβ‚‚
        β†’ ⟦ e₁ ⟧c ≑ ⟦ eβ‚‚ ⟧c
  solve {A}{B} e₁ eβ‚‚ p = cong ⟦_⟧c (isFaithfulPseudoYoneda {C = W𝓒} _ _ _ _ lem) where
    lem : π“˜ βŸͺ e₁ ⟫ ≑ π“˜ βŸͺ eβ‚‚ ⟫
    lem = transport
            (Ξ» i β†’ Yo-YoSem-agree (~ i) βŸͺ e₁ ⟫ ≑ Yo-YoSem-agree (~ i) βŸͺ eβ‚‚ ⟫)
            p

solve : (𝓒 : Category β„“ β„“')
      β†’ {A B : 𝓒 .ob}
      β†’ (e₁ eβ‚‚ : FreeCat (Catβ†’Quiver 𝓒) [ A , B ])
      β†’ (p : Eval.eval 𝓒 e₁ ≑ Eval.eval 𝓒 eβ‚‚)
      β†’ _
solve = Eval.solve