package extension

import (
	"context"
	"fmt"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

// CatalogHint is the bounded machine-hint payload for one Agent's Extension
// Catalog (spec §8.2). It contains ONLY the revision counter — no schemas,
// Definitions, bindings, credentials, or endpoints. The client treats it purely
// as a "did it change" signal: it never orders, compares against fetched
// documents, or binds it into identities. The delivery worker re-reads this at
// delivery time (never from a frozen payload) and pushes it via
// Hub.SyncMachineCatalog.
type CatalogHint struct {
	Version int64
}

// ReadAgentCatalogHint reads one Agent's catalog row and returns the bounded
// hint. A missing row (NotFound) means the Agent has never been authorized
// (revision 0) — this is the ONLY case that returns a zero hint without error.
// All other errors (DB/Scan/context/ownership/state) propagate fail-closed.
// The hint's durable state is validated via validateCatalogState, and ownership
// is fenced via assertAgentInWorkspace.
func (tx *TxStore) ReadAgentCatalogHint(ctx context.Context, workspaceID, agentID string) (CatalogHint, error) {
	if tx == nil || tx.db == nil {
		return CatalogHint{}, fmt.Errorf("extension store transaction is not configured")
	}
	if workspaceID == "" || agentID == "" {
		return CatalogHint{}, fmt.Errorf("workspaceId and agentId are required")
	}
	// Ownership fence: the Agent must belong to this workspace.
	if err := assertAgentInWorkspace(ctx, tx.db, workspaceID, agentID); err != nil {
		return CatalogHint{}, err
	}
	var hint CatalogHint
	var state string
	var enabled bool
	var rowWorkspace, rowAgent string
	err := tx.db.QueryRow(ctx, `
SELECT workspace_id, agent_id, version, catalog_state, enabled
FROM agent_extension_catalogs
WHERE workspace_id = $1 AND agent_id = $2`, workspaceID, agentID).Scan(
		&rowWorkspace, &rowAgent, &hint.Version, &state, &enabled,
	)
	if err != nil {
		if coredb.IsNotFound(err) {
			// Never-enabled Agent: revision 0.
			return CatalogHint{}, nil
		}
		return CatalogHint{}, fmt.Errorf("read agent catalog hint: %w", err)
	}
	// PAD SPACE defense (stage-6 final-review): under utf8mb4_bin the WHERE clause
	// matches a trailing-space-tampered identity, so the hint must read the raw row
	// identity and compare it byte-for-byte to the requested one. A mismatch is
	// corrupt state and fails closed rather than returning a hint for the wrong row.
	if rowWorkspace != workspaceID || rowAgent != agentID {
		return CatalogHint{}, fmt.Errorf("agent catalog row identity %q/%q does not exactly match requested %q/%q", rowWorkspace, rowAgent, workspaceID, agentID)
	}
	// Validate durable state (fail-closed on corrupt version/state/enabled).
	if err := validateCatalogState(hint.Version, state, enabled); err != nil {
		return CatalogHint{}, err
	}
	return hint, nil
}
