// Package feishubot registers the Feishu Custom Bot Builtin Extension,
// ported from OpenConnector (baseline 0484cac): the incoming-webhook custom
// bot on open.feishu.cn. The webhook token rides the request path and the
// optional signing secret stamps an HMAC-SHA256 signature into the message
// body, so both credential fields stay connection-scoped instead of being
// injected as headers.
package feishubot

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 = "feishucustombot"
	// Revision is the implementation identity folded into the runtime digest.
	Revision = "feishucustombot-rest-v1"
	// BaseURL is the fixed Feishu open platform origin.
	BaseURL = "https://open.feishu.cn"
	// APIKeyField is the connection credential carrying the webhook token
	// (or the full webhook URL).
	APIKeyField = "apiKey"
	// SigningSecretField is the optional connection credential enabling
	// Feishu signature verification.
	SigningSecretField = "signingSecret"
)

// Extension composes the Feishu Custom 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("feishucustombot: 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_feishu_custom_bot",
			AppSlug:                   "feishucustombot",
			DefaultName:               Domain,
			DefaultEnabled:            true,
			IconURL:                   "/app-icons/feishucustombot.svg",
			GenericProviderConnection: true,
		},
	}, nil
}

// AuthSpec declares the webhook credential contract. Both fields are
// connection-scoped (no header binding): the webhook token becomes the request
// path and the signing secret the body signature, so the runtime injects
// nothing on its own.
func AuthSpec() runner.AuthSpec {
	return runner.AuthSpec{
		Kind: runner.AuthCredential,
		Fields: []runner.CredentialField{
			{
				Name:        APIKeyField,
				DisplayName: "Feishu webhook token",
				Description: "The token path segment of the Feishu custom bot webhook URL; the full webhook URL is also accepted.",
				Required:    true,
				Secret:      true,
			},
			{
				Name:        SigningSecretField,
				DisplayName: "Feishu signing secret",
				Description: "Optional signing secret from the Feishu bot security settings, used when signature verification is enabled.",
				Required:    false,
				Secret:      true,
			},
		},
	}
}

// ValidateCredentials live-checks the webhook with one bounded probe whose
// msg_type Feishu rejects, treating the structured rejection as proof the
// webhook and signature are accepted.
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{
		"msg_type": "__validation_probe__",
	})
	return probeErr
}
