package packs

import (
	"sort"
	"strings"

	"github.com/nexuscrm/files"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/kernel/pkg/formula"
	"github.com/nexuscrm/shared/pkg/searchmetadata"
)

type runtimeRelatedListRuleProvider interface {
	RuntimeRelatedListRules() []metadata.RelatedListRule
}

type runtimeStartupShallowDeclarationProvider interface {
	RuntimeStartupShallowDeclaredRows() []runtimeStartupShallowDeclaredRow
}

type runtimeSearchAllDefaultFieldsProvider interface {
	RuntimeSearchAllDefaultFields() []searchmetadata.SearchAllDefaultFieldsDeclaration
}

type runtimeFormulaFunctionBundleProvider interface {
	RuntimeFormulaFunctionBundles() []formula.FunctionBundle
}

type runtimeHrefRewriteFieldsProvider interface {
	RuntimeHrefRewriteFields() []files.HrefRewriteField
}

type runtimeStartupShallowDeclaredRow struct {
	Kind        ComponentKind `json:"kind"`
	BusinessKey string        `json:"businessKey"`
	Projection  string        `json:"projection,omitempty"`
}

type runtimeDeclaration struct {
	objectSpecPayloads     [][]byte
	relatedListRules       []metadata.RelatedListRule
	searchScopeOrder       []metadata.SearchScopeOrderItem
	searchAllDefaultFields []searchmetadata.SearchAllDefaultFieldsDeclaration
	formulaFunctionBundles []formula.FunctionBundle
	hrefRewriteFields      []files.HrefRewriteField
	startupShallowRows     []runtimeStartupShallowDeclaredRow
}

func buildRuntimeDeclaration(pack Pack) runtimeDeclaration {
	decl := runtimeDeclaration{}

	if provider, ok := pack.(runtimeMetadataProvider); ok && provider != nil {
		payloads := provider.RuntimeObjectSpecPayloads()
		decl.objectSpecPayloads = make([][]byte, 0, len(payloads))
		for _, payload := range payloads {
			decl.objectSpecPayloads = append(decl.objectSpecPayloads, append([]byte(nil), payload...))
		}
	}
	if provider, ok := pack.(runtimeRelatedListRuleProvider); ok && provider != nil {
		decl.relatedListRules = append([]metadata.RelatedListRule(nil), provider.RuntimeRelatedListRules()...)
	}
	if provider, ok := pack.(runtimeSearchScopeOrderProvider); ok && provider != nil {
		decl.searchScopeOrder = append([]metadata.SearchScopeOrderItem(nil), provider.RuntimeSearchScopeOrder()...)
	}
	if provider, ok := pack.(runtimeSearchAllDefaultFieldsProvider); ok && provider != nil {
		decl.searchAllDefaultFields = append([]searchmetadata.SearchAllDefaultFieldsDeclaration(nil), provider.RuntimeSearchAllDefaultFields()...)
	}
	if provider, ok := pack.(runtimeFormulaFunctionBundleProvider); ok && provider != nil {
		decl.formulaFunctionBundles = append([]formula.FunctionBundle(nil), provider.RuntimeFormulaFunctionBundles()...)
	}
	if provider, ok := pack.(runtimeHrefRewriteFieldsProvider); ok && provider != nil {
		decl.hrefRewriteFields = files.NormalizeHrefRewriteFields(provider.RuntimeHrefRewriteFields())
	}
	if provider, ok := pack.(runtimeStartupShallowDeclarationProvider); ok && provider != nil {
		decl.startupShallowRows = normalizeRuntimeStartupShallowDeclaredRows(provider.RuntimeStartupShallowDeclaredRows())
	}

	return decl
}

func normalizeRuntimeStartupShallowDeclaredRows(rows []runtimeStartupShallowDeclaredRow) []runtimeStartupShallowDeclaredRow {
	seen := map[string]runtimeStartupShallowDeclaredRow{}
	for _, row := range rows {
		row.Kind = ComponentKind(strings.TrimSpace(string(row.Kind)))
		row.BusinessKey = strings.TrimSpace(row.BusinessKey)
		row.Projection = strings.TrimSpace(row.Projection)
		if row.Kind == "" || row.BusinessKey == "" {
			continue
		}
		seen[string(row.Kind)+"\x00"+row.BusinessKey] = row
	}
	out := make([]runtimeStartupShallowDeclaredRow, 0, len(seen))
	for _, row := range seen {
		out = append(out, row)
	}
	sort.Slice(out, func(i, j int) bool {
		if out[i].Kind != out[j].Kind {
			return out[i].Kind < out[j].Kind
		}
		return out[i].BusinessKey < out[j].BusinessKey
	})
	return out
}
