{-# OPTIONS --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.Instances.Free.Category.Quiver as FreeCat
open import Cubical.Categories.Instances.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