package catalogsnapshot

import (
	"fmt"
	"sort"
	"strings"

	"github.com/nexuscrm/kernel/pkg/formula"
)

func canonicalFormulaFunctionBundles(values []formula.FunctionBundle) ([]formula.FunctionBundle, error) {
	if len(values) == 0 {
		return nil, nil
	}
	result := formula.CloneFunctionBundles(values)
	if err := formula.ValidateFunctionBundles(result); err != nil {
		return nil, fmt.Errorf("validate formula function bundles: %w", err)
	}
	for i := range result {
		sort.Slice(result[i].Functions, func(left, right int) bool {
			return strings.ToUpper(result[i].Functions[left].Name) < strings.ToUpper(result[i].Functions[right].Name)
		})
	}
	sort.Slice(result, func(left, right int) bool {
		leftName, rightName := "", ""
		if len(result[left].Functions) != 0 {
			leftName = strings.ToUpper(result[left].Functions[0].Name)
		}
		if len(result[right].Functions) != 0 {
			rightName = strings.ToUpper(result[right].Functions[0].Name)
		}
		return leftName < rightName
	})
	return result, nil
}

func validateFormulaFunctionBundles(values []formula.FunctionBundle) error {
	if len(values) == 0 {
		return nil
	}
	canonical, err := canonicalFormulaFunctionBundles(values)
	if err != nil {
		return err
	}
	if !formulaFunctionBundlesEqual(values, canonical) {
		return fmt.Errorf("formula function bundles are not canonical")
	}
	return nil
}

func newRevisionFormulaEngine(values []formula.FunctionBundle) (*formula.Engine, error) {
	engine := formula.NewEngine(formula.WithoutRuntimeFunctionBundles())
	if err := engine.RegisterFunctionBundles(values); err != nil {
		return nil, fmt.Errorf("register revision formula function bundles: %w", err)
	}
	return engine, nil
}

// formulaEngineForCatalogArtifact preserves the published reopen semantics of
// pre-v15 artifacts, whose grammar could not carry revision-local bundles.
// V15 and later must use only the declarations embedded in that immutable
// revision; the legacy process registry is never a fallback for new serving
// artifacts.
func formulaEngineForCatalogArtifact(schema string, values []formula.FunctionBundle) (*formula.Engine, error) {
	if schema == CatalogSnapshotSchemaV15 || schema == CatalogSnapshotSchemaV16 || schema == CatalogSnapshotSchemaV17 || schema == CatalogSnapshotSchemaV18 || schema == CatalogSnapshotSchemaV19 || (schema == CatalogSnapshotSchemaV20 || schema == CatalogSnapshotSchemaV21) {
		return newRevisionFormulaEngine(values)
	}
	return formula.NewEngine(), nil
}

// formulaEngineForRuntimeArtifact is the Runtime-view counterpart of
// formulaEngineForCatalogArtifact. V17 carries record-mutation vocabulary;
// v19 carries the same revision-local authority for approval execution; v22
// carries the v17 vocabulary alongside typed mail authority.
func formulaEngineForRuntimeArtifact(schema string, values []formula.FunctionBundle) (*formula.Engine, error) {
	if schema == RuntimeMetadataViewSchemaV17 || schema == RuntimeMetadataViewSchemaV19 || schema == RuntimeMetadataViewSchemaV22 || schema == RuntimeMetadataViewSchemaV25 || schema == RuntimeMetadataViewSchemaV26 {
		return newRevisionFormulaEngine(values)
	}
	return formula.NewEngine(), nil
}

func formulaFunctionBundlesEqual(left, right []formula.FunctionBundle) bool {
	leftBytes, leftErr := marshalCanonical(left)
	rightBytes, rightErr := marshalCanonical(right)
	return leftErr == nil && rightErr == nil && string(leftBytes) == string(rightBytes)
}
