package tools

import (
	"context"
	"log/slog"
	"sync"

	"github.com/google/uuid"

	"github.com/nextlevelbuilder/goclaw/internal/store"
)

// DynamicToolLoader loads custom tools from the database and registers them into tool registries.
type DynamicToolLoader struct {
	store     store.CustomToolStore
	workspace string
	mu        sync.Mutex
	// Track names of globally registered custom tools (for reload/unregister).
	globalNames map[string]bool
}

// NewDynamicToolLoader creates a loader for custom tools from the database.
func NewDynamicToolLoader(s store.CustomToolStore, workspace string) *DynamicToolLoader {
	return &DynamicToolLoader{
		store:       s,
		workspace:   workspace,
		globalNames: make(map[string]bool),
	}
}

// LoadGlobal loads all global custom tools (agent_id IS NULL) and registers them into the registry.
// Skips tools whose names collide with existing built-in or MCP tools.
func (l *DynamicToolLoader) LoadGlobal(ctx context.Context, reg *Registry) error {
	l.mu.Lock()
	defer l.mu.Unlock()

	defs, err := l.store.ListGlobal(ctx)
	if err != nil {
		return err
	}

	registered := 0
	for _, def := range defs {
		if _, exists := reg.Get(def.Name); exists {
			slog.Warn("custom_tools: skipping global tool (name collision with built-in/MCP)",
				"tool", def.Name)
			continue
		}
		reg.Register(NewDynamicTool(def, l.workspace))
		l.globalNames[def.Name] = true
		registered++
	}

	if registered > 0 {
		slog.Info("custom_tools: loaded global tools", "count", registered)
	}
	return nil
}

// LoadForAgent loads per-agent custom tools and returns a cloned registry with them added.
// Returns nil if the agent has no custom tools (caller should use global registry).
func (l *DynamicToolLoader) LoadForAgent(ctx context.Context, globalReg *Registry, agentID uuid.UUID) (*Registry, error) {
	defs, err := l.store.ListByAgent(ctx, agentID)
	if err != nil {
		return nil, err
	}
	if len(defs) == 0 {
		return nil, nil // no per-agent tools
	}

	clone := globalReg.Clone()
	for _, def := range defs {
		clone.Register(NewDynamicTool(def, l.workspace))
	}

	slog.Debug("custom_tools: loaded per-agent tools", "agent_id", agentID, "count", len(defs))
	return clone, nil
}

// ReloadGlobal unregisters all previously loaded global tools and re-loads from DB.
// Used on cache invalidation events.
func (l *DynamicToolLoader) ReloadGlobal(ctx context.Context, reg *Registry) {
	l.mu.Lock()
	// Unregister old global custom tools
	for name := range l.globalNames {
		reg.Unregister(name)
	}
	l.globalNames = make(map[string]bool)
	l.mu.Unlock()

	// Re-load (LoadGlobal acquires its own lock)
	if err := l.LoadGlobal(ctx, reg); err != nil {
		slog.Warn("custom_tools: failed to reload global tools", "error", err)
	}
}
