// Package wecombot registers the WeCom Bot Builtin Extension, ported from
// OpenConnector (baseline 0484cac): the group-robot webhook on
// qyapi.weixin.qq.com. The webhook key rides the query string and no signing
// secret exists for webhook robots, so the single credential field stays
// connection-scoped instead of being injected as a header.
package wecombot

import (
	"context"
	"fmt"

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

const (
	// Domain is the Extension domain.
	Domain = "wecombot"
	// Revision is the implementation identity folded into the runtime digest.
	Revision = "wecombot-rest-v1"
	// BaseURL is the fixed WeCom open API origin.
	BaseURL = "https://qyapi.weixin.qq.com"
	// APIKeyField is the connection credential carrying the webhook key (or
	// the full webhook URL).
	APIKeyField = "apiKey"
)

// Extension composes the WeCom Bot Builtin. It fails closed when the
// credential source or provider client cannot be constructed.
func Extension(credentials runner.CredentialSource) (extension.Extension, error) {
	client, err := runner.NewRESTClient(runner.RESTOptions{
		BaseURL: BaseURL,
		Auth:    AuthSpec(),
	})
	if err != nil {
		return extension.Extension{}, fmt.Errorf("wecombot: build REST client: %w", err)
	}
	executor := runner.NewRESTExecutor(Domain, client, credentials, Handlers())
	return extension.Extension{
		Definition: Definition(),
		Implementation: runner.Implementation{
			Executor:            executor,
			Revision:            Revision,
			ConnectionPolicy:    registry.ConnectionRequired,
			Auth:                AuthSpec(),
			ValidateCredentials: ValidateCredentials,
		},
		Distribution: extension.Distribution{
			AppID:                     "app_wecom_bot",
			AppSlug:                   "wecombot",
			DefaultName:               Domain,
			DefaultEnabled:            true,
			IconURL:                   "/app-icons/wecombot.svg",
			GenericProviderConnection: true,
		},
	}, nil
}

// AuthSpec declares the webhook credential contract. The field is
// connection-scoped (no header binding): the webhook key becomes a query
// parameter, so the runtime injects nothing on its own.
func AuthSpec() runner.AuthSpec {
	return runner.AuthSpec{
		Kind: runner.AuthCredential,
		Fields: []runner.CredentialField{
			{
				Name:        APIKeyField,
				DisplayName: "WeCom webhook key",
				Description: "The key query value of the WeCom group bot webhook URL; the full webhook URL is also accepted.",
				Required:    true,
				Secret:      true,
			},
		},
	}
}

// ValidateCredentials live-checks the webhook with one bounded empty-payload
// probe: a structured bot-side rejection proves the webhook exists.
func ValidateCredentials(ctx context.Context, credentials runner.Credentials) error {
	client, err := runner.NewRESTClient(runner.RESTOptions{
		BaseURL: BaseURL,
		Auth:    AuthSpec(),
	})
	if err != nil {
		return err
	}
	_, _, probeErr := callWebhook(ctx, client, credentials, map[string]any{})
	return probeErr
}
