/--
To show `P` is a sheaf for the binding of `U` with `B`, it suffices to show that `P` is a sheaf for
`U`, that `P` is a sheaf for each sieve in `B`, and that it is separated for any pullback of any
sieve in `B`.

This is mostly an auxiliary lemma to show `isSheafFor_trans`.
Adapted from [Elephant], Lemma C2.1.7(i) with suggestions as mentioned in
https://math.stackexchange.com/a/358709/
-/
theorem isSheafFor_bind (P : Cᵒᵖ ⥤ Type v) (U : Sieve X) (B : ∀ ⦃Y⦄ ⦃f : Y ⟶ X⦄, U f → Sieve Y)
    (hU : Presieve.IsSheafFor P (U : Presieve X))
    (hB : ∀ ⦃Y⦄ ⦃f : Y ⟶ X⦄ (hf : U f), Presieve.IsSheafFor P (B hf : Presieve Y))
    (hB' : ∀ ⦃Y⦄ ⦃f : Y ⟶ X⦄ (h : U f) ⦃Z⦄ (g : Z ⟶ Y),
      Presieve.IsSeparatedFor P (((B h).pullback g) : Presieve Z)) :
    Presieve.IsSheafFor P (Sieve.bind (U : Presieve X) B : Presieve X) := by
  intro s hs
  let y : ∀ ⦃Y⦄ ⦃f : Y ⟶ X⦄ (hf : U f), Presieve.FamilyOfElements P (B hf : Presieve Y) :=
    fun Y f hf Z g hg => s _ (Presieve.bind_comp _ _ hg)
  have hy : ∀ ⦃Y⦄ ⦃f : Y ⟶ X⦄ (hf : U f), (y hf).Compatible := by
    intro Y f H Y₁ Y₂ Z g₁ g₂ f₁ f₂ hf₁ hf₂ comm
    apply hs
    apply reassoc_of% comm
  let t : Presieve.FamilyOfElements P (U : Presieve X) :=
    fun Y f hf => (hB hf).amalgamate (y hf) (hy hf)
  have ht : ∀ ⦃Y⦄ ⦃f : Y ⟶ X⦄ (hf : U f), (y hf).IsAmalgamation (t f hf) := fun Y f hf =>
    (hB hf).isAmalgamation _
  have hT : t.Compatible := by
    rw [Presieve.compatible_iff_sieveCompatible]
    intro Z W f h hf
    apply (hB (U.downward_closed hf h)).isSeparatedFor.ext
    intro Y l hl
    apply (hB' hf (l ≫ h)).ext
    intro M m hm
    have : bind U B (m ≫ l ≫ h ≫ f) := by
      -- porting note: had to make explicit the parameter `((m ≫ l ≫ h) ≫ f)` and
      -- using `by exact`
      have : bind U B ((m ≫ l ≫ h) ≫ f) := by exact Presieve.bind_comp f hf hm
      simpa using this
    trans s (m ≫ l ≫ h ≫ f) this
    · have := ht (U.downward_closed hf h) _ ((B _).downward_closed hl m)
      rw [op_comp, FunctorToTypes.map_comp_apply] at this
      rw [this]
      change s _ _ = s _ _
      -- porting note: the proof was `by simp`
      congr 1
      simp only [assoc]
    · have h : s _ _ = _ := (ht hf _ hm).symm
      -- porting note: this was done by `simp only [assoc] at`
      conv_lhs at h => 

Proof state:
C : Type u
inst✝ : Category.{v, u} C
P✝ : Cᵒᵖ ⥤ Type v
X Y✝ : C
S : Sieve X
R : Presieve X
J J₂ : GrothendieckTopology C
P : Cᵒᵖ ⥤ Type v
U : Sieve X
B : ⦃Y : C⦄ → ⦃f : Y ⟶ X⦄ → U.arrows f → Sieve Y
hU : Presieve.IsSheafFor P U.arrows
hB : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄ (hf : U.arrows f), Presieve.IsSheafFor P (B hf).arrows
hB' :
  ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄ (h : U.arrows f) ⦃Z : C⦄ (g : Z ⟶ Y), Presieve.IsSeparatedFor P (Sieve.pullback g (B h)).arrows
s : Presieve.FamilyOfElements P (Sieve.bind U.arrows B).arrows
hs : Presieve.FamilyOfElements.Compatible s
y : ⦃Y : C⦄ → ⦃f : Y ⟶ X⦄ → (hf : U.arrows f) → Presieve.FamilyOfElements P (B hf).arrows :=
  fun Y f hf Z g hg => s (g ≫ f) (_ : Presieve.bind (fun Y f => U.arrows f) (fun Y f H => (B H).arrows) (g ≫ f))
hy : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄ (hf : U.arrows f), Presieve.FamilyOfElements.Compatible (y hf)
t : Presieve.FamilyOfElements P U.arrows :=
  fun Y f hf =>
    Presieve.IsSheafFor.amalgamate (_ : Presieve.IsSheafFor P (B hf).arrows) (y hf)
      (_ : Presieve.FamilyOfElements.Compatible (y hf))
ht : ∀ ⦃Y : C⦄ ⦃f : Y ⟶ X⦄ (hf : U.arrows f), Presieve.FamilyOfElements.IsAmalgamation (y hf) (t f hf)
Z W : C
f : Z ⟶ X
h✝ : W ⟶ Z
hf : U.arrows f
Y : C
l : Y ⟶ W
hl : (B (_ : U.arrows (h✝ ≫ f))).arrows l
M : C
m : M ⟶ Y
hm : (Sieve.pullback (l ≫ h✝) (B hf)).arrows m
this : (Sieve.bind U.arrows B).arrows (m ≫ l ≫ h✝ ≫ f)
h :
  s ((m ≫ l ≫ h✝) ≫ f) (_ : Presieve.bind (fun Y f => U.arrows f) (fun Y f H => (B H).arrows) ((m ≫ l ≫ h✝) ≫ f)) =
    P.map (m ≫ l ≫ h✝).op (t f hf)
| s ((m ≫ l ≫ h✝) ≫ f) (_ : Presieve.bind (fun Y f => U.arrows f) (fun Y f H => (B H).arrows) ((m ≫ l ≫ h✝) ≫ f))

Next tactic:
congr; rw [assoc, assoc]