// Package filters — kb_inject.go
//
// v0.29.0 CS0: Built-in pre-completion filter that auto-injects KB
// context into the system prompt. Refactored from the inline
// BuildKBHint() function in knowledge_bases.go.
//
// This is the reference implementation for the filter chain model
// that Starlark extensions will mirror in CS3.
package filters

import (
	"context"
	"fmt"
	"log"

	"chat-switchboard/store"
)

// KBInjectFilter injects a system prompt listing active knowledge bases
// so the LLM knows to use the kb_search tool.
type KBInjectFilter struct {
	stores store.Stores
}

// NewKBInjectFilter creates the built-in KB auto-inject filter.
func NewKBInjectFilter(stores store.Stores) *KBInjectFilter {
	return &KBInjectFilter{stores: stores}
}

func (f *KBInjectFilter) Name() string { return "kb-auto-inject" }
func (f *KBInjectFilter) Order() int   { return 10 } // built-in, runs early

func (f *KBInjectFilter) Execute(ctx context.Context, cc *CompletionContext) (*Contribution, error) {
	type kbInfo struct {
		Name     string
		DocCount int
	}
	var kbs []kbInfo
	seen := make(map[string]bool)

	// ── Persona-bound KBs (v0.17.0) ──
	if cc.PersonaID != "" {
		personaKBs, err := f.stores.Personas.GetKBs(ctx, cc.PersonaID)
		if err == nil {
			for _, pkb := range personaKBs {
				if pkb.ChunkCount > 0 {
					kbs = append(kbs, kbInfo{Name: pkb.KBName, DocCount: pkb.DocumentCount})
					seen[pkb.KBID] = true
				}
			}
		}
	}

	// ── Project-bound KBs (v0.19.0) ──
	if f.stores.Projects != nil {
		projID, _ := f.stores.Projects.GetProjectIDForChannel(ctx, cc.ChannelID)
		if projID != "" {
			projKBIDs, projErr := f.stores.Projects.GetKBIDs(ctx, projID)
			if projErr == nil {
				for _, kbID := range projKBIDs {
					if !seen[kbID] {
						kb, kbErr := f.stores.KnowledgeBases.GetByID(ctx, kbID)
						if kbErr == nil && kb.ChunkCount > 0 {
							kbs = append(kbs, kbInfo{Name: kb.Name, DocCount: kb.DocumentCount})
							seen[kbID] = true
						}
					}
				}
			}
		}
	}

	// ── Channel-linked KBs ──
	channelKBs, err := f.stores.KnowledgeBases.GetChannelKBs(ctx, cc.ChannelID)
	if err == nil {
		for _, ckb := range channelKBs {
			if ckb.Enabled && ckb.DocumentCount > 0 && !seen[ckb.KBID] {
				kbs = append(kbs, kbInfo{Name: ckb.KBName, DocCount: ckb.DocumentCount})
				seen[ckb.KBID] = true
			}
		}
	}

	// ── Personal KBs (always available to owner) ──
	personalKBs, err := f.stores.KnowledgeBases.ListPersonal(ctx, cc.UserID)
	if err == nil {
		for _, kb := range personalKBs {
			if !seen[kb.ID] && kb.ChunkCount > 0 {
				kbs = append(kbs, kbInfo{Name: kb.Name, DocCount: kb.DocumentCount})
				seen[kb.ID] = true
			}
		}
	}

	if len(kbs) == 0 {
		return nil, nil
	}

	hint := "\nYou have access to the following knowledge bases:\n"
	for _, kb := range kbs {
		hint += fmt.Sprintf("- \"%s\" (%d documents)\n", kb.Name, kb.DocCount)
	}
	hint += "Use the kb_search tool to find relevant information from these sources when the user asks questions that might be answered by their documents."

	log.Printf("  🔗 kb-auto-inject: %d KBs active for channel %s", len(kbs), cc.ChannelID[:min(8, len(cc.ChannelID))])

	return &Contribution{
		Messages: []InjectedMessage{
			{Role: "system", Content: hint},
		},
	}, nil
}
