/-
  Topolei.Cubical.Syntax
  ======================
  Deep embedding of the cubical term language (CCHM §2–3).

  Grammar:
    A, B  ::= U | Π A B | Path A a b
    t, u  ::= x | λx.t | t u | ⟨i⟩t | t@r | transpⁱ A φ t | compⁱ A φ u t

  CType and CTerm are mutually inductive because path types carry endpoint
  terms, and terms carry path applications over DimExprs.

  transp and comp store (i : DimVar) (A : CType) inline rather than DimLine,
  because DimLine is defined later in the import chain (Subst → DimLine).
  The typing rules in Typing.lean use DimLine as a convenient wrapper.

  The path β-rule  (⟨i⟩ t) @ r  ↝  t[i := r]
  and the four "fully-reducing" transport/comp cases (T1, T2, C1, C2)
  are NbE theorems in `Cubical/Readback.lean`.  The residual step-level
  axioms — T3, T5, C4 (subject reduction + face congruence) and T4
  (path-line shape preservation) — live in `TransportLaws.lean` /
  `CompLaws.lean`.
-/

import CubicalTransport.Face

-- ── Syntax ────────────────────────────────────────────────────────────────────

mutual
  /-- Types in the cubical calculus. -/
  inductive CType where
    | univ                             : CType          -- U
    | pi   (A : CType) (B : CType)   : CType          -- Π A B
    | path (A : CType) (a b : CTerm) : CType          -- Path A a b
    /-- Non-dependent Σ type (cells-spec §6.2, §8.1, §9.2).

        `Sigma A B` — pairs whose first component has type `A` and whose
        second has type `B`.  Non-dependent: `B` does not refer to the
        first component.  Dependent Σ (where `B : A → CType`) is deferred
        for the same reason as dependent Π — requires a term evaluator
        to apply `B` to a term. -/
    | sigma (A B : CType)            : CType          -- Σ A B
    /-- Glue type (CCHM §6).
        `Glue [φ ↦ (T, e)] A` — on face `φ` the type is `T` with `e : T ≃ A`
        as witness; off face `φ` the type is `A`.  The equivalence `e` is
        inlined as five `CTerm` fields (f, fInv, sec, ret, coh) rather than
        a nested `EquivData` so that `CType` / `CTerm` remain a closed
        mutual inductive block — `EquivData` lives in `Equiv.lean` and is
        downstream in the import chain.  Use `EquivData.toGlueType` in
        `Equiv.lean` for an ergonomic wrapper. -/
    | glue (φ : FaceFormula) (T : CType)
           (f fInv sec ret coh : CTerm)
           (A : CType)                                        : CType  -- Glue [φ ↦ (T, e)] A
    deriving Repr

  /-- Terms in the cubical calculus. -/
  inductive CTerm where
    | var   (x : String)                                        : CTerm  -- x
    | lam   (x : String) (t : CTerm)                           : CTerm  -- λx. t
    | app   (f a : CTerm)                                       : CTerm  -- f a
    | plam  (i : DimVar) (t : CTerm)                           : CTerm  -- ⟨i⟩ t
    | papp  (t : CTerm) (r : DimExpr)                          : CTerm  -- t @ r
    | transp (i : DimVar) (A : CType) (φ : FaceFormula)
             (t : CTerm)                                        : CTerm  -- transpⁱ A φ t
    | comp   (i : DimVar) (A : CType) (φ : FaceFormula)
             (u t : CTerm)                                      : CTerm  -- compⁱ A φ u t
    /-- Multi-clause heterogeneous composition.
        `compⁱ A [φ₁ ↦ u₁, …, φₙ ↦ uₙ] t` — a partial element defined
        over the union of the clause faces.  Coherence (clauses agree
        on overlaps and with `t` at `i = 0`) is a typing-level side
        condition, not enforced syntactically.  Used by CCHM path
        transport and heterogeneous Π composition to express systems
        that a single-clause `.comp` cannot represent. -/
    | compN  (i : DimVar) (A : CType)
             (clauses : List (FaceFormula × CTerm))
             (t : CTerm)                                        : CTerm  -- compⁱ A [φ₁↦u₁,…] t
    /-- Glue introduction (CCHM §6.1).
        `glueIn [φ ↦ t] a` — on face `φ`, equals `t : T`; off face `φ`,
        equals `a : A`.  Well-typedness requires `e.f t = a` on the
        overlap (a typing-side condition, not enforced syntactically).
        The equivalence `e` is carried by the type, not the term. -/
    | glueIn (φ : FaceFormula) (t a : CTerm)                   : CTerm  -- glue [φ↦t] a
    /-- Glue elimination (CCHM §6.1).
        `unglue [φ ↦ f] g` — extract the underlying `A`-value from a
        glued term.  On face `φ`, equals `f g` (apply the forward map);
        off face `φ`, equals `g` (already an `A`-value).  `f` is the
        `f`-field of the equivalence witnessing `T ≃ A` — carried
        explicitly at the term level because we don't have type
        annotations on terms. -/
    | unglue (φ : FaceFormula) (f g : CTerm)                    : CTerm  -- unglue [φ↦f] g
    /-- Σ introduction (pair). -/
    | pair (a b : CTerm)                                        : CTerm  -- (a, b)
    /-- Σ elimination (first projection). -/
    | fst (t : CTerm)                                           : CTerm  -- t.1
    /-- Σ elimination (second projection). -/
    | snd (t : CTerm)                                           : CTerm  -- t.2
    deriving Repr
end

-- ── Dimension substitution ────────────────────────────────────────────────────
-- Substitute dimension variable i with DimExpr r throughout a term.
--
-- Scope inside transp/comp:
--   · j is the binder of the transport line, bound in A and in φ.
--   · The base term t (and system u) are in outer scope — we substitute there.
--
-- Approximation: `substDim` does NOT descend into A or φ — even when j ≠ i
-- and i would be free under the binder. Consequence: this substitution is
-- only faithful for *endpoint* calls (`substDimBool`), where downstream
-- uses the dimension-absent predicate to justify correctness. Full
-- DimExpr-in-FaceFormula substitution is deferred (see cells-spec §5.5).

mutual
  def CTerm.substDim (i : DimVar) (r : DimExpr) : CTerm → CTerm
    | .var x           => .var x
    | .lam x t         => .lam x (t.substDim i r)
    | .app f a         => .app (f.substDim i r) (a.substDim i r)
    | .plam j t        => if j = i then .plam j t          -- i bound; stop
                          else .plam j (t.substDim i r)
    | .papp t s        => .papp (t.substDim i r) (DimExpr.subst i r s)
    -- transp/comp: leave A alone (approximation); descend into t, u and
    -- substitute in φ via the general DimExpr face-formula substitution.
    | .transp j A φ t  => .transp j A (φ.substDim i r) (t.substDim i r)
    | .comp   j A φ u t => .comp j A (φ.substDim i r) (u.substDim i r) (t.substDim i r)
    -- Multi-clause comp: substitute in each clause's face and body, and in t.
    -- Uses an explicit recursive helper `substDim.clauses` so Lean can see
    -- structural termination through the clause list.
    | .compN  j A clauses t =>
        .compN j A (CTerm.substDim.clauses i r clauses) (t.substDim i r)
    -- Glue introduction / elimination: descend into all sub-terms and
    -- substitute into the face formula.  Same approximation for types
    -- as transp/comp (A not touched — the `φ` face formula carries the
    -- whole dim-dependency story; in our approximation we still don't
    -- recurse into CType sub-trees here, matching `.transp`/`.comp`).
    | .glueIn φ t a     => .glueIn (φ.substDim i r) (t.substDim i r) (a.substDim i r)
    | .unglue φ f g     => .unglue (φ.substDim i r) (f.substDim i r) (g.substDim i r)
    -- Σ constructors: structural recursion into sub-terms.
    | .pair a b         => .pair (a.substDim i r) (b.substDim i r)
    | .fst t            => .fst (t.substDim i r)
    | .snd t            => .snd (t.substDim i r)

  /-- Helper: apply `CTerm.substDim i r` to each clause body (and
      `FaceFormula.substDim` to each face) in a system's clause list.
      Defined mutually with `substDim` so Lean can verify termination. -/
  def CTerm.substDim.clauses (i : DimVar) (r : DimExpr) :
      List (FaceFormula × CTerm) → List (FaceFormula × CTerm)
    | []                 => []
    | (φ, u) :: rest     =>
        (φ.substDim i r, u.substDim i r) :: CTerm.substDim.clauses i r rest
end

-- ── One-step reduction ────────────────────────────────────────────────────────
-- `CTerm.step` is left *opaque*.  Semantically it is what a CCHM-style
-- evaluator will compute.  Keeping `step` opaque is required for
-- soundness: if `step` were a concrete `def` with a wildcard identity
-- arm, any axiom of the shape `step (.transp …) = t` would rfl-collapse
-- to `.transp … = t`, contradicting `CTerm.noConfusion`.
--
-- **Stage 4.4 decision (2026-04-23).**  After the Phase 1 step↔eval
-- bridge + Stream B #2d + Stage 2.3, only one step-level axiom remains:
-- `transp_plam_is_plam_path` (T4, path-restricted form; Stage 4.2).
-- T1/T2/C1/C2/T5/`step_papp_plam` are NbE theorems in `Readback.lean`;
-- T3/C4 are theorems via `CTerm.step_preserves_type` (ValueTyping.lean);
-- glue β/η are theorems via `eval_unglue_of_glueIn`/`eval_glueIn_of_unglue`.
--
-- **Rust discharge flexibility.**  The Rust backend has two valid
-- implementation strategies for `CTerm.step`:
--
--   · *Option A — native step*: implement `step` directly as a C ABI
--     entry point.  The single T4 axiom is discharged by emitting the
--     CCHM comp-shaped body for path-typed transp-of-plam inputs, plus
--     identity behaviour on other shapes.
--
--   · *Option B — derived step*: define `step := readback ∘ eval .nil`
--     entirely in Rust (no separate `step` FFI entry).  This matches
--     the Lean-side "bridge" intuition and keeps the Rust FFI surface
--     smaller by one function.  Satisfies T4 on path-typed lines via
--     the `vPathTransp` → `.compN` readback arm.
--
-- The Lean-side spec is agnostic to which strategy Rust picks — both
-- satisfy the same axiom set.  See FFI_DESIGN.md (Stage 4.5) for the
-- concrete recommendation.

opaque CTerm.step : CTerm → CTerm := id
