import Mathlib
import Definitions.Def_LocalLanglands_HeckePair

set_option autoImplicit false

open scoped Pointwise

namespace HeckePair

noncomputable section

variable {G : Type*} [Group G] {U : Subgroup G}
variable {R₀ : Type*} [CommRing R₀]

theorem apply_eq_of_mem_doubleCoset (f : HeckeAlgebra U R₀) {g x : G}
    (hx : x ∈ doubleCoset U g) : (f : G → R₀) x = (f : G → R₀) g := by
  obtain ⟨u, hu, v, hv, rfl⟩ := mem_doubleCoset_iff.mp hx
  rw [apply_mul_right f hv, apply_left_mul f hu]

theorem doubleCoset_eq_of_mem {g x : G} (hx : x ∈ doubleCoset U g) :
    doubleCoset U x = doubleCoset U g := by
  obtain ⟨u, hu, v, hv, rfl⟩ := mem_doubleCoset_iff.mp hx
  ext w
  constructor
  · intro hw
    obtain ⟨a, ha, b, hb, rfl⟩ := mem_doubleCoset_iff.mp hw
    exact mem_doubleCoset_iff.mpr ⟨a * u, mul_mem ha hu, v * b, mul_mem hv hb, by group⟩
  · intro hw
    obtain ⟨a, ha, b, hb, rfl⟩ := mem_doubleCoset_iff.mp hw
    exact mem_doubleCoset_iff.mpr
      ⟨a * u⁻¹, mul_mem ha (inv_mem hu), v⁻¹ * b, mul_mem (inv_mem hv) hb, by group⟩

variable (U) in

structure IsGelfandInvolution (τ : G → G) : Prop where

  map_mul : ∀ x y, τ (x * y) = τ y * τ x

  involutive : ∀ x, τ (τ x) = x

  map_mem : ∀ u ∈ U, τ u ∈ U

  mem_doubleCoset : ∀ g, τ g ∈ doubleCoset U g

namespace IsGelfandInvolution

variable {τ : G → G} (hτ : IsGelfandInvolution U τ)

include hτ

theorem map_one : τ 1 = 1 := by
  have h := hτ.map_mul 1 1
  rw [one_mul] at h

  exact (mul_right_cancel (a := (1 : G)) (b := τ 1) (by rw [one_mul, ← h])).symm

theorem map_inv (x : G) : τ x⁻¹ = (τ x)⁻¹ := by
  have h : τ x⁻¹ * τ x = 1 := by rw [← hτ.map_mul, mul_inv_cancel, hτ.map_one]
  exact eq_inv_of_mul_eq_one_left h

theorem bijective : Function.Bijective τ :=
  Function.Involutive.bijective hτ.involutive

theorem apply_tau (f : HeckeAlgebra U R₀) (x : G) : (f : G → R₀) (τ x) = (f : G → R₀) x :=
  apply_eq_of_mem_doubleCoset f (hτ.mem_doubleCoset x)

protected def reindex (x : G) : G ⧸ U → G ⧸ U :=
  Quotient.lift (fun y => (QuotientGroup.mk (x * (τ y)⁻¹) : G ⧸ U)) <| by
    intro a b hab
    obtain ⟨u, hu, rfl⟩ : ∃ u ∈ U, a * u = b :=
      ⟨a⁻¹ * b, QuotientGroup.leftRel_apply.mp hab, by group⟩
    refine (QuotientGroup.eq.mpr ?_).symm

    rw [hτ.map_mul a u]
    have hgrp : (x * (τ u * τ a)⁻¹)⁻¹ * (x * (τ a)⁻¹) = τ u := by group
    rw [hgrp]
    exact hτ.map_mem u hu

@[simp] theorem reindex_mk (x y : G) :
    hτ.reindex x (QuotientGroup.mk y) = QuotientGroup.mk (x * (τ y)⁻¹) := rfl

protected def reindexInv (x : G) : G ⧸ U → G ⧸ U :=
  Quotient.lift (fun z => (QuotientGroup.mk (τ (z⁻¹ * x)) : G ⧸ U)) <| by
    intro a b hab
    obtain ⟨u, hu, rfl⟩ : ∃ u ∈ U, a * u = b :=
      ⟨a⁻¹ * b, QuotientGroup.leftRel_apply.mp hab, by group⟩
    refine (QuotientGroup.eq.mpr ?_).symm

    have hexp : (a * u)⁻¹ * x = u⁻¹ * (a⁻¹ * x) := by group
    rw [hexp, hτ.map_mul, hτ.map_inv]
    have hgrp : (τ (a⁻¹ * x) * (τ u)⁻¹)⁻¹ * τ (a⁻¹ * x) = τ u := by group
    rw [hgrp]
    exact hτ.map_mem u hu

@[simp] theorem reindexInv_mk (x z : G) :
    hτ.reindexInv x (QuotientGroup.mk z) = QuotientGroup.mk (τ (z⁻¹ * x)) := rfl

theorem bijective_reindex (x : G) : Function.Bijective (hτ.reindex x) := by
  have hleft : ∀ c, hτ.reindexInv x (hτ.reindex x c) = c := by
    refine fun c => Quotient.inductionOn c fun y => ?_
    rw [reindex_mk, reindexInv_mk]
    refine congrArg _ ?_
    have h1 : (x * (τ y)⁻¹)⁻¹ * x = τ y := by group
    rw [h1, hτ.involutive]
  have hright : ∀ c, hτ.reindex x (hτ.reindexInv x c) = c := by
    refine fun c => Quotient.inductionOn c fun z => ?_
    rw [reindexInv_mk, reindex_mk]
    refine congrArg _ ?_
    rw [hτ.involutive]
    group
  exact Function.bijective_iff_has_inverse.mpr ⟨hτ.reindexInv x, hleft, hright⟩

theorem convTerm_tau (f₁ f₂ : HeckeAlgebra U R₀) (x : G) (c : G ⧸ U) :
    convTerm f₁ f₂ (τ x) c = convTerm f₂ f₁ x (hτ.reindex x c) := by
  refine Quotient.inductionOn c fun y => ?_
  rw [reindex_mk, convTerm_mk, convTerm_mk]

  have h2 : (x * (τ y)⁻¹)⁻¹ * x = τ y := by group
  rw [h2, hτ.apply_tau f₁ y]

  have h1 : (f₂ : G → R₀) (y⁻¹ * τ x) = (f₂ : G → R₀) (x * (τ y)⁻¹) := by
    conv_lhs => rw [← hτ.apply_tau f₂ (y⁻¹ * τ x)]
    rw [hτ.map_mul, hτ.involutive, hτ.map_inv]
  rw [h1, mul_comm]

theorem mul_apply_tau (f₁ f₂ : HeckeAlgebra U R₀) (x : G) :
    ((f₁ * f₂ : HeckeAlgebra U R₀) : G → R₀) (τ x)
      = ((f₂ * f₁ : HeckeAlgebra U R₀) : G → R₀) x := by
  rw [mul_apply, mul_apply]
  exact finsum_eq_of_bijective (hτ.reindex x) (hτ.bijective_reindex x)
    (hτ.convTerm_tau f₁ f₂ x)

theorem heckeMul_comm (f₁ f₂ : HeckeAlgebra U R₀) : f₁ * f₂ = f₂ * f₁ := by
  ext x

  rw [← hτ.apply_tau (f₁ * f₂) x, hτ.mul_apply_tau f₁ f₂ x]

@[reducible] protected def commRing : CommRing (HeckeAlgebra U R₀) :=
  { (inferInstance : Ring (HeckeAlgebra U R₀)) with
    mul_comm := hτ.heckeMul_comm }

end IsGelfandInvolution

theorem IsGelfandInvolution.of_forall_exists_fixedRep {τ : G → G}
    (hmul : ∀ x y, τ (x * y) = τ y * τ x) (hinv : ∀ x, τ (τ x) = x)
    (hU : ∀ u ∈ U, τ u ∈ U)
    (hrep : ∀ g : G, ∃ d, τ d = d ∧ g ∈ doubleCoset U d) :
    IsGelfandInvolution U τ := by
  refine ⟨hmul, hinv, hU, fun g => ?_⟩
  obtain ⟨d, hd, hgd⟩ := hrep g

  rw [doubleCoset_eq_of_mem hgd]
  obtain ⟨u, hu, v, hv, rfl⟩ := mem_doubleCoset_iff.mp hgd
  rw [hmul, hmul, hd]
  exact mem_doubleCoset_iff.mpr ⟨τ v, hU v hv, τ u, hU u hu, by group⟩

end

end HeckePair
