/-
Copyright (c) 2024 Judith Ludwig, Christian Merten. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Judith Ludwig, Christian Merten
-/
import Mathlib.LinearAlgebra.Projectivization.Cardinality
import Mathlib.RingTheory.DiscreteValuationRing.Basic
import BruhatTits.Graph.Tree

/-!
# Proof that the Bruhat-Tits tree is regular

Let `R` be a discrete valuation ring. Assume that the residue field `k = R ⧸ 𝓂 R` of `R` is finite.

In this file we show that the Bruhat-Tits tree associated to `R` is regular, i.e. that every vertex has the same (finite) number of neighbours. Furthermore we show that this number is `#k+1`.

# Main result

- `BruhatTits.btgraph_regular` : The Bruhat-Tits Tree is `q + 1`-regular, where `q` is the cardinality of `R ⧸ 𝓂 R`.
-/

suppress_compilation

-- Let R be a discrete valuation ring and K its field of fractions
variable {K : Type*} [Field K]
variable {R : Subring K} [IsDiscreteValuationRing R] [IsFractionRing R K]

namespace BruhatTits

def standardNeighbourBasis {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) :
    Basis (Fin 2) K (Fin 2 → K) :=
  (exists_basis_eq_ntwist_of_isNeighbour L y ((isNeighbour_symm _ _).mp h)).choose

@[simp]
lemma standardNeighbourBasis_toLattice_Eq {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) :
    (standardNeighbourBasis h).toLattice = L :=
  (exists_basis_eq_ntwist_of_isNeighbour L y
    ((isNeighbour_symm _ _).mp h)).choose_spec.choose_spec.choose_spec.left

def standardNeighbourϖ {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) : R :=
  (exists_basis_eq_ntwist_of_isNeighbour L y ((isNeighbour_symm _ _).mp h)).choose_spec.choose

lemma standardNeighbourϖ_irreducible {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) :
    Irreducible (standardNeighbourϖ h) :=
  (exists_basis_eq_ntwist_of_isNeighbour L y
    ((isNeighbour_symm _ _).mp h)).choose_spec.choose_spec.choose

def standardNeighbour {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) :
    Lattice R :=
  ((standardNeighbourBasis h).ntwist₂ (standardNeighbourϖ_irreducible h) 1 0).toLattice

@[simp]
lemma standardNeighbourBasis_ntwist_eq {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) :
    ((standardNeighbourBasis h).ntwist₂ (standardNeighbourϖ_irreducible h) 1 0).toLattice =
      standardNeighbour h :=
  rfl

lemma standardNeighbour_isStandardNeighbour {L : Lattice R} {y : Vertices R}
    (h : IsNeighbour y ⟦L⟧) :
    IsStandardNeighbour (standardNeighbour h) L := by
  nth_rw 2 [← standardNeighbourBasis_toLattice_Eq h]
  apply ntwist_isStandardNeighbour

@[simp]
lemma standardNeighbour_mk_eq {L : Lattice R} {y : Vertices R} (h : IsNeighbour y ⟦L⟧) :
    ⟦standardNeighbour h⟧ = y :=
  (exists_basis_eq_ntwist_of_isNeighbour L y
    ((isNeighbour_symm _ _).mp h)).choose_spec.choose_spec.choose_spec.right

lemma exists_basis_standardNeighbour_eq_twist {L : Lattice R} {y : Vertices R}
    (h : IsNeighbour y ⟦L⟧) :
    ∃ (ϖ : R) (hϖ : Irreducible ϖ) (b : Basis (Fin 2) K (Fin 2 → K)),
      L = b.toLattice ∧ standardNeighbour h = (b.ntwist₂ hϖ 1 0).toLattice := by
  use standardNeighbourϖ h, standardNeighbourϖ_irreducible h, standardNeighbourBasis h
  simp

lemma eq_standardNeighbour_of_isStandardNeighbour {L : Lattice R} {y : Vertices R}
    (h : IsNeighbour y ⟦L⟧) (M : Lattice R) (hM : ⟦M⟧ = y)
    (hML : IsStandardNeighbour M L) :
    M = standardNeighbour h := by
  obtain ⟨ϖ, hϖ, b, rfl, hstd⟩ := exists_basis_standardNeighbour_eq_twist h
  have : (⟦M⟧ : Vertices R) = ⟦standardNeighbour h⟧ := by simp [hM]
  have : Lattice.IsSimilar R (standardNeighbour h) M := Quotient.exact this.symm
  obtain ⟨a, rfl⟩ := this
  obtain ⟨n, u, ha⟩ := eq_unit_mul_pow_irreducible' ϖ hϖ a
  rw [ha, mul_comm, mul_smul, unit_smul_eq] at hML ⊢
  rw [hstd] at hML
  nth_rw 2 [← b.ntwist₂_zero_zero hϖ] at hML
  have h1 := hML.lt
  have h2 := hML.ϖlt ϖ hϖ
  rw [Basis.toLattice_module, b.smul_ntwist₂] at h2
  simp only [Lattice.smul_module, Basis.toLattice_module, Units.smul_def,
    Units.val_zpow_eq_zpow_val, Units.val_mk0, Basis.ntwist₂] at h1 h2
  rw [b.smul_pow_twist₂] at h1
  simp at h1
  rw [b.smul_pow_twist₂] at h2
  simp only [zero_add, Nat.cast_one, CharP.cast_eq_zero] at h2
  apply le_of_lt at h1
  apply le_of_lt at h2
  rw [b.twist₂_le_twist₂_iff] at h1 h2
  have : n = 0 := by omega
  rw [this]
  simp

open IsLocalRing

/-- The neighbours of `⟦L⟧` are in one to one correspondence to standard neighbours of `L`. -/
def neighborsEquivStandardNeighbors (L : Lattice R) :
    { y : Vertices R | IsNeighbour y ⟦L⟧ } ≃ { M : Lattice R | IsStandardNeighbour M L } where
  toFun y := ⟨standardNeighbour y.property, standardNeighbour_isStandardNeighbour y.property⟩
  invFun M := ⟨⟦M⟧, isNeighbour_of_isStandardNeighbour M.property⟩
  left_inv y := by simp
  right_inv M := by
    ext : 1
    simp only [Set.mem_setOf_eq]
    symm
    apply eq_standardNeighbour_of_isStandardNeighbour
    rfl
    exact M.property

def Lattice.quotientRestrictScalarsEquiv (L : Lattice R) :
    Submodule (ResidueField R) L.quotient ≃o Submodule R L.quotient where
  toFun M := M.restrictScalars R
  invFun M := {
      toAddSubmonoid := M.toAddSubmonoid
      smul_mem' := fun r _ hx ↦
        Quotient.inductionOn' r (fun r ↦ Submodule.smul_mem _ r hx)
    }
  left_inv _ := rfl
  right_inv _ := rfl
  map_rel_iff' := Iff.rfl

instance (priority := 99999) (L : Lattice R) : SMul (Ideal R) (Submodule R L.M) := Submodule.instSMul

def Lattice.submoduleRestrictQuotientEquiv (L : Lattice R) :
    Submodule R L.quotient ≃o { M : Submodule R L.M // (maximalIdeal R • ⊤ : Submodule R L.M) ≤ M } :=
  Submodule.comapMkQRelIso (maximalIdeal R • ⊤ : Submodule R L.M)

def Lattice.submoduleQuotientEquiv (L : Lattice R) :
    Submodule (ResidueField R) L.quotient ≃o { M : Submodule R L.M // maximalIdeal R • ⊤ ≤ M } :=
  L.quotientRestrictScalarsEquiv.trans L.submoduleRestrictQuotientEquiv

lemma submodule_ne_top_of_finrank {L : Lattice R}
    {M : Submodule (ResidueField R) L.quotient}
    (h : Module.finrank (ResidueField R) M = 1) : M ≠ ⊤ := by
  rintro rfl
  simp only [finrank_top] at h
  have : Module.finrank (ResidueField ↥R) L.quotient = 2 := Lattice.quotient_finrank L
  omega

omit [IsFractionRing R K] in
lemma submodule_ne_bot_of_finrank {L : Lattice R}
    {M : Submodule (ResidueField R) L.quotient}
    (h : Module.finrank (ResidueField R) M = 1) : M ≠ ⊥ := by
  rintro rfl
  simp at h

lemma submodule_finrank_eq_one_of_ne_bot_of_ne_top {L : Lattice R}
    {M : Submodule (ResidueField R) L.quotient}
    (h₁ : M ≠ ⊥) (h₂ : M ≠ ⊤) :
    Module.finrank (ResidueField R) M = 1 := by
  have : Module.finrank (ResidueField R) M < 2 := by
    rw [← L.quotient_finrank]
    apply Submodule.finrank_lt_finrank_of_ne_top
    exact h₂
  have : 0 < Module.finrank (ResidueField R) M := by
    apply Submodule.zero_lt_finrank_of_ne_bot
    exact h₁
  omega

lemma submodule_finrank_eq_one_iff {L : Lattice R}
    {M : Submodule (ResidueField R) L.quotient} :
    Module.finrank (ResidueField R) M = 1 ↔ M ≠ ⊥ ∧ M ≠ ⊤ :=
  ⟨fun h ↦ ⟨submodule_ne_bot_of_finrank h, submodule_ne_top_of_finrank h⟩,
   fun h ↦ submodule_finrank_eq_one_of_ne_bot_of_ne_top h.left h.right⟩

def linesQuotientEquiv (L : Lattice R) :
    { M : Submodule (ResidueField R) L.quotient // Module.finrank (ResidueField R) M = 1 } ≃
      { M : { M : Submodule R L.M // maximalIdeal R • ⊤ ≤ M } // M ≠ ⊥ ∧ M ≠ ⊤ } where
  toFun M := ⟨L.submoduleQuotientEquiv M, by
    simp only [ne_eq, map_eq_bot_iff, map_eq_top_iff, ← submodule_finrank_eq_one_iff]
    exact M.property⟩
  invFun M := ⟨L.submoduleQuotientEquiv.symm M, by
    rw [submodule_finrank_eq_one_iff]
    simp only [ne_eq, map_eq_bot_iff, M.property, not_false_eq_true, map_eq_top_iff, and_self]⟩
  left_inv M := by simp only [OrderIso.symm_apply_apply, Subtype.coe_eta]
  right_inv M := by simp only [ne_eq, OrderIso.apply_symm_apply, Subtype.coe_eta]

private def Lattice.submoduleNeAndNeEquivLtAndLt (L : Lattice R) :
    { M : { M : Submodule R L.M // maximalIdeal R • ⊤ ≤ M } // M ≠ ⊥ ∧ M ≠ ⊤ } ≃
      { M : Submodule R (Fin 2 → K) // maximalIdeal R • L.M < M ∧ M < L.M } where
  toFun M := ⟨Submodule.map L.M.subtype M.val.val, by
    refine ⟨ideal_smul_lt_of_ne_bot _ _ M.property.left, ?_⟩
    apply lt_of_ne_top
    exact M.property.right⟩
  invFun M := ⟨⟨Submodule.comap L.M.subtype M, by
      rw [← Submodule.comap_subtype_smul]
      apply Submodule.comap_mono
      exact le_of_lt M.property.left⟩, by
    refine ⟨?_, ?_⟩
    · simp only [ne_eq, Submodule.quotient_equiv_eq_bot_iff]
      intro hc
      apply_fun Submodule.map L.M.subtype at hc
      rw [Submodule.map_comap_eq_self, Submodule.map_subtype_smul] at hc
      absurd hc
      exact ne_of_gt M.property.left
      simp only [Submodule.range_subtype]
      exact le_of_lt M.property.right
    · simp only [ne_eq, le_top, Subtype.mk_eq_top_iff, Submodule.comap_subtype_eq_top]
      exact not_le_of_gt M.property.right⟩
  left_inv M := by
    ext : 2
    simp only [ne_eq, Submodule.comap_map_eq_self, Submodule.ker_subtype, bot_le]
  right_inv M := by
    ext : 1
    dsimp only
    rw [Submodule.map_comap_eq_self]
    simp only [Submodule.range_subtype]
    exact le_of_lt M.property.right

open Pointwise

/-- `R`-submodules of `K^2` that are strictly between `ϖ L` and `L` are in one-to-one
correspondence to standard neighbours of `L`. -/
def Lattice.standardNeighboursEquivLinesAux (L : Lattice R) :
    { M : Submodule R (Fin 2 → K) // maximalIdeal R • L.M < M ∧ M < L.M } ≃
      { M : Lattice R // IsStandardNeighbour M L } where
  toFun M := ⟨⟨M, .of_le_of_isLattice_right L.M M
      (le_of_lt M.property.right) (le_of_lt M.property.left)⟩,
    M.property.right, fun ϖ hϖ ↦ by
      rw [← maximalIdeal_smul_eq_uniformizer_smul _ hϖ]
      exact M.property.left⟩
  invFun M := ⟨M.val.M, by
      obtain ⟨ϖ, hϖ⟩ := IsDiscreteValuationRing.exists_irreducible R
      rw [maximalIdeal_smul_eq_uniformizer_smul _ hϖ]
      exact M.property.ϖlt ϖ hϖ, M.property.lt⟩
  left_inv M := rfl
  right_inv M := rfl

/-- The standard neighbours of `⟦L⟧` are in one-to-one correspondence to
one-dimensional subspaces of `L ⧸ ϖ L`, i.e. lines. -/
def standardNeighboursEquivLines (L : Lattice R) :
    { M : Lattice R // IsStandardNeighbour M L } ≃
      { M : Submodule (ResidueField R) L.quotient // Module.finrank (ResidueField R) M = 1 } :=
  L.standardNeighboursEquivLinesAux.symm.trans <|
    L.submoduleNeAndNeEquivLtAndLt.symm.trans (linesQuotientEquiv L).symm

/-- Neighbors of a vertex `⟦L⟧` correspond to lines in `L ⧸ ϖ L`. -/
def neighborsEquivLines (L : Lattice R) :
    { y : Vertices R // IsNeighbour y ⟦L⟧ } ≃
      { M : Submodule (ResidueField R) L.quotient // Module.finrank (ResidueField R) M = 1 } :=
  (neighborsEquivStandardNeighbors L).trans (standardNeighboursEquivLines L)

open scoped LinearAlgebra.Projectivization

/-- Neighbors of a vertex `⟦L⟧` correspond the projectivization of `L ⧸ ϖ L`. -/
def neighborSetEquivProjectivization (L : Lattice R) :
    BTgraph.neighborSet ⟦L⟧ ≃ ℙ (ResidueField R) L.quotient :=
  (Equiv.setCongr <| by ext; simp [BTgraph_adj, isNeighbour_symm]; rfl).trans <|
  (neighborsEquivLines L).trans
    (Projectivization.equivSubmodule (ResidueField R) L.quotient).symm

variable [Finite (ResidueField R)]

/-- If the residue field of `R` is finite, every vertex has finitely many neighbors. -/
instance (x : Vertices R) : Finite ((BTgraph (R := R)).neighborSet x) := by
  refine Quotient.inductionOn x (fun L ↦ ?_)
  show Finite {y : Vertices R // IsNeighbour ⟦L⟧ y}
  have : Finite L.quotient := Module.finite_of_finite (ResidueField R)
  apply Finite.of_equiv _ (neighborSetEquivProjectivization L).symm

instance (x : Vertices R) : Fintype ((BTgraph (R := R)).neighborSet x) :=
  Fintype.ofFinite _

/-- The degree of the Bruhat-Tits tree is `q + 1` where `q` is the cardinality of `R ⧸ 𝓂 R`. -/
lemma btgraph_degree (x : Vertices R) :
    (BTgraph (R := R)).degree x = Nat.card (ResidueField R) + 1 := by
  rw [← SimpleGraph.card_neighborSet_eq_degree, Fintype.card_eq_nat_card]
  refine Quotient.inductionOn x (fun L ↦ ?_)
  rw [Nat.card_congr (neighborSetEquivProjectivization L)]
  apply Projectivization.card_of_finrank_two
  exact Lattice.quotient_finrank L

/-- If `R ⧸ 𝓂 R` is finite, the Bruhat-Tits Tree is `q + 1`-regular, where `q` is the
cardinality of `R ⧸ 𝓂 R`. -/
theorem btgraph_regular :
    (BTgraph (R := R)).IsRegularOfDegree (Nat.card (ResidueField R) + 1) :=
  btgraph_degree

end BruhatTits
