/-
  Infoductor.Foundation.MacroAlias — `@[macroAlias]` attribute
  ===============================================================
  Phase C of `docs/ALGEBRA_PLAN.md`.  Provides the user-extensible
  registry of frozen `restructure` invocations.

  When a developer notices that a `restructure` invocation pattern
  recurs ("oh, this is just a relocate-with-namespace-fixup"), they
  attach `@[macroAlias]` to a `def` that wraps the pattern.  The
  attribute records the alias in a global registry (`AliasRegistry`)
  keyed by name.  The widget can then suggest "name this pattern as
  X" when an instantiation matches an existing alias's signature.

  Per ALGEBRA_PLAN §2.3:
  > The codebase ships with `restructure` itself (~150 lines).  Each
  > `@[macroAlias] def …` is a 1–3-line shorthand.

  This module provides the attribute and the registry; concrete
  alias declarations (`transport_artifact`, `relocate_invariant`,
  …) live in `Algebra/Restructure.lean`.
-/

import Lean
import Infoductor.Foundation.Restructure

namespace Infoductor

open Lean

-- ── The alias registry ──────────────────────────────────────────────────────

/-- An entry in the alias registry: a `Name` paired with its
    documentary description (extracted from the `def`'s docstring
    if present). -/
structure AliasEntry where
  name        : Name
  description : String := ""
  deriving Repr, Inhabited

/-- Global registry of `@[macroAlias]`-tagged declarations, indexed
    by name.  Implemented as a Lean `EnvExtension` so that adds in
    one module are visible from any importing module.

    Per ALGEBRA_PLAN §10 OQ #2 the registry is on-the-fly; persistence
    via `lake exe algebra-cache` is deferred. -/
initialize aliasRegistryExt :
    SimplePersistentEnvExtension AliasEntry (Array AliasEntry) ←
  registerSimplePersistentEnvExtension {
    name          := `Infoductor.aliasRegistry
    addEntryFn    := fun arr e => arr.push e
    addImportedFn := fun arrs => arrs.foldl (init := #[]) Array.append
    asyncMode     := .sync
  }

/-- Look up every registered alias.  Order is unspecified (no
    guarantees beyond "all registered entries are in the array"). -/
def getAliases : CoreM (Array AliasEntry) := do
  let env ← getEnv
  return aliasRegistryExt.getState env

/-- Register an alias entry.  Inserts into the env extension. -/
def registerAlias (entry : AliasEntry) : CoreM Unit := do
  modifyEnv (aliasRegistryExt.addEntry · entry)

-- ── The `@[macroAlias]` attribute ───────────────────────────────────────────

/-- Lean attribute syntax registered as `@[macroAlias]`.  Attached to
    a `def` to register it as a frozen `restructure` invocation
    accessible via `Infoductor.getAliases`. -/
initialize macroAliasAttr : Unit ←
  registerBuiltinAttribute {
    name := `macroAlias
    descr := "Register this declaration as a `restructure` alias \
              (Phase C of ALGEBRA_PLAN.md).  The decl's name and \
              docstring become an `AliasEntry` in the global \
              `aliasRegistryExt`."
    add := fun declName _stx _kind => do
      let env ← getEnv
      let docstring? := (← findDocString? env declName).getD ""
      registerAlias { name := declName, description := docstring? }
  }

-- ── Diagnostics ─────────────────────────────────────────────────────────────

/-- Print the current alias registry to `IO.println`.  Used by
    `lake exe algebra-list-aliases` and the widget's "show registered
    aliases" button. -/
def printAliases : CoreM Unit := do
  let aliases ← getAliases
  IO.println s!"── Algebra macro-alias registry ({aliases.size}) ──"
  for entry in aliases do
    if entry.description.isEmpty then
      IO.println s!"  {entry.name}"
    else
      IO.println s!"  {entry.name} — {entry.description}"

end Infoductor
