package controlplane

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	"github.com/nexuscrm/shared/pkg/searchmetadata"
)

// PackGenerationAuthorityExecutor is the transaction-bound SQL surface needed
// to read and verify active immutable pack generations.
type PackGenerationAuthorityExecutor interface {
	ExecContext(context.Context, string, ...any) (sql.Result, error)
	QueryContext(context.Context, string, ...any) (*sql.Rows, error)
	QueryRowContext(context.Context, string, ...any) *sql.Row
}

// ReadActiveRuntimeSearchAllDefaultFields returns declarations only after the
// complete active generation authority has passed the same fail-closed
// verifier used by pack activation. The caller must provide the transaction
// that also owns the surrounding Catalog compilation.
func ReadActiveRuntimeSearchAllDefaultFields(
	ctx context.Context,
	tx PackGenerationAuthorityExecutor,
) ([]searchmetadata.SearchAllDefaultFieldsDeclaration, error) {
	if ctx == nil || tx == nil {
		return nil, fmt.Errorf(
			"%w: context and generation authority transaction are required",
			ErrPackGenerationPrecondition,
		)
	}
	generations, err := readActivePackGenerationsForCatalog(ctx, tx)
	if err != nil {
		return nil, err
	}

	var result []searchmetadata.SearchAllDefaultFieldsDeclaration
	ownedObjects := map[string]string{}
	for _, generation := range generations {
		packName := generation.packName
		var values []searchmetadata.SearchAllDefaultFieldsDeclaration
		if err := json.Unmarshal(
			[]byte(generation.runtimeSearchAllDefault.String),
			&values,
		); err != nil {
			return nil, fmt.Errorf(
				"decode active pack %q search-all declarations: %w",
				packName,
				err,
			)
		}
		if err := searchmetadata.ValidatePackSearchAllDefaultFields(values); err != nil {
			return nil, fmt.Errorf(
				"validate active pack %q search-all declarations: %w",
				packName,
				err,
			)
		}
		for _, value := range values {
			key := strings.ToLower(strings.TrimSpace(value.ObjectAPIName))
			if owner, duplicate := ownedObjects[key]; duplicate {
				return nil, fmt.Errorf(
					"active packs %q and %q both own search-all object %q",
					owner,
					packName,
					value.ObjectAPIName,
				)
			}
			ownedObjects[key] = packName
			result = append(result, value)
		}
	}
	sort.Slice(result, func(left, right int) bool {
		return strings.ToLower(result[left].ObjectAPIName) <
			strings.ToLower(result[right].ObjectAPIName)
	})
	return result, nil
}
