import Lean.Elab.Binders
import Lean.Elab.Tactic.Basic
import Lean.Meta.Tactic.Intro

/-!
# `let_intros` Tactic

`let_intros` is a weaker form of `intros` aimed to only introduce `let` statements,
but not for example `∀`-binders.
-/

namespace GameServer

open Lean Meta Elab Parser Tactic

/--
Copied from `Lean.Meta.getIntrosSize`.
-/
private partial def getLetIntrosSize : Expr → Nat
  -- | .forallE _ _ b _ => getLetIntrosSize b + 1
  | .letE _ _ _ b _  => getLetIntrosSize b + 1
  | .mdata _ b       => getLetIntrosSize b
  | e                =>
    if let some (_, _, _, b) := e.letFun? then
      getLetIntrosSize b + 1
    else
      0

/--
Copied and from `Lean.MVarId.intros`.
-/
def _root_.Lean.MVarId.letIntros (mvarId : MVarId) : MetaM (Array FVarId × MVarId) := do
  let type ← mvarId.getType
  let type ← instantiateMVars type
  let n := getLetIntrosSize type
  if n == 0 then
    return (#[], mvarId)
  else
    -- `introNP` preserves the binder names
    mvarId.introNP n

/--
`let_intros` introduces all `let` statements that are preceeding the proof. Concretely
it does a subset of what `intros` does.

If names are provided, it will introduce as many `let` statements as there are names.
-/
syntax (name := letIntros) "let_intros" : tactic
-- (ppSpace colGt (ident <|> hole))*

#check letIntros

@[tactic letIntros] def evalLetIntros : Tactic := fun stx => do
  match stx with
  | `(tactic| let_intros) => liftMetaTactic fun mvarId => do
    let (_, mvarId) ← mvarId.letIntros
    return [mvarId]
  -- | `(tactic| let_intros $ids*) => do
  --   let fvars ← liftMetaTacticAux fun mvarId => do
  --     let (fvars, mvarId) ← mvarId.introN ids.size (ids.map getNameOfIdent').toList
  --     return (fvars, [mvarId])
  --   withMainContext do
  --     for stx in ids, fvar in fvars do
  --       Term.addLocalVarInfo stx (mkFVar fvar)
  | _ => throwUnsupportedSyntax
