import Mathlib.Order.Filter.Basic
import Mathlib.Topology.Basic
import Mathlib.Topology.Bases
import Mathlib.Topology.Separation

section Order

variable {α β : Type _}

def DoubleMonotoneOn [Preorder α] [Preorder β] (f: α → β) (B : Set α): Prop :=
  ∀ x, x ∈ B → ∀ y, y ∈ B → (x ≤ y ↔ f x ≤ f y)

variable {f : α → β} {B : Set α}

theorem DoubleMonotoneOn.monotoneOn [Preorder α] [Preorder β] (f_double_mono : DoubleMonotoneOn f B) :
  MonotoneOn f B :=
by
  intro x x_in_B y y_in_B
  rw [f_double_mono x x_in_B y y_in_B]
  tauto

theorem DoubleMonotoneOn.injective [PartialOrder α] [Preorder β] (f_double_mono : DoubleMonotoneOn f B) :
  Function.Injective (Set.restrict B f) :=
by
  intro ⟨x, x_in_B⟩ ⟨y, y_in_B⟩
  simp
  intro fx_eq_fy
  apply le_antisymm
  swap
  symm at fx_eq_fy
  all_goals apply le_of_eq at fx_eq_fy
  all_goals rw [f_double_mono]
  all_goals assumption

theorem DoubleMonotoneOn.subset_iff {B : Set (Set α)} {f : Set α → Set β} (f_double_mono : DoubleMonotoneOn f B) :
  ∀ s ∈ B, ∀ t ∈ B, s ⊆ t ↔ f s ⊆ f t :=
by
  simp only [<-Set.le_eq_subset]
  exact f_double_mono

structure OrderIsoOn (α : Type _) (β : Type _) [Preorder α] [Preorder β] (S : Set α) :=
  toFun : α → β
  invFun : β → α

  leftInv_on : ∀ a ∈ S, invFun (toFun a) = a
  rightInv_on : ∀ b ∈ toFun '' S, toFun (invFun b) = b

  toFun_doubleMonotone : DoubleMonotoneOn toFun S

theorem OrderIsoOn.mk_of_subset [Preorder α] [Preorder β] (F : OrderIsoOn α β S)
  {T : Set α} (T_ss_S : T ⊆ S) : OrderIsoOn α β T
where
  toFun := F.toFun
  invFun := F.invFun

  leftInv_on := by
    intro a a_in_T
    rw [F.leftInv_on a (T_ss_S a_in_T)]

  rightInv_on := by
    intro b b_in_mT
    have b_in_mS : b ∈ F.toFun '' S := Set.image_subset _ T_ss_S b_in_mT
    rw [F.rightInv_on b b_in_mS]

  toFun_doubleMonotone := by
    intro a a_in_T b b_in_T
    rw [F.toFun_doubleMonotone a (T_ss_S a_in_T) b (T_ss_S b_in_T)]

theorem OrderIsoOn.invFun_doubleMonotone [Preorder α] [Preorder β] (F : OrderIsoOn α β S) :
  DoubleMonotoneOn F.invFun (F.toFun '' S) :=
by
  intro x ⟨x', x'_in_S, x_eq⟩ y ⟨y', y'_in_S, y_eq⟩
  rw [<-x_eq, <-y_eq]
  (repeat rw [F.leftInv_on]) <;> try assumption
  symm
  apply toFun_doubleMonotone <;> assumption

instance [Preorder α] [Preorder β] : CoeOut (OrderIsoOn α β S) (α → β) where
  coe := fun F x => F.toFun x

variable [Preorder α] [Preorder β]

theorem OrderIsoOn.toFun_injective (F : OrderIsoOn α β S) :
  Function.Injective (Set.restrict S F.toFun) :=
by
  intro ⟨x, x_in_S⟩ ⟨y, y_in_S⟩
  simp
  intro fX_eq_fY
  rw [<-F.leftInv_on _ x_in_S]
  rw [<-F.leftInv_on _ y_in_S]
  rw [fX_eq_fY]

theorem OrderIsoOn.toFun_inj (F : OrderIsoOn α β S) :
  ∀ x ∈ S, ∀ y ∈ S, F.toFun x = F.toFun y → x = y :=
by
  intro x xs y ys fx_eq_fy
  have res := F.toFun_injective (by
    show Set.restrict S F.toFun ⟨x, xs⟩ = Set.restrict S F.toFun ⟨y, ys⟩
    simp
    exact fx_eq_fy
  )
  simp at res
  exact res

theorem OrderIsoOn.mem_toFun_image (F : OrderIsoOn α β S) (y : β):
  y ∈ F.toFun '' S → F.invFun y ∈ S :=
by
  simp
  intro x x_in_S y_eq
  rw [<-y_eq]
  rw [F.leftInv_on x x_in_S]
  assumption

theorem OrderIsoOn.toFun_eq_to_invFun (F : OrderIsoOn α β S) :
  ∀ x ∈ S, ∀ y, F.toFun x = y → x = F.invFun y :=
by
  intro x x_in_S y y_eq
  have y_in_mS : y ∈ F.toFun '' S := by use x
  rw [<-F.rightInv_on y y_in_mS] at y_eq
  apply F.toFun_inj <;> try assumption
  apply mem_toFun_image
  assumption

theorem OrderIsoOn.toFun_eq_iff (F : OrderIsoOn α β S) :
  ∀ x ∈ S, ∀ y ∈ F.toFun '' S, F.toFun x = y ↔ x = F.invFun y :=
by
  intro x x_in_S y y_in_fS
  constructor
  · apply toFun_eq_to_invFun
    assumption
  · intro x_eq
    rw [x_eq]
    apply F.rightInv_on
    assumption

@[simp]
theorem OrderIsoOn.leftInv_image (F : OrderIsoOn α β S) :
  (F.invFun ∘ F.toFun) '' S = S :=
by
  ext x
  simp
  constructor
  · intro ⟨y, y_in_S, y_eq⟩
    rw [F.leftInv_on y y_in_S] at y_eq
    exact y_eq ▸ y_in_S
  · intro x_in_S
    use x
    exact ⟨x_in_S, F.leftInv_on x x_in_S⟩

@[simp]
theorem OrderIsoOn.leftInv_image' (F : OrderIsoOn α β S) :
  F.invFun '' (F.toFun '' S) = S := by rw [Set.image_image, <-Function.comp_def, leftInv_image]

def OrderIsoOn.comp {γ : Type _} [Preorder γ] (F : OrderIsoOn α β S)
  (G : OrderIsoOn β γ (F.toFun '' S)) : OrderIsoOn α γ S where
  toFun := G.toFun ∘ F.toFun
  invFun := F.invFun ∘ G.invFun

  leftInv_on := by
    intro x x_in_S
    simp
    rw [G.leftInv_on _ (by use x)]
    rw [F.leftInv_on x x_in_S]

  rightInv_on := by
    intro y y_in_img
    simp
    rw [Function.comp_def, <-Set.image_image G.toFun F.toFun] at y_in_img
    let ⟨z, z_in_fS, fz_eq_y⟩ := y_in_img
    have z_eq_fy : z = G.invFun y := G.toFun_eq_to_invFun z z_in_fS y fz_eq_y
    rw [<-z_eq_fy, <-fz_eq_y]
    rw [F.rightInv_on]
    assumption

  toFun_doubleMonotone := by
    intro x x_in_S y y_in_S
    simp
    apply Iff.trans
    exact F.toFun_doubleMonotone x x_in_S y y_in_S
    apply G.toFun_doubleMonotone
    use x
    use y

@[simp]
theorem OrderIsoOn.comp_toFun {γ : Type _} [Preorder γ] (F : OrderIsoOn α β S) (G : OrderIsoOn β γ (F.toFun '' S)) :
  (F.comp G).toFun = G.toFun ∘ F.toFun := rfl

def OrderIsoOn.inv (F : OrderIsoOn α β S) : OrderIsoOn β α (F.toFun '' S) where
  toFun := F.invFun
  invFun := F.toFun

  leftInv_on := F.rightInv_on
  rightInv_on := by
    simp
    exact F.leftInv_on

  toFun_doubleMonotone := F.invFun_doubleMonotone

@[simp]
theorem OrderIsoOn.inv_toFun (F : OrderIsoOn α β S) : F.inv.toFun = F.invFun := rfl

@[simp]
theorem OrderIsoOn.inv_invFun (F : OrderIsoOn α β S) : F.inv.invFun = F.toFun := rfl

def OrderIso.orderIsoOn (F : α ≃o β) (S : Set α) : OrderIsoOn α β S where
  toFun := F.toFun
  invFun := F.invFun

  leftInv_on := by
    intro a _
    apply Equiv.left_inv
  rightInv_on := by
    intro b _
    apply Equiv.right_inv

  toFun_doubleMonotone := by
    intro a _ b _
    exact Iff.symm (RelIso.map_rel_iff F)

instance : Coe (α ≃o β) (OrderIsoOn α β S) where
  coe := fun f => OrderIso.orderIsoOn f S

def OrderIsoOn.identity (α : Type _) [Preorder α] (S : Set α) : OrderIsoOn α α S where
  toFun := id
  invFun := id
  leftInv_on := by simp
  rightInv_on := by simp
  toFun_doubleMonotone := by simp [DoubleMonotoneOn]

@[simp]
theorem OrderIsoOn.comp_inv (F : OrderIsoOn α β S) : ∀ x ∈ S, (F.comp (F.inv)).toFun x = x := by
  simp
  exact F.leftInv_on

end Order

def Filter.InBasis {α : Type _} (F : Filter α) (B : Set (Set α)) : Prop :=
  ∀ (S : Set α), S ∈ F → ∃ T ∈ F, T ∈ B ∧ T ⊆ S

theorem Filter.InBasis.from_hasBasis {α ι : Type _} {F : Filter α} {pi : ι → Prop} {si : ι → Set α}
  (F_basis: Filter.HasBasis F pi si)
  {B : Set (Set α)}
  (basis_subset : ∀ i : ι, pi i → si i ∈ B) :
  Filter.InBasis F B :=
by
  intro S S_in_F
  let ⟨i, pi_i, si_i_ss⟩ := (F_basis.mem_iff' _).mp S_in_F
  use si i
  repeat' apply And.intro
  · rw [F_basis.mem_iff']
    use i
  · apply basis_subset
    assumption
  · assumption

variable {α : Type _} {F : Filter α} {B : Set (Set α)}

theorem Filter.InBasis.mono {C : Set (Set α)} (F_basis : F.InBasis B) (B_ss_C : B ⊆ C) :
  F.InBasis C :=
by
  intro S S_in_F
  let ⟨T, T_in_F, T_in_B, T_ss_S⟩ := F_basis S S_in_F
  use T
  repeat' apply And.intro
  any_goals apply B_ss_C
  all_goals assumption

def Filter.InBasis.basis {α : Type _} (F : Filter α) (B : Set (Set α)): Set (Set α) :=
  { S : Set α | S ∈ F ∧ S ∈ B }

theorem Filter.InBasis.basis_subset : Filter.InBasis.basis F B ⊆ B := fun _ ⟨_, S_in_B⟩ => S_in_B

theorem Filter.InBasis.basis_nonempty (F_basis : Filter.InBasis F B): Set.Nonempty (Filter.InBasis.basis F B) :=
by
  let ⟨T, T_in_F, T_in_B, _⟩ := F_basis Set.univ Filter.univ_mem
  exact ⟨T, T_in_F, T_in_B⟩

theorem Filter.InBasis.basis_nonempty' (F_basis : Filter.InBasis F B): Nonempty (Filter.InBasis.basis F B) :=
by
  rw [Set.nonempty_coe_sort]
  exact Filter.InBasis.basis_nonempty F_basis

theorem Filter.InBasis.basis_hasBasis (F_basis : Filter.InBasis F B):
  F.HasBasis (fun S => S ∈ Filter.InBasis.basis F B) id :=
by
  constructor
  intro T
  simp [basis, and_assoc]
  constructor
  · intro T_in_F
    exact F_basis T T_in_F
  · intro ⟨S, S_in_F, _, S_ss_T⟩
    exact mem_of_superset S_in_F S_ss_T

theorem Filter.InBasis.mem_image_basis_of_injective_on (F : Filter α) (B : Set (Set α))
  {f : Set α → Set β} (f_injective_on : Function.Injective (Set.restrict B f))
  {S : Set α} (S_in_B : S ∈ B) : f S ∈ f '' (basis F B) ↔ S ∈ basis F B :=
by
  simp
  constructor
  · intro ⟨T, T_in_basis, fT_eq_fS⟩
    have T_eq_S : (⟨T, T_in_basis.right⟩ : { s // s ∈ B }) = ⟨S, S_in_B⟩ := by
      apply f_injective_on
      simp
      exact fT_eq_fS
    simp at T_eq_S
    rw [<-T_eq_S]
    assumption
  · intro S_in_basis
    use S

def Filter.InBasis.map_basis {α β : Type _} (F : Filter α) (B : Set (Set α))
  (map : Set α → Set β): Filter β :=
  ⨅ (S : Filter.InBasis.basis F B), Filter.principal (map S)

lemma Filter.InBasis.map_directed {β : Type _} (F_basis : F.InBasis B)
  (map : Set α → Set β) (map_mono : MonotoneOn map B) :
  Directed (fun S T => S ≥ T) fun S : Filter.InBasis.basis F B => Filter.principal (map S.val) :=
by
  intro ⟨S, S_in_F, S_in_B⟩ ⟨T, T_in_F, T_in_B⟩
  have S_int_T_in_F : S ∩ T ∈ F := inter_mem S_in_F T_in_F
  let ⟨U, U_in_F, U_in_B, U_ss_ST⟩ := F_basis _ S_int_T_in_F
  let ⟨U_ss_S, U_ss_T⟩ := Set.subset_inter_iff.mp U_ss_ST
  use ⟨U, U_in_F, U_in_B⟩
  simp
  constructor <;> apply map_mono <;> assumption

theorem Filter.InBasis.map_basis_neBot {β : Type _} [Nonempty β] (F_basis : F.InBasis B)
  (map : Set α → Set β) (map_mono : MonotoneOn map B) (map_nonempty : ∀ S ∈ B, Set.Nonempty (map S)) :
  Filter.NeBot (Filter.InBasis.map_basis F B map) :=
by
  apply Filter.iInf_neBot_of_directed
  · exact Filter.InBasis.map_directed F_basis map map_mono
  · intro ⟨S, S_in_F, S_in_B⟩
    simp
    exact map_nonempty _ S_in_B

theorem Filter.InBasis.map_basis_neBot_of_neBot {β : Type _} [Nonempty β] [Filter.NeBot F] (F_basis : F.InBasis B)
  (map : Set α → Set β) (map_mono : MonotoneOn map B) (map_nonempty : ∀ S ∈ B, S.Nonempty → Set.Nonempty (map S)) :
  Filter.NeBot (Filter.InBasis.map_basis F B map) :=
by
  apply Filter.iInf_neBot_of_directed
  · exact Filter.InBasis.map_directed F_basis map map_mono
  · intro ⟨S, S_in_F, S_in_B⟩
    simp
    exact map_nonempty _ S_in_B (nonempty_of_mem S_in_F)

theorem Filter.InBasis.map_basis_hasBasis {β : Type _} (F_basis : F.InBasis B)
  (map : Set α → Set β) (map_mono : MonotoneOn map B):
  (Filter.InBasis.map_basis F B map).HasBasis (fun T : Set α => T ∈ F ∧ T ∈ B) map :=
by
  unfold map_basis
  constructor
  intro T

  have basis_nonempty := Filter.InBasis.basis_nonempty' F_basis

  rw [Filter.mem_iInf_of_directed]
  · simp [basis, <-and_assoc]
  · exact Filter.InBasis.map_directed F_basis map map_mono

theorem Filter.InBasis.map_basis_inBasis (F_basis : Filter.InBasis F B)
  (map : Set α → Set β) (map_mono : MonotoneOn map B) :
  (Filter.InBasis.map_basis F B map).InBasis (map '' B) :=
by
  intro S S_in_map

  have has_basis := Filter.InBasis.map_basis_hasBasis F_basis map map_mono

  rw [has_basis.mem_iff'] at S_in_map
  let ⟨T, ⟨T_in_F, T_in_B⟩, mapT_ss_S⟩ := S_in_map

  use map T
  repeat' apply And.intro
  · rw [has_basis.mem_iff']
    use T
  · simp
    use T
  · assumption

theorem Filter.InBasis.map_basis_id (F_basis : F.InBasis B) (f : Set α → Set α) (f_idlike : ∀ S ∈ B, f S = S):
  Filter.InBasis.map_basis F B f = F :=
by
  simp [map_basis]
  ext S
  have basis_nonempty := Filter.InBasis.basis_nonempty' F_basis
  have basis_directed := Filter.InBasis.map_directed F_basis id (monotone_id.monotoneOn B)
  simp only [id_eq] at basis_directed

  conv => {
    lhs
    rhs
    congr; intro x
    rw [f_idlike _ x.prop.right]
  }

  rw [mem_iInf_of_directed basis_directed]
  rw [(F_basis.basis_hasBasis).mem_iff]
  simp

theorem Filter.InBasis.mem_map_basis
  (map : Set α → Set β) (map_mono : MonotoneOn map B)
  (F_basis : Filter.InBasis F B) (x : Set β) :
  x ∈ (Filter.InBasis.map_basis F B map) ↔ ∃ y : Set α, y ∈ F ∧ y ∈ B ∧ map y ⊆ x :=
by
  rw [(Filter.InBasis.map_basis_hasBasis F_basis map map_mono).mem_iff]
  simp only [and_assoc]

theorem nhds_in_basis [TopologicalSpace α] (p : α) (B_basis : TopologicalSpace.IsTopologicalBasis B):
  (nhds p).InBasis B :=
  Filter.InBasis.from_hasBasis B_basis.nhds_hasBasis (fun _ ⟨in_B, _⟩ => in_B)

theorem Filter.InBasis.basis_map_basis (map : Set α → Set β)
  (map_double_mono : DoubleMonotoneOn map B) {F : Filter α} (F_basis : F.InBasis B):
  Filter.InBasis.basis (Filter.InBasis.map_basis F B map) (map '' B) = map '' (Filter.InBasis.basis F B) :=
by
  ext x
  simp [basis]
  rw [Filter.InBasis.mem_map_basis map map_double_mono.monotoneOn F_basis]
  constructor
  · intro ⟨⟨y1, y1_in_F, _, y1_ss_x⟩, ⟨y2, y2_in_B, y2_eq⟩⟩
    use y2
    rw [<-y2_eq] at y1_ss_x
    repeat' apply And.intro
    apply Filter.mem_of_superset y1_in_F
    rw [map_double_mono.subset_iff]
    all_goals assumption
  · intro ⟨S, ⟨S_in_F, S_in_B⟩, x_eq⟩
    constructor
    all_goals use S
    repeat' apply And.intro
    any_goals apply subset_of_eq
    all_goals assumption

theorem Filter.InBasis.mem_basis_iff_of_basis_set (F : Filter α) {S : Set α} (S_in_B : S ∈ B):
  S ∈ basis F B ↔ S ∈ F :=
by
  unfold basis
  simp
  tauto

theorem Filter.InBasis.map_mem_map_basis_of_basis_set (map : Set α → Set β) (map_double_mono : DoubleMonotoneOn map B)
  {F : Filter α} (F_basis : F.InBasis B) {S : Set α} (S_in_B : S ∈ B):
  map S ∈ Filter.InBasis.map_basis F B map ↔ S ∈ F :=
by
  suffices S ∈ basis F B ↔ map S ∈ map '' (basis F B) by
    rw [mem_basis_iff_of_basis_set _ S_in_B] at this
    rw [this]
    have mS_in_mB : map S ∈ map '' B := by
      simp
      use S
    rw [<-basis_map_basis map map_double_mono F_basis]
    rw [mem_basis_iff_of_basis_set _ mS_in_mB]

  rw [Filter.InBasis.mem_image_basis_of_injective_on _ _ map_double_mono.injective S_in_B]

-- TODO: clean this up :c
theorem Filter.InBasis.map_basis_comp {γ : Type _} (m₁ : OrderIsoOn (Set α) (Set β) B)
  (m₂ : OrderIsoOn (Set β) (Set γ) (m₁.toFun '' B))
  {F : Filter α} (F_basis : F.InBasis B):
  Filter.InBasis.map_basis (Filter.InBasis.map_basis F B m₁) (m₁.toFun '' B) m₂.toFun = Filter.InBasis.map_basis F B (m₁.comp m₂).toFun :=
by

  unfold map_basis
  rw [iInf_subtype]
  simp [basis_map_basis]
  rw [<-iInf_subtype (f := fun i => Filter.principal (m₂.toFun i.val))]

  have F'_basis := (Filter.InBasis.map_basis_inBasis F_basis m₁.toFun m₁.toFun_doubleMonotone.monotoneOn)
  have basis_nonempty : Nonempty { i // i ∈ basis (⨅ S : basis F B, Filter.principal (m₁.toFun ↑S)) (m₁.toFun '' B) } :=
    (Filter.InBasis.basis_nonempty' F'_basis).elim (fun p => ⟨p.val, p.prop⟩)

  have basis_nonempty' := Filter.InBasis.basis_nonempty' F_basis

  have m₁_directed := Filter.InBasis.map_directed F_basis m₁.toFun m₁.toFun_doubleMonotone.monotoneOn
  have m₂_directed := Filter.InBasis.map_directed F'_basis m₂.toFun m₂.toFun_doubleMonotone.monotoneOn

  have m₁₂_directed := Filter.InBasis.map_directed F_basis (m₁.comp m₂).toFun (m₁.comp m₂).toFun_doubleMonotone.monotoneOn
  simp only [OrderIsoOn.comp_toFun, Function.comp_apply] at m₁₂_directed

  ext S
  rw [mem_iInf_of_directed]
  swap
  assumption

  constructor
  · intro ⟨⟨T, ⟨T_in_basis, T_in_m₁B⟩⟩, m₂T_ss_S⟩
    simp at m₂T_ss_S
    apply Filter.mem_of_superset _ m₂T_ss_S
    rw [mem_iInf_of_directed]
    rw [mem_iInf_of_directed] at T_in_basis
    any_goals assumption

    let ⟨⟨U, ⟨U_in_F, U_in_B⟩⟩, m₁U_ss_T⟩ := T_in_basis
    simp at m₁U_ss_T
    use ⟨U, ⟨U_in_F, U_in_B⟩⟩

    simp
    apply m₂.toFun_doubleMonotone.monotoneOn
    use U
    all_goals assumption
  · intro S_in_iInf
    rw [mem_iInf_of_directed] at S_in_iInf
    any_goals assumption
    let ⟨⟨U, ⟨U_in_F, U_in_B⟩⟩, m₁₂U_ss_S⟩ := S_in_iInf
    simp at m₁₂U_ss_S

    have m₁U_in_basis : m₁.toFun U ∈ basis (map_basis F B m₁.toFun) (m₁.toFun '' B) := by
      constructor
      swap
      use U
      rw [map_mem_map_basis_of_basis_set m₁.toFun m₁.toFun_doubleMonotone]
      all_goals assumption

    use ⟨m₁.toFun U, m₁U_in_basis⟩
    assumption

theorem Filter.InBasis.map_basis_inv (map : OrderIsoOn (Set α) (Set β) B)
  {F : Filter α} (F_basis : F.InBasis B):
  Filter.InBasis.map_basis (Filter.InBasis.map_basis F B map) (map.toFun '' B) map.invFun = F :=
by
  rw [<-map.inv_toFun, map_basis_comp _ _ F_basis, map_basis_id F_basis]
  exact OrderIsoOn.comp_inv map

theorem Filter.InBasis.map_basis_inv' (map : OrderIsoOn (Set α) (Set β) B)
  {F : Filter β} (F_basis : F.InBasis (map.toFun '' B)):
  Filter.InBasis.map_basis (Filter.InBasis.map_basis F (map.toFun '' B) map.invFun) B map.toFun = F :=
by
  nth_rw 4 [<-OrderIsoOn.leftInv_image' map]
  rw [<-map.inv_toFun]
  nth_rw 5 [<-map.inv_invFun]
  rw [<-map.inv.inv_toFun]

  rw [map_basis_comp map.inv map.inv.inv F_basis, map_basis_id F_basis]

  simp
  intro a a_in_B
  rw [map.leftInv_on a a_in_B]


theorem Filter.InBasis.map_basis_mono (map : OrderIsoOn (Set α) (Set β) B)
  {F G : Filter α} (F_basis : F.InBasis B) (G_basis : G.InBasis B):
  Filter.InBasis.map_basis F B map.toFun ≤ Filter.InBasis.map_basis G B map.toFun → F ≤ G :=
by
  intro mF_le_mG S S_in_G
  let ⟨T, T_in_G, T_in_B, T_ss_S⟩ := G_basis S S_in_G
  have mT_in_mG : map.toFun T ∈ map_basis G B map.toFun := by
    rw [map_mem_map_basis_of_basis_set map.toFun map.toFun_doubleMonotone]
    all_goals assumption
  have mT_in_mF := mF_le_mG mT_in_mG
  rw [map_mem_map_basis_of_basis_set map.toFun map.toFun_doubleMonotone] at mT_in_mF
  apply Filter.mem_of_superset mT_in_mF
  all_goals assumption

theorem Filter.InBasis.map_basis_le_iff (map : OrderIsoOn (Set α) (Set β) B)
  {F G : Filter α} (F_basis : F.InBasis B) (G_basis : G.InBasis B):
  Filter.InBasis.map_basis F B map.toFun ≤ Filter.InBasis.map_basis G B map.toFun ↔ F ≤ G :=
by
  constructor
  · exact map_basis_mono map F_basis G_basis
  · nth_rw 1 [<-map_basis_inv map F_basis]
    nth_rw 1 [<-map_basis_inv map G_basis]
    rw [<-OrderIsoOn.inv_toFun]
    apply map_basis_mono
    all_goals apply map_basis_inBasis
    any_goals assumption
    all_goals exact map.toFun_doubleMonotone.monotoneOn

theorem Filter.InBasis.map_basis_inBasis' (map : OrderIsoOn (Set α) (Set β) B)
  {F : Filter α} (F_basis : F.InBasis B) :
  (Filter.InBasis.map_basis F B map.toFun).InBasis (map.toFun '' B) :=
by
  apply map_basis_inBasis
  assumption
  exact map.toFun_doubleMonotone.monotoneOn

theorem Filter.InBasis.map_basis_inBasis₂ (map : OrderIsoOn (Set α) (Set β) B)
  {F : Filter β} (F_basis : F.InBasis (map.toFun '' B)) :
  (Filter.InBasis.map_basis F (map.toFun '' B) map.invFun).InBasis B :=
by
  nth_rw 4 [<-OrderIsoOn.leftInv_image map]
  rw [Function.comp_def, <-Set.image_image map.invFun]
  apply map_basis_inBasis
  assumption
  exact map.invFun_doubleMonotone.monotoneOn

theorem Filter.InBasis.map_basis_le_inv (map : OrderIsoOn (Set α) (Set β) B)
  {F : Filter α} (F_basis : F.InBasis B) {G : Filter β} (G_basis : G.InBasis (map.toFun '' B)) :
  F ≤ Filter.InBasis.map_basis G (map.toFun '' B) map.invFun ↔ Filter.InBasis.map_basis F B map.toFun ≤ G :=
by
  suffices ∀ map : OrderIsoOn (Set α) (Set β) B, ∀ F : Filter α, F.InBasis B → ∀ G : Filter β, G.InBasis (map.toFun '' B) →
    F ≤ Filter.InBasis.map_basis G (map.toFun '' B) map.invFun →
    Filter.InBasis.map_basis F B map.toFun ≤ G
  by
    constructor
    · apply this
      all_goals assumption
    · intro mF_le_G

      nth_rw 1 [<-map_basis_inv map F_basis]
      apply map_basis_mono map
      rw [map_basis_inv _ F_basis]
      any_goals assumption
      apply map_basis_inBasis₂
      assumption

      apply this
      rw [map_basis_inv _ F_basis]
      assumption
      apply map_basis_inBasis'
      apply map_basis_inBasis₂
      assumption

      rw [<-OrderIsoOn.inv_toFun, map_basis_le_iff]

      · simp
        rw [map_basis_inv']
        exact mF_le_G
        assumption
      · apply map_basis_inBasis'
        assumption
      · simp
        rw [map_basis_inv']
        all_goals assumption

  intro map F F_basis G G_basis F_le_mG
  intro S S_in_G
  let ⟨T, T_in_G, T_in_mB, T_ss_S⟩ := G_basis S S_in_G
  let ⟨U, U_in_B, T_eq⟩ := T_in_mB
  have mT_in_F : map.invFun T ∈ F := by
    apply F_le_mG
    rw [map_mem_map_basis_of_basis_set]
    any_goals assumption
    exact map.invFun_doubleMonotone
  have U_in_F : U ∈ F := by
    rw [<-T_eq] at mT_in_F
    rw [map.leftInv_on U U_in_B] at mT_in_F
    exact mT_in_F

  apply Filter.mem_of_superset
  · show map.toFun U ∈ map_basis F B map.toFun
    rw [map_mem_map_basis_of_basis_set]
    any_goals assumption
    exact map.toFun_doubleMonotone
  · rw [T_eq]
    assumption

theorem Filter.InBasis.map_basis_le_inv' (map : OrderIsoOn (Set α) (Set β) B)
  {F : Filter α} (F_basis : F.InBasis B) {G : Filter β} (G_basis : G.InBasis (map.toFun '' B)) :
  G ≤ Filter.InBasis.map_basis F B map.toFun ↔ Filter.InBasis.map_basis G (map.toFun '' B) map.invFun ≤ F :=
by
  nth_rw 1 [<-map.leftInv_image']
  rw [<-map.inv_invFun, <-map.inv_toFun]

  rw [<-map.leftInv_image', <-map.inv_toFun] at F_basis
  nth_rw 1 [map.inv_invFun]
  rw [Filter.InBasis.map_basis_le_inv _ G_basis F_basis]
  simp

instance Filter.InBasis.order_bot : OrderBot { F : Filter α // F.InBasis B ∨ F = ⊥ } where
  bot := ⟨⊥, by right; rfl ⟩
  bot_le := by
    intro ⟨F, _⟩
    simp

theorem Filter.InBasis.inf {F₁ F₂ : Filter α}
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  (F₁_basis : F₁.InBasis B) (F₂_basis : F₂.InBasis B) (nebot : Filter.NeBot (F₁ ⊓ F₂)):
  Filter.InBasis (F₁ ⊓ F₂) B :=
by
  intro T T_in_inf

  let ⟨T₁, T₁_in_F₁, T₂, T₂_in_F₂, T_eq⟩ := Filter.mem_inf_iff.mp T_in_inf

  let ⟨S₁, S₁_in_F₁, S₁_in_B, S₁_ss_T₁⟩ := F₁_basis T₁ T₁_in_F₁
  let ⟨S₂, S₂_in_F₂, S₂_in_B, S₂_ss_T₂⟩ := F₂_basis T₂ T₂_in_F₂

  let S := S₁ ∩ S₂
  have S_in_inf : S ∈ F₁ ⊓ F₂ := by
    rw [Filter.mem_inf_iff]
    exact ⟨S₁, S₁_in_F₁, S₂, S₂_in_F₂, rfl⟩

  use S
  repeat' apply And.intro
  · assumption
  · apply basis_closed S₁ S₂ S₁_in_B S₂_in_B
    apply Filter.nonempty_of_mem S_in_inf
  · rw [T_eq]
    exact Set.inter_subset_inter S₁_ss_T₁ S₂_ss_T₂

theorem Finset.Nonempty.induction {α : Type*} {p : ∀ (F : Finset α), F.Nonempty → Prop} [DecidableEq α] (initial : ∀ a : α, p {a} (Finset.singleton_nonempty a))
  (insert : ∀ ⦃a : α⦄ {s : Finset α} (ne : s.Nonempty), a ∉ s → p s ne → p (insert a s) (Finset.insert_nonempty _ _)) : ∀ s, ∀ ne : s.Nonempty, p s ne :=
by
  intro S S_nonempty
  apply Finset.Nonempty.cons_induction
  exact initial
  intro a s a_notin_s s_nonempty p_rec
  simp
  apply insert <;> assumption

theorem Finset.Nonempty.induction_on' {α : Type*} {p : ∀ (F : Finset α), F.Nonempty → Prop} [DecidableEq α] {I : Finset α} (I_nonempty : I.Nonempty)
  (initial : ∀ a : α, a ∈ I → p {a} (Finset.singleton_nonempty a))
  (insert : ∀ ⦃a : α⦄ {s : Finset α} (ne : s.Nonempty), a ∈ I → a ∉ s → (s ⊆ I) → p s ne → p (insert a s) (Finset.insert_nonempty _ _)) :
  p I I_nonempty :=
by
  suffices ∀ (s : Finset α) (ne : Finset.Nonempty s), s ⊆ I → p s ne by
    exact this I I_nonempty (subset_refl _)
  apply Finset.Nonempty.induction
  · simp only [singleton_subset_iff]
    exact initial
  · intro a s ne a_notin_s h_ind insert_subset
    have ⟨a_in_I, s_ss_I⟩ := Finset.insert_subset_iff.mp insert_subset
    specialize h_ind s_ss_I
    apply insert <;> try assumption

theorem Filter.InBasis.sInf_finset_closed
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  (I : Finset (Filter α))
  (I_nonempty : Finset.Nonempty I)
  (in_basis : ∀ (F : Filter α), F ∈ I → F.InBasis B)
  (inf_nebot : Filter.NeBot (⨅ f ∈ I, f)) :
  Filter.InBasis (sInf I) B := by
  rw [sInf_eq_iInf]
  have dec_eq: DecidableEq (Filter α) := Classical.typeDecidableEq _

  apply Finset.Nonempty.induction_on' I_nonempty (p := fun i _ => InBasis (⨅ a ∈ i, a) B)
  · simp
    exact in_basis
  · intro F I' _ F_in_I _ I'_ss_I I'_basis
    simp only [Finset.mem_insert_coe]
    rw [iInf_insert (s := I') (b := F)]
    apply Filter.InBasis.inf basis_closed _ I'_basis
    · simp only [<-Finset.mem_coe]
      show NeBot (id F ⊓ ⨅ f ∈ (I' : Set (Filter α)), id f)
      rw [<-iInf_insert]
      apply Filter.neBot_of_le (f := ⨅ f ∈ I, f)
      simp only [id_eq, <-Finset.mem_coe]
      apply iInf_le_iInf_of_subset
      exact Set.insert_subset F_in_I I'_ss_I
    · exact in_basis _ F_in_I

theorem Filter.InBasis.sInf_closed
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  (I : Set (Filter α))
  (I_nonempty : I.Nonempty)
  (in_basis : ∀ (F : Filter α), F ∈ I → F.InBasis B)
  (inf_nebot : Filter.NeBot (⨅ f ∈ I, f)) :
  Filter.InBasis (sInf I) B :=
by
  rw [sInf_eq_iInf]

  have iInf_I_eq := iInf_subtype (p := fun x => x ∈ I) (f := fun x => x.val)
  simp only at iInf_I_eq

  intro T T_in_sinf
  rw [<-iInf_I_eq] at T_in_sinf
  rw [Filter.mem_iInf_finite] at T_in_sinf
  let ⟨S, T_in_inf⟩ := T_in_sinf
  clear T_in_sinf
  have dec_eq: DecidableEq (Filter α) := Classical.typeDecidableEq _

  let S' := Finset.image (Subtype.val) S

  have S'_eq : ⨅ i ∈ S, i.val = sInf S' := by
    rw [sInf_eq_iInf]
    simp
    rw [iInf_subtype]

  by_cases S_empty? : S.Nonempty
  swap
  {
    rw [Finset.not_nonempty_iff_eq_empty] at S_empty?
    rw [S_empty?] at T_in_inf
    simp at T_in_inf
    let ⟨F, F_in_I⟩ := I_nonempty
    rw [T_in_inf]
    simp
    let ⟨U, U_in_F, U_in_B, _⟩ := in_basis F F_in_I Set.univ Filter.univ_mem
    use U
    constructor
    · rw [<-iInf_I_eq]
      apply Filter.mem_iInf_of_mem ⟨F, F_in_I⟩
      exact U_in_F
    · assumption
  }

  have S_basis : Filter.InBasis (⨅ i ∈ S, i.val) B := by
    rw [S'_eq]
    apply Filter.InBasis.sInf_finset_closed basis_closed
    · simp
      assumption
    · unfold_let
      simp only [Finset.mem_image]
      intro F ⟨F', _, F'_eq⟩
      apply in_basis
      rw [<-F'_eq]
      exact Subtype.mem F'
    · simp only [<-Finset.mem_coe]
      apply Filter.neBot_of_le (f := ⨅ f ∈ I, f)
      apply iInf_le_iInf_of_subset
      simp

  have S_inf_le_I_inf : ⨅ i ∈ I, i ≤ ⨅ i ∈ S, i.val := by
    rw [S'_eq, sInf_eq_iInf]
    apply iInf_le_iInf_of_subset
    simp

  let ⟨U, U_in_inf, U_in_B, U_ss_T⟩ := S_basis T T_in_inf
  use U
  repeat' apply And.intro
  apply S_inf_le_I_inf
  all_goals assumption

theorem Filter.InBasis.is_atomic
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  : IsAtomic { F : Filter α // F.InBasis B ∨ F = ⊥ } :=
by
  by_cases α_nonempty? : Nonempty α
  swap
  {
    rw [not_nonempty_iff] at α_nonempty?
    constructor
    intro ⟨F, _⟩
    left
    simp
    exact filter_eq_bot_of_isEmpty F
  }

  apply IsAtomic.of_isChain_bounded
  intro ch ch_chain ch_nonempty bot_notin_ch

  let ch' : Set (Filter α) := { F : Filter α | ∃ F' ∈ ch, F'.val = F }

  have ch'_chain : IsChain (fun x y => x ≥ y) ch' := by
    intro f f_in_ch' g g_in_ch' f_ne_g
    let ⟨f', f'_in_ch, f'_eq⟩ := f_in_ch'
    let ⟨g', g'_in_ch, g'_eq⟩ := g_in_ch'
    rw [<-f'_eq, <-g'_eq]
    simp
    have f'_ne_g' : f' ≠ g' := by
      intro eq
      rw [eq] at f'_eq
      rw [<-f'_eq, <-g'_eq] at f_ne_g
      exact f_ne_g rfl
    symm at f'_ne_g'
    apply ch_chain <;> assumption

  have bot_notin_ch' : ⊥ ∉ ch' := by
    intro ⟨f, f_in_ch, f_eq_bot⟩
    simp at f_eq_bot
    rw [f_eq_bot] at f_in_ch
    exact bot_notin_ch f_in_ch

  let sch := sInf ch'
  have sch_neBot : Filter.NeBot sch := by
    apply Filter.sInf_neBot_of_directed
    apply ch'_chain.directedOn
    exact bot_notin_ch'

  have sch_basis : InBasis sch B := by
    apply Filter.InBasis.sInf_closed basis_closed
    · let ⟨x, x_in_ch⟩ := ch_nonempty
      use x
      simp only [Subtype.exists, exists_and_right, exists_eq_right, Set.mem_setOf_eq,
        Subtype.coe_eta, or_true, exists_prop]
      exact ⟨x.prop, x_in_ch⟩
    · intro F F_in_ch
      simp at F_in_ch
      let ⟨F_basis, F_in_ch⟩ := F_in_ch
      apply F_basis.elim id
      intro F_eq_bot
      exfalso
      simp [F_eq_bot] at F_in_ch
      exact bot_notin_ch F_in_ch
    · rw [<-sInf_eq_iInf]
      exact sch_neBot

  let sch' : { F // InBasis F B ∨ F = ⊥ } := ⟨sch, Or.intro_left _ sch_basis⟩
  have sch'_ne_bot : sch' ≠ ⊥ := by
    unfold_let
    intro eq_bot
    rw [Subtype.mk_eq_bot_iff] at eq_bot
    apply sch_neBot.ne
    exact eq_bot
    right; rfl

  use sch'
  use sch'_ne_bot
  unfold lowerBounds
  simp
  intro F F_basis F_in_ch
  apply sInf_le
  simp
  use F_basis

/--
This is a formulation for prefilters in a basis that are ultra.
It is a weaker statement than regular ultrafilters,
but it allows for some nice properties, like the equivalence of cluster points and neighborhoods.
--/
structure UltrafilterInBasis {α : Type} (B : Set (Set α)) :=
  filter : Filter α

  in_basis : Filter.InBasis filter B

  ne_bot : Filter.NeBot filter

  ultra : ∀ (F' : Filter α), F'.InBasis B → F'.NeBot → F' ≤ filter → filter ≤ F'

instance : CoeOut (@UltrafilterInBasis α B) (Filter α) where
  coe := fun U => U.filter

instance : Membership (Set α) (UltrafilterInBasis B) where
  mem := fun s U => s ∈ U.filter

instance (U : UltrafilterInBasis B): Filter.NeBot (U.filter) where
  ne' := U.ne_bot.ne

theorem UltrafilterInBasis.exists_le {α: Type _} {F : Filter α} {B : Set (Set α)}
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  (F_basis : Filter.InBasis F B) (F_nebot : Filter.NeBot F) :
  ∃ U : UltrafilterInBasis B, U.filter ≤ F :=
by
  have atomic := Filter.InBasis.is_atomic basis_closed
  let ⟨⟨U, U_basis⟩, U_atom, U_le_F⟩ := (atomic.eq_bot_or_exists_atom_le ⟨F, Or.intro_left _ F_basis⟩).resolve_left (by simp; exact F_nebot.ne)

  have U_neBot : Filter.NeBot U := by
    constructor
    have U_atom := U_atom.left
    simp at U_atom
    exact U_atom

  have U_basis := U_basis.resolve_right U_neBot.ne

  simp at U_le_F

  have U_ultra : ∀ (F' : Filter α), F'.InBasis B → F'.NeBot → F' ≤ U → U ≤ F' := by
    intro F' F'_basis F'_neBot F'_le
    have U_atom := U_atom.right
    simp at U_atom
    cases le_iff_lt_or_eq.mp F'_le with
    | inl F'_lt_U =>
      exfalso
      apply F'_neBot.ne
      apply U_atom
      left
      all_goals assumption
    | inr F'_eq_U =>
      symm at F'_eq_U
      exact le_of_eq F'_eq_U

  use ⟨U, U_basis, U_neBot, U_ultra⟩

noncomputable def UltrafilterInBasis.of {α: Type _} {F : Filter α} {B : Set (Set α)}
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  (F_basis : Filter.InBasis F B) [F_neBot : Filter.NeBot F]:
  UltrafilterInBasis B := Exists.choose $ UltrafilterInBasis.exists_le basis_closed F_basis F_neBot

theorem UltrafilterInBasis.of_le {α: Type _} {F : Filter α} {B : Set (Set α)}
  (basis_closed : ∀ b1 b2 : Set α, b1 ∈ B → b2 ∈ B → Set.Nonempty (b1 ∩ b2) → b1 ∩ b2 ∈ B)
  (F_basis : Filter.InBasis F B) [F_neBot : Filter.NeBot F]:
  UltrafilterInBasis.of basis_closed F_basis ≤ F :=
by
  exact Exists.choose_spec $ UltrafilterInBasis.exists_le basis_closed F_basis F_neBot

theorem UltrafilterInBasis.coe_in_basis (U : UltrafilterInBasis B) : Filter.InBasis U B := U.in_basis

theorem UltrafilterInBasis.map_basis_ultra [Nonempty α] {β : Type _}
  (empty_notin_B : ∅ ∉ B)
  (map : OrderIsoOn (Set α) (Set β) B)
  (U : UltrafilterInBasis B) :
  ∀ (F : Filter β), F.InBasis (map.toFun '' B) → F.NeBot →
  F ≤ Filter.InBasis.map_basis U B map → Filter.InBasis.map_basis U B map ≤ F :=
by
  intro F F_in_basis F_neBot F_le_map
  intro S S_in_F

  have U_le_mF : U ≤ Filter.InBasis.map_basis F (map.toFun '' B) map.invFun := by
    apply U.ultra
    apply Filter.InBasis.map_basis_inBasis₂
    · assumption
    · apply Filter.InBasis.map_basis_neBot_of_neBot F_in_basis
      exact map.invFun_doubleMonotone.monotoneOn
      intro S ⟨T, T_in_B, T_eq⟩ _
      rw [<-T_eq, map.leftInv_on T T_in_B]
      rw [Set.nonempty_iff_ne_empty]
      intro T_empty
      exact empty_notin_B (T_empty ▸ T_in_B)
    · rw [Filter.InBasis.map_basis_le_inv' map U.in_basis F_in_basis] at F_le_map
      exact F_le_map

  rw [Filter.InBasis.mem_map_basis map.toFun map.toFun_doubleMonotone.monotoneOn U.in_basis]
  let ⟨T, T_in_F, T_in_mB, T_ss_S⟩ := F_in_basis S S_in_F

  use map.invFun T
  repeat' apply And.intro
  · apply U_le_mF
    rw [Filter.InBasis.map_mem_map_basis_of_basis_set]
    any_goals assumption
    exact map.invFun_doubleMonotone
  · apply map.mem_toFun_image
    assumption
  · rw [map.rightInv_on T T_in_mB]
    exact T_ss_S

def UltrafilterInBasis.map_basis [Nonempty α] {β : Type _} [Nonempty β]
  (empty_notin_B : ∅ ∉ B)
  (map : OrderIsoOn (Set α) (Set β) B)
  (empty_notin_mB : ∅ ∉ map.toFun '' B)
  (U : UltrafilterInBasis B) :
  UltrafilterInBasis (map.toFun '' B)
where
  filter := Filter.InBasis.map_basis U.filter B map.toFun
  in_basis := Filter.InBasis.map_basis_inBasis' map U.in_basis
  ne_bot := by
    apply Filter.InBasis.map_basis_neBot_of_neBot U.in_basis _ map.toFun_doubleMonotone.monotoneOn
    intro S S_in_B _
    by_contra empty
    rw [Set.not_nonempty_iff_eq_empty] at empty
    apply empty_notin_mB
    use S
  ultra := by
    apply map_basis_ultra
    exact empty_notin_B

-- TODO: show that clusterpt iff le nhds if B is a topological basis
