/*
 * Copyright 2025 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Package supervisor implements the supervisor pattern for multi-agent systems,
// where a central agent coordinates a set of sub-agents.
//
// # Unified Tracing
//
// The supervisor pattern provides unified tracing support through an internal container.
// When using callbacks (e.g., for tracing or observability), the entire supervisor structure
// (supervisor agent + all sub-agents) shares a single trace root. This means:
//   - OnStart is invoked once at the supervisor container level
//   - The callback-enriched context (containing parent span info) is propagated to all agents
//   - All agents within the supervisor appear as children of the same trace root
//
// This is achieved by wrapping the supervisor structure in an internal container that acts
// as the single entry point for tracing. The container delegates all execution to the
// underlying agents while providing a unified identity for callbacks.
package supervisor

import (
	"context"

	"github.com/cloudwego/eino/adk"
)

// Config is the configuration for creating a supervisor-based multi-agent system.
//
// NOT RECOMMENDED: Supervisor is built on agent transfer with full context sharing,
// which has not proven to be more effective empirically. Consider using
// ChatModelAgent with AgentTool or DeepAgent instead for most multi-agent scenarios.
type Config struct {
	// Supervisor specifies the agent that will act as the supervisor, coordinating and managing the sub-agents.
	Supervisor adk.Agent

	// SubAgents specifies the list of agents that will be supervised and coordinated by the supervisor agent.
	SubAgents []adk.Agent
}

// supervisorContainer wraps the entire supervisor structure to provide unified tracing.
// When callbacks are registered (e.g., via Runner.Query with WithCallbacks), OnStart/OnEnd
// are invoked once for this container, creating a single trace root. The callback-enriched
// context is then propagated to the supervisor and all sub-agents, ensuring they share
// the same trace parent.
//
// This container implements Agent and ResumableAgent by delegating to the inner agent.
// It provides its own Name and GetType for callback identification.
type supervisorContainer struct {
	name  string
	inner adk.ResumableAgent
}

func (s *supervisorContainer) Name(_ context.Context) string {
	return s.name
}

func (s *supervisorContainer) Description(ctx context.Context) string {
	return s.inner.Description(ctx)
}

func (s *supervisorContainer) GetType() string {
	return "Supervisor"
}

func (s *supervisorContainer) Run(ctx context.Context, input *adk.AgentInput, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
	return s.inner.Run(ctx, input, opts...)
}

func (s *supervisorContainer) Resume(ctx context.Context, info *adk.ResumeInfo, opts ...adk.AgentRunOption) *adk.AsyncIterator[*adk.AgentEvent] {
	return s.inner.Resume(ctx, info, opts...)
}

// New creates a supervisor-based multi-agent system with the given configuration.
//
// In the supervisor pattern, a designated supervisor agent coordinates multiple sub-agents.
// The supervisor can delegate tasks to sub-agents and receive their responses, while
// sub-agents can only communicate with the supervisor (not with each other directly).
// This hierarchical structure enables complex problem-solving through coordinated agent interactions.
//
// The returned agent is wrapped in an internal container that provides unified tracing.
// When used with Runner and callbacks, all agents within the supervisor structure will
// share the same trace root, making it easy to observe the entire multi-agent execution
// as a single logical unit.
//
// NOT RECOMMENDED: Supervisor is built on agent transfer with full context sharing,
// which has not proven to be more effective empirically. Consider using
// ChatModelAgent with AgentTool or DeepAgent instead for most multi-agent scenarios.
func New(ctx context.Context, conf *Config) (adk.ResumableAgent, error) {
	subAgents := make([]adk.Agent, 0, len(conf.SubAgents))
	supervisorName := conf.Supervisor.Name(ctx)
	for _, subAgent := range conf.SubAgents {
		subAgents = append(subAgents, adk.AgentWithDeterministicTransferTo(ctx, &adk.DeterministicTransferConfig{
			Agent:        subAgent,
			ToAgentNames: []string{supervisorName},
		}))
	}

	inner, err := adk.SetSubAgents(ctx, conf.Supervisor, subAgents)
	if err != nil {
		return nil, err
	}

	return &supervisorContainer{
		name:  supervisorName,
		inner: inner,
	}, nil
}
