// Package bootstrap validates and registers the trusted Builtin provider
// Extensions into the immutable registry in one atomic startup pass.
//
// Registration is fail-closed: an Extension whose Definition catalog, executor
// dispatch table, auth contract, or Distribution is inconsistent aborts server
// startup instead of surfacing as a runtime failure.
package bootstrap

import (
	"fmt"

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

// Register validates every Extension and registers it as a Builtin. Duplicate
// domains and cross-field inconsistencies are startup errors.
func Register(reg *registry.Registry, list []extension.Extension) error {
	if reg == nil {
		return fmt.Errorf("bootstrap: registry is required")
	}
	seen := make(map[string]struct{}, len(list))
	for _, extension := range list {
		if err := validate(extension); err != nil {
			return err
		}
		if _, duplicate := seen[extension.Definition.Domain]; duplicate {
			return fmt.Errorf(
				"bootstrap: provider domain %q is registered twice",
				extension.Definition.Domain)
		}
		seen[extension.Definition.Domain] = struct{}{}
		if _, err := reg.RegisterBuiltinWithOptions(
			extension.Definition,
			extension.Implementation.Executor,
			extension.Implementation.Revision,
			registry.BuiltinOptions{
				ConnectionPolicy: extension.Implementation.ConnectionPolicy,
				CatalogPolicy:    extension.Implementation.CatalogPolicy,
			},
		); err != nil {
			return fmt.Errorf(
				"bootstrap: register provider %q: %w",
				extension.Definition.Domain, err)
		}
	}
	return nil
}

// validate enforces the composite invariants that keep the catalog, the
// executor, and the product mapping from drifting apart.
func validate(extension extension.Extension) error {
	domain := extension.Definition.Domain
	if domain == "" {
		return fmt.Errorf("bootstrap: provider domain is required")
	}
	implementation := extension.Implementation
	if implementation.Executor == nil {
		return fmt.Errorf("bootstrap: provider %q has no executor", domain)
	}
	if implementation.Revision == "" {
		return fmt.Errorf("bootstrap: provider %q has no implementation revision", domain)
	}
	if implementation.ConnectionPolicy != registry.ConnectionRequired &&
		implementation.ConnectionPolicy != registry.ConnectionNone {
		return fmt.Errorf(
			"bootstrap: provider %q has an invalid connection policy", domain)
	}
	if err := validateAuthContract(extension); err != nil {
		return err
	}
	if lister, ok := implementation.Executor.(runner.ActionLister); ok {
		if err := validateActionCoverage(domain, extension.Definition, lister); err != nil {
			return err
		}
	}
	distribution := extension.Distribution
	if distribution.AppID == "" || distribution.AppSlug == "" ||
		distribution.DefaultName == "" || distribution.IconURL == "" {
		return fmt.Errorf(
			"bootstrap: provider %q has an incomplete distribution", domain)
	}
	return nil
}

// hasHeaderBinding reports whether at least one credential field carries a
// header binding under the headers kind; a code-defined Signature
// substitution is checked separately by the caller.
func hasHeaderBinding(auth runner.AuthSpec) bool {
	for _, field := range auth.Fields {
		if field.HeaderName != "" {
			return true
		}
	}
	return false
}

// validateAuthContract checks the auth contract against the connection
// policy. A provider that declares runtime credential injection must require a
// connection and carry a live credential validator or connection preparer; no
// field may be declared under the none kind; and an unknown kind always fails
// closed. Legacy
// Builtins with bespoke credential stores declare no runtime auth contract
// and are not constrained here.
func validateAuthContract(extension extension.Extension) error {
	domain := extension.Definition.Domain
	implementation := extension.Implementation
	auth := implementation.Auth
	if err := implementation.ConnectionConfig.ValidateSpec(); err != nil {
		return fmt.Errorf(
			"bootstrap: provider %q has an invalid connection config contract: %w",
			domain,
			err,
		)
	}
	if len(implementation.ConnectionConfig.Fields) != 0 &&
		implementation.PrepareConnection == nil {
		return fmt.Errorf(
			"bootstrap: provider %q declares connection config without a preparer",
			domain,
		)
	}
	switch auth.Kind {
	case runner.AuthNone:
		if err := auth.ValidateSpec(); err != nil {
			return fmt.Errorf(
				"bootstrap: provider %q has an invalid auth contract: %w",
				domain, err)
		}
		return nil
	case runner.AuthBearer, runner.AuthHeader, runner.AuthBasic, runner.AuthHeaders,
		runner.AuthCredential, runner.AuthQuery:
		if implementation.ConnectionPolicy != registry.ConnectionRequired {
			return fmt.Errorf(
				"bootstrap: authenticated provider %q requires a connection", domain)
		}
		if err := auth.ValidateSpec(); err != nil {
			return fmt.Errorf(
				"bootstrap: provider %q has an invalid auth contract: %w",
				domain, err)
		}
		if auth.Kind == runner.AuthHeaders && !hasHeaderBinding(auth) && auth.Signature == nil {
			return fmt.Errorf(
				"bootstrap: headers-auth provider %q binds no credential header", domain)
		}
		hasValidator := implementation.ValidateCredentials != nil
		hasPreparer := implementation.PrepareConnection != nil
		if hasValidator == hasPreparer {
			return fmt.Errorf(
				"bootstrap: authenticated provider %q must declare exactly one credential validator or connection preparer",
				domain,
			)
		}
		return nil
	default:
		return fmt.Errorf(
			"bootstrap: provider %q declares an unknown auth kind %d",
			domain, auth.Kind)
	}
}

// validateActionCoverage asserts the executor dispatch table serves exactly
// the Definition's Action set.
func validateActionCoverage(domain string, definition registry.Definition, lister runner.ActionLister) error {
	catalog := make(map[string]struct{}, len(definition.Actions))
	for _, action := range definition.Actions {
		catalog[action.Name] = struct{}{}
	}
	served := make(map[string]struct{})
	for _, name := range lister.ActionNames() {
		if _, declared := catalog[name]; !declared {
			return fmt.Errorf(
				"bootstrap: provider %q executor serves undeclared action %q",
				domain, name)
		}
		served[name] = struct{}{}
	}
	for name := range catalog {
		if _, handled := served[name]; !handled {
			return fmt.Errorf(
				"bootstrap: provider %q executor does not serve action %q",
				domain, name)
		}
	}
	return nil
}
