package api

import (
	"context"
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent"
	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeconfig"
	"github.com/tidbcloud/AgentTeam/desktop/internal/agentview"
	"github.com/tidbcloud/AgentTeam/desktop/internal/api/session"
	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

type execAgentReader struct {
	sessions session.SessionStore
}

func (reader execAgentReader) ListAgents(context.Context) ([]agentview.Snapshot, error) {
	agents := profile.Default().Agents().All()
	result := make([]agentview.Snapshot, 0, len(agents))
	for _, configured := range agents {
		snapshot, ok := execAgentSession(reader.sessions, configured)
		result = append(result, buildExecAgentSnapshot(configured, snapshot, ok))
	}
	return result, nil
}

func (reader execAgentReader) ListAgentsPage(ctx context.Context, query, revision, afterID string, limit int) ([]agentview.Snapshot, bool, string, error) {
	items, hasMore, currentRevision, _, err := reader.ListAgentsPageWithAnchor(ctx, query, revision, afterID, limit)
	return items, hasMore, currentRevision, err
}

// ListAgentsPageWithAnchor is the bounded continuation fast path. The extra
// anchor is required when a sparse filter consumes the index scan budget after
// the last returned Agent.
func (reader execAgentReader) ListAgentsPageWithAnchor(_ context.Context, query, revision, afterID string, limit int) ([]agentview.Snapshot, bool, string, string, error) {
	page, err := profile.Default().Agents().PageResult(query, revision, afterID, limit)
	if err != nil {
		return nil, false, "", "", err
	}
	result := make([]agentview.Snapshot, 0, len(page.Agents))
	for _, configured := range page.Agents {
		snapshot, ok := execAgentSession(reader.sessions, configured)
		result = append(result, buildExecAgentSnapshot(configured, snapshot, ok))
	}
	return result, page.HasMore, page.Revision, page.NextAnchor, nil
}

func (reader execAgentReader) GetAgent(_ context.Context, id string) (agentview.Snapshot, bool, error) {
	configured, ok := profile.Default().Agents().Get(strings.TrimSpace(id))
	if !ok {
		return agentview.Snapshot{}, false, nil
	}
	snapshot, hasSession := execAgentSession(reader.sessions, configured)
	return buildExecAgentSnapshot(configured, snapshot, hasSession), true, nil
}

func execAgentSession(store session.SessionStore, configured agent.Config) (types.SessionSnapshot, bool) {
	if store == nil {
		return types.SessionSnapshot{}, false
	}
	if snapshot, ok := store.Session(configured.ID); ok {
		return snapshot, true
	}
	// AgentTeam-synced identities can be renamed locally while the active session
	// still resolves under the server's stable Agent identifier. The lookup
	// store owns that alias index, so probe only the configured stable source
	// identifier rather than materializing every active session.
	if id := strings.TrimSpace(configured.Source.ID); id != "" {
		if snapshot, ok := store.Session(id); ok {
			return snapshot, true
		}
	}
	return types.SessionSnapshot{}, false
}

func buildExecAgentSnapshot(configured agent.Config, snapshot types.SessionSnapshot, hasSession bool) agentview.Snapshot {
	buildContext := agentview.BuildContext{}
	manager := profile.Default()
	if configured.Source.ID != "" {
		if server, ok := manager.Servers().Get(configured.Source.ID); ok {
			buildContext.SourceName = server.Name
		}
	}
	if runtime, ok := manager.Runtimes().Get(configured.Runtime); ok {
		configuredType := runtimeconfig.NormalizeType(string(runtime.Type))
		buildContext.RuntimeType = string(configuredType)
	} else if validated, err := runtimeconfig.ValidateType(configured.Runtime); err == nil {
		configuredType := runtimeconfig.NormalizeType(string(validated))
		buildContext.RuntimeType = string(configuredType)
	}
	if hasSession {
		buildContext.Session = &snapshot
	}
	return agentview.Build(configured, buildContext)
}
