package extensionflow

import (
	"context"

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

// projectedCatalogChange compares the complete App-hosted surface at the
// current Catalog revision with the stored canonical state. A persistence
// change outside the effective surface (including a retired standalone
// Extension grant) is a Catalog no-op.
func (service *Service) projectedCatalogChange(
	ctx context.Context,
	tx coredb.Tx,
	extTx *extension.TxStore,
	workspaceID string,
	agentID string,
	current int64,
	storedState string,
) (state string, enabled bool, changed bool, err error) {
	entries, enabled, err := service.projectAppHostedCatalogEntries(
		ctx,
		tx,
		extTx,
		workspaceID,
		agentID,
	)
	if err != nil {
		return "", false, false, err
	}
	return projectedCatalogStateChange(entries, enabled, current, storedState)
}

func (service *Service) projectedCatalogChangeFromAuthorizationDecisions(
	ctx context.Context,
	tx coredb.Tx,
	extTx *extension.TxStore,
	workspaceID string,
	decisionsByInstallation map[string]appAuthorizationDecision,
	current int64,
	storedState string,
) (state string, enabled bool, changed bool, err error) {
	selections, err := extTx.ListActiveAppHostedExtensionSelections(ctx, workspaceID)
	if err != nil {
		return "", false, false, err
	}
	entries, enabled, err := service.projectAppHostedCatalogEntriesFromSelections(
		ctx,
		tx,
		workspaceID,
		selections,
		decisionsByInstallation,
	)
	if err != nil {
		return "", false, false, err
	}
	return projectedCatalogStateChange(entries, enabled, current, storedState)
}

// projectedCatalogStateChange derives the canonical state of the projected
// command set and reports whether it differs from the stored state at the
// current revision. changed=false is a no-op: the revision does not advance and
// no delivery event is written.
func projectedCatalogStateChange(
	entries []catalogEntry,
	enabled bool,
	current int64,
	storedState string,
) (state string, newEnabled bool, changed bool, err error) {
	state, err = marshalCatalogState(entries)
	if err != nil {
		return "", false, false, err
	}
	// The persisted state is the projection of the same source rows; enabled is
	// derivable from the entry set, so state equality alone decides the no-op.
	if current > 0 && state == storedState {
		return storedState, enabled, false, nil
	}
	if current == 0 && !enabled {
		return "", false, false, nil
	}
	return state, enabled, true, nil
}
