// Copyright 2026 printing-press-golden and contributors. Licensed under Apache-2.0. See LICENSE.
// Generated by CLI Printing Press (https://github.com/mvanhorn/cli-printing-press). DO NOT EDIT.

// learn_init.go is the spec-driven init shim for the self-learning loop.
// newLearnConfig builds the per-CLI *entities.Config the cobra teach /
// recall / learnings commands thread through every call site;
// initLearn seeds the entity_lookups table from the canonical-+-aliases
// table declared in spec.Learn.EntityLookupSeeds. Both run once at
// startup; subsequent calls are idempotent (INSERT OR IGNORE on the
// store side, fresh Config build on the registration side).
//
// Soft validation: a runtime regex compile failure (which spec parse
// already rejects at load time) logs to stderr and continues without
// the pattern instead of aborting the CLI. Empty Learn config produces
// a Config with built-in stopwords and a no-op initLearn.

package cli

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"regexp"
	"sync"

	"learn-loop-example-pp-cli/internal/learn/entities"
	"learn-loop-example-pp-cli/internal/learn/lookups"
	"learn-loop-example-pp-cli/internal/store"
)

// newLearnConfig returns the per-CLI entity extractor Config the
// teach / recall / normalize call sites pass into the learn package.
// Reads the printed-CLI's spec.Learn.TickerPatterns and Stopwords;
// patterns that fail to compile at runtime (defense in depth — spec
// parse already validated them) log a warning and are skipped.
func newLearnConfig() *entities.Config {
	cfg := entities.NewConfig()
	if re, err := regexp.Compile(`^EXAMPLE-[A-Z0-9]+(-[A-Z0-9]+)*$`); err != nil {
		fmt.Fprintf(os.Stderr, "warning: learn-loop-example-pp-cli: ticker pattern %q failed to compile: %v\n", `^EXAMPLE-[A-Z0-9]+(-[A-Z0-9]+)*$`, err)
	} else {
		cfg.RegisterTickerPattern(re)
	}
	cfg.RegisterStopwords(
		"filler1",
		"filler2",
	)
	return cfg
}

// initLearn seeds the entity_lookups table from the spec.Learn.
// EntityLookupSeeds declaration. INSERT OR IGNORE on the store side
// makes this idempotent — calling it twice produces the same DB state
// as calling it once. An empty seed map is a no-op (returns nil).
func initLearn(ctx context.Context, db *sql.DB) error {
	_ = ctx
	seeds := map[string][]lookups.SeedConfig{
		"thing": {
			{Canonical: "ALPHA", Values: []string{"Alpha", "alpha-canonical"}},
			{Canonical: "BRAVO", Values: []string{"Bravo", "br"}},
		},
	}
	if _, err := lookups.SeedFromConfig(db, seeds); err != nil {
		return fmt.Errorf("initLearn: seed entity_lookups: %w", err)
	}
	return nil
}

// learnInitOnce gates runLearnInitOnce so the seed pass happens at
// most once per CLI process. The sync.Once keeps the cost off the
// hot path for repeat command invocations within an MCP session.
var learnInitOnce sync.Once

// runLearnInitOnce opens the canonical store path, fires initLearn
// once per process, and downgrades any failure to a stderr warning.
// Per the self-learning loop's soft-validation contract, a seed
// failure must never abort the CLI — the recall path returns the
// empty envelope if entity_lookups is missing rows, which is the
// same behavior an opt-out CLI sees.
func runLearnInitOnce(ctx context.Context) {
	learnInitOnce.Do(func() {
		dbPath := defaultDBPath("learn-loop-example-pp-cli")
		s, err := store.OpenWithContext(ctx, dbPath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "warning: learn-loop-example-pp-cli: learn init: open store: %v\n", err)
			return
		}
		defer s.Close()
		if err := initLearn(ctx, s.DB()); err != nil {
			fmt.Fprintf(os.Stderr, "warning: learn-loop-example-pp-cli: learn init: %v\n", err)
		}
	})
}
