package jin10

import (
	"context"
	"encoding/json"
	"fmt"
	"sort"

	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	"github.com/tidbcloud/AgentTeam/internal/extension/runner"
	"github.com/tidbcloud/AgentTeam/internal/extension/runner/mcp"
)

// Executor resolves one installation's credential and delegates one closed
// semantic action to the immutable shared MCP endpoint definition.
type Executor struct {
	credentials runner.CredentialSource
	client      *mcp.Client
	validators  catalogValidators
}

func newExecutor(
	credentials runner.CredentialSource,
	client *mcp.Client,
) (*Executor, error) {
	validators, err := compileCatalogValidators(Definition())
	if err != nil {
		return nil, err
	}
	return &Executor{
		credentials: credentials,
		client:      client,
		validators:  validators,
	}, nil
}

func (*Executor) Strategy() string {
	return "mcp"
}

func (executor *Executor) ActionNames() []string {
	if executor == nil {
		return nil
	}
	names := make([]string, 0, len(toolBindings)+1)
	names = append(names, ActionListQuoteCodes)
	for name := range toolBindings {
		names = append(names, name)
	}
	sort.Strings(names)
	return names
}

func (executor *Executor) Invoke(
	ctx context.Context,
	invocation registry.BuiltinInvocation,
) (registry.BuiltinResult, error) {
	if executor == nil || executor.client == nil {
		return registry.BuiltinResult{}, fmt.Errorf(
			"Jin10 MCP executor is not configured")
	}
	input, err := executor.validators.validateInput(
		invocation.Action,
		invocation.Input,
	)
	if err != nil {
		return registry.BuiltinResult{}, runner.InvocationError(err)
	}
	if executor.credentials == nil {
		return registry.BuiltinResult{},
			runner.InvocationError(runner.CredentialsUnavailable(nil))
	}
	credentials, err := executor.credentials.ResolveProviderCredentials(
		ctx,
		invocation.WorkspaceID,
		invocation.ServiceInstallationID,
	)
	if err != nil {
		mapped := runner.InvocationError(runner.CredentialsUnavailable(err))
		if mapped == nil {
			return registry.BuiltinResult{}, err
		}
		return registry.BuiltinResult{}, mapped
	}
	output, err := invokeAction(
		ctx,
		executor.client,
		credentials,
		invocation.Action,
		input,
	)
	if err != nil {
		if mapped := runner.InvocationError(err); mapped != nil {
			return registry.BuiltinResult{}, mapped
		}
		return registry.BuiltinResult{}, err
	}
	normalized, err := executor.validators.validateOutput(
		invocation.Action,
		output,
	)
	if err != nil {
		return registry.BuiltinResult{}, runner.InvocationError(err)
	}
	return registry.BuiltinResult{
		Data: append(json.RawMessage(nil), normalized...),
	}, nil
}
