package docforge

// VariableResolver populates one namespace of the placeholder bag.
//
// Each resolver owns a dotted namespace (e.g. "project", "parties") and
// pushes its keys into a shared PlaceholderMap. The push model — rather
// than a pull Resolve(key) — is deliberate: some namespaces emit a
// data-dependent set of keys (a multi-party suit produces
// parties.claimant.0.name, .1.name, … one per party), which a fixed
// key-by-key pull interface can't enumerate cleanly. Populate lets each
// resolver decide its own (possibly dynamic) key set in one pass.
//
// The consuming application implements concrete resolvers against its own
// data sources (paliad resolves project/party/deadline state from its
// Postgres database); docforge owns only the interface and the
// composition machinery (ResolverSet). This is the seam a second consumer
// (e.g. upc-commentary) plugs its own resolvers into without touching the
// engine.
type VariableResolver interface {
	// Namespace returns the dotted prefix this resolver owns, e.g.
	// "project". Informational — used for diagnostics and as the default
	// group for this resolver's catalogue entries.
	Namespace() string

	// Populate writes this resolver's keys into bag. Resolvers own
	// disjoint namespaces, so population order across resolvers does not
	// affect the final bag.
	Populate(bag PlaceholderMap)

	// Keys returns the user-facing catalogue entries for this resolver —
	// the variables an authoring palette can offer and a sidebar form can
	// render, each with its bilingual label. This is the curated, static
	// surface (e.g. the flat parties.claimant.name form), not the full
	// possibly-dynamic key set Populate emits (e.g. the indexed
	// parties.claimant.0.name). Go owns these labels so the frontend form
	// and the authoring palette read one source of truth instead of a
	// duplicated TS table.
	Keys() []VariableKey
}

// VariableKey is one catalogue entry: the placeholder key plus its
// bilingual label and a group (the owning namespace by default). The
// frontend maps groups onto its own lawyer-facing presentation sections.
type VariableKey struct {
	Key     string `json:"key"`
	LabelDE string `json:"label_de"`
	LabelEN string `json:"label_en"`
	Group   string `json:"group"`
}

// ResolverSet composes an ordered list of VariableResolvers into a single
// PlaceholderMap. It is the replacement for hard-coded "call addFooVars,
// then addBarVars, …" sequencing: a consumer registers the resolvers that
// apply to a given render and calls BuildBag.
type ResolverSet struct {
	resolvers []VariableResolver
}

// NewResolverSet builds a set from the given resolvers, in order.
func NewResolverSet(resolvers ...VariableResolver) *ResolverSet {
	return &ResolverSet{resolvers: resolvers}
}

// Add appends a resolver to the set.
func (s *ResolverSet) Add(r VariableResolver) { s.resolvers = append(s.resolvers, r) }

// BuildBag runs every resolver's Populate into a fresh PlaceholderMap and
// returns it. Because resolvers own disjoint namespaces, the result is
// independent of resolver order.
func (s *ResolverSet) BuildBag() PlaceholderMap {
	bag := PlaceholderMap{}
	for _, r := range s.resolvers {
		r.Populate(bag)
	}
	return bag
}

// Catalogue concatenates every resolver's Keys() in resolver order — the
// full set of user-facing variables for a palette or form, with bilingual
// labels. It does not require any per-call entity state, so a consumer can
// build a metadata-only ResolverSet (resolvers constructed with nil
// entities) purely to serve the catalogue.
func (s *ResolverSet) Catalogue() []VariableKey {
	var out []VariableKey
	for _, r := range s.resolvers {
		out = append(out, r.Keys()...)
	}
	return out
}
