// Package jin10 registers the Jin10 Builtin Extension over AgentTeam's shared,
// guarded MCP runtime. The provider exposes only a fixed semantic catalog;
// endpoint, tool names, and the quote-code resource URI are code-owned.
package jin10

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

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

const (
	// Domain is the immutable Extension identity.
	Domain = "jin10"
	// Revision identifies the first guarded MCP implementation contract.
	Revision = "jin10-mcp-v1"
	// Endpoint is the only Jin10 MCP endpoint this Builtin may contact.
	Endpoint = "https://mcp.jin10.com/mcp"
	// QuoteCodesResourceURI is the read-only connection probe and catalog
	// resource.
	QuoteCodesResourceURI = "quote://codes"
)

// Extension composes the Jin10 Builtin and fails closed if its static MCP
// policy or catalog schemas cannot be constructed.
func Extension(credentials runner.CredentialSource) (extension.Extension, error) {
	client, err := newMCPClient(
		nil,
		allowedToolNames(),
		[]string{QuoteCodesResourceURI},
	)
	if err != nil {
		return extension.Extension{}, err
	}
	executor, err := newExecutor(credentials, client)
	if err != nil {
		return extension.Extension{}, fmt.Errorf(
			"jin10: build executor: %w", err)
	}
	return extension.Extension{
		Definition: Definition(),
		Implementation: runner.Implementation{
			Executor:            executor,
			Revision:            Revision,
			ConnectionPolicy:    registry.ConnectionRequired,
			Auth:                AuthSpec(),
			ValidateCredentials: ValidateCredentials,
		},
		Distribution: extension.Distribution{
			AppID:                     "app_jin10",
			AppSlug:                   Domain,
			DefaultName:               Domain,
			DefaultEnabled:            true,
			IconURL:                   "/app-icons/jin10.svg",
			GenericProviderConnection: true,
		},
	}, nil
}

// AuthSpec declares one installation-scoped API key sent only as a Bearer
// credential by the shared MCP transport.
func AuthSpec() runner.AuthSpec {
	return runner.AuthSpec{
		Kind: runner.AuthBearer,
		Fields: []runner.CredentialField{
			runner.APIKeyField(
				"Jin10 API Key",
				"The Jin10 MCP API key from your Jin10 data service account.",
			),
		},
	}
}

func newMCPClient(
	transport http.RoundTripper,
	allowedTools []string,
	allowedResourceURIs []string,
) (*mcp.Client, error) {
	options := mcp.Options{
		Endpoint:            Endpoint,
		Auth:                AuthSpec(),
		AllowedTools:        allowedTools,
		AllowedResourceURIs: allowedResourceURIs,
		Transport:           transport,
	}
	client, err := mcp.NewClient(options)
	if err != nil {
		return nil, fmt.Errorf("jin10: build MCP client: %w", err)
	}
	return client, nil
}

// ValidateCredentials performs exactly one read of the fixed, read-only
// quote-code resource. It stores no token-derived profile or metadata.
func ValidateCredentials(
	ctx context.Context,
	credentials runner.Credentials,
) error {
	client, err := newMCPClient(
		nil,
		nil,
		[]string{QuoteCodesResourceURI},
	)
	if err != nil {
		return err
	}
	return validateCredentials(ctx, client, credentials)
}

func validateCredentials(
	ctx context.Context,
	client *mcp.Client,
	credentials runner.Credentials,
) error {
	if client == nil {
		return runner.Errorf(
			runner.ErrorConnectionUnavailable,
			"Jin10 MCP client is unavailable",
			nil,
		)
	}
	validators, err := compileCatalogValidators(Definition())
	if err != nil {
		return runner.Errorf(
			runner.ErrorUpstreamFailure,
			"Jin10 response contract is unavailable",
			nil,
		)
	}
	result, err := client.ReadResource(
		ctx,
		credentials,
		QuoteCodesResourceURI,
	)
	if err != nil {
		return err
	}
	raw, err := normalizeResourceResult(result)
	if err != nil {
		return err
	}
	normalized, err := validators.validateOutput(ActionListQuoteCodes, raw)
	if err != nil {
		return err
	}
	var payload struct {
		Data []json.RawMessage `json:"data"`
	}
	if err := json.Unmarshal(normalized, &payload); err != nil ||
		len(payload.Data) == 0 {
		return runner.Errorf(
			runner.ErrorUpstreamFailure,
			"Jin10 returned no supported quote codes",
			nil,
		)
	}
	return nil
}
