package extension

import (
	"context"
	"log/slog"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/agent"
	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	"github.com/tidbcloud/AgentTeam/internal/service/extensionflow"
)

// RuntimeBase is the canonical Agent Runtime base for Extension Catalog and
// invocation routes (spec §6.2).
const RuntimeBase = "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/ext"

// CatalogSource is the read-only view of registered Extensions projected into
// the Agent-visible Catalog. *registry.Registry satisfies it.
type CatalogSource interface {
	List() []registry.RegisteredExtension
}

// AgentDomainAccess describes one authorized Extension for an Agent. Definition
// identity is server-internal projection material; only the Agent-visible name,
// descriptors, and precise allowed action set cross the wire.
type AgentDomainAccess struct {
	Domain            string
	DefinitionVersion int
	DefinitionDigest  string
	ExtensionName     string
	Actions           []string
}

// AuthorizationSource provides the Agent's authorized Extension domains/actions.
// The catalog projection uses it to filter the full registry to ONLY what the
// Agent is authorized for (spec §6.3, stage 6).
type AuthorizationSource interface {
	ActiveAgentDomains(ctx context.Context, workspaceID, agentID string) ([]AgentDomainAccess, error)
}

// CatalogSnapshot is a consistent point-in-time view: the authorized domain
// entries, read in one transaction with the durable catalog row so a concurrent
// Grant/Revoke cannot return a stale entry set (stage-6 final-review blocker 4).
type CatalogSnapshot struct {
	Domains []AgentDomainAccess
}

// CatalogSnapshotSource returns the Agent's catalog snapshot (entries verified
// against the durable catalog state) in a single consistent transaction.
type CatalogSnapshotSource interface {
	AgentCatalogSnapshot(ctx context.Context, workspaceID, agentID string) (CatalogSnapshot, error)
}

// InvocationService is the narrow authoritative execution boundary. The
// implementation rechecks catalog/authorization/installation/connection state;
// handlers must not treat their discovery surface as execution authority.
type InvocationService interface {
	Invoke(context.Context, extensionflow.InvokeParams) (extensionflow.InvokeResult, error)
}

// Dependencies wires the ProfileExtension HTTP runtime.
type Dependencies struct {
	Logger   *slog.Logger
	Auth     agent.AgentAuthenticator
	Catalog  CatalogSource
	Access   AuthorizationSource
	Snapshot CatalogSnapshotSource
	Invoker  InvocationService
}

// RegisterRoutes mounts the two Extension runtime routes under RuntimeBase: the
// full Catalog document (root GET) and invocation (POST).
func RegisterRoutes(router gin.IRoutes, dependencies Dependencies) {
	handler := NewHandler(dependencies)
	requirePrincipal := agent.RequireAgentRuntimePrincipal(dependencies.Auth)

	router.GET(RuntimeBase, requirePrincipal, api.HandleAgentScoped(handler.Root))
	router.POST(
		RuntimeBase+"/:extensionName/actions/:action",
		requirePrincipal,
		api.HandleAgentScoped(handler.Invoke),
	)
}
