package daemon

import (
	"log/slog"

	"github.com/tidbcloud/AgentTeam/desktop/internal/memorysearch"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
	"github.com/tidbcloud/AgentTeam/desktop/internal/provisioning/kb"
	"github.com/tidbcloud/AgentTeam/desktop/internal/provisioning/memory"
	"github.com/tidbcloud/AgentTeam/desktop/internal/provisioning/skill"
)

type machineCapabilitiesResender interface {
	ResendMachineCapabilities()
}

// wireMemoryProvider wires the provisioning/memory provider that resolves each
// agent's owning AgentTeam server runtime to route batch access requests to the
// correct server.
func (d *Daemon) wireMemoryProvider() {
	resolver := memory.NewAgentServerConnectionResolver(d.dispatcher.ServerConnections())
	d.dispatcher.SetMemoryAccessProvider(memory.New(resolver))
	// Wire the daemon-scoped memorysearch.Reconciler owner. The resolver is
	// pinned to profile.AgentWorkDir so every reconciler's ResolvedRoot is
	// `<profile.AgentWorkDir(agentID)>/memory`, regardless of which session
	// workDir later triggers a getOrCreate. Search derivative reconciliation
	// is independent of any interactive session: it runs for bound agents
	// even when no session is active (startup, periodic, tool-time).
	d.dispatcher.EnableMemorySearch(
		memorysearch.AgentWorkDirResolver(profile.AgentWorkDir),
		memorysearch.Scan,
		slog.Default(),
	)
	slog.Info("memory provider wired")
}

// wireSkillProvider wires the provisioning/skill provider that resolves each
// agent's accessible MeshX skills and syncs the backing AGS repos to
// ~/.agentteam/managed-skills/. The producer shares the same AgentTeam server runtime
// resolver as the memory provider (per-agent ServerURL + UserToken +
// WorkspaceID) and uses the go-git-backed profile.DefaultGitRunner.
func (d *Daemon) wireSkillProvider() {
	resolver := skill.NewAgentServerConnectionResolver(d.dispatcher.ServerConnections())
	sync := skill.NewSync(
		skill.New(resolver).Lookup,
		profile.NewDefaultGitRunner(),
		profile.Default().ManagedSkillRepositories(),
	)
	d.dispatcher.SetSkillSyncer(sync)
	slog.Info("skill provider wired")
}

func (d *Daemon) resendMachineCapabilitiesOnRuntimeChange(change profile.RuntimeDetectChange) {
	if d == nil || d.dispatcher == nil {
		return
	}
	serverConnections := d.dispatcher.ServerConnections().ServerConnections()
	resent := 0
	for _, ch := range serverConnections {
		resender, ok := ch.(machineCapabilitiesResender)
		if !ok {
			continue
		}
		resender.ResendMachineCapabilities()
		resent++
	}
	if resent > 0 {
		slog.Info("runtime detection changed; resent machine capabilities",
			"previous", change.Previous,
			"current", change.Current,
			"serverConnections", resent,
		)
	}
}

func (d *Daemon) wireKnowledgeBaseProvider() {
	resolver := kb.NewAgentServerConnectionResolver(d.dispatcher.ServerConnections())
	sync := kb.NewSync(
		kb.New(resolver).Lookup,
		profile.NewDefaultGitRunner(),
		profile.Default().ManagedKnowledgeBaseRepositories(),
	)
	d.dispatcher.SetKnowledgeBaseSyncer(sync)
	slog.Info("KB provider wired")
}
