package packs

import (
	"context"
	"database/sql"
	"sort"
	"strings"

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

func (p *SourceBackedPack) RuntimeObjectSpecPayloads() [][]byte {
	out := make([][]byte, 0, len(p.runtimeObjectPayloads))
	for _, payload := range p.runtimeObjectPayloads {
		out = append(out, append([]byte(nil), payload...))
	}
	return out
}

func (p *SourceBackedPack) RegisterRuntimeMetadata(context.Context, *sql.DB) error {
	if err := metadata.RegisterPackRelatedListRules(p.Name(), p.relatedListRules); err != nil {
		return err
	}
	return metadata.RegisterPackSearchScopeOrder(p.Name(), p.searchScopeOrder)
}

func (p *SourceBackedPack) RuntimeRelatedListRules() []metadata.RelatedListRule {
	return cloneRuntimeRelatedListRules(p.relatedListRules)
}

func (p *SourceBackedPack) RuntimeSearchScopeOrder() []metadata.SearchScopeOrderItem {
	return append([]metadata.SearchScopeOrderItem(nil), p.searchScopeOrder...)
}

func (p *SourceBackedPack) RuntimeSearchAllDefaultFields() []searchmetadata.SearchAllDefaultFieldsDeclaration {
	return cloneSearchAllDefaultFieldsDeclarations(p.searchAllDefaultFields)
}

func (p *SourceBackedPack) RuntimeFormulaFunctionBundles() []formula.FunctionBundle {
	return formula.CloneFunctionBundles(p.formulaFunctionBundles)
}

func (p *SourceBackedPack) RuntimeHrefRewriteFields() []files.HrefRewriteField {
	return append([]files.HrefRewriteField(nil), p.hrefRewriteFields...)
}

func (p *SourceBackedPack) OwnedObjectAPINames() []string {
	return append([]string(nil), p.ownedObjectAPINames...)
}

func (p *SourceBackedPack) RuntimeStartupShallowDeclaredRows() []runtimeStartupShallowDeclaredRow {
	out := make([]runtimeStartupShallowDeclaredRow, 0, len(p.runtimeStartupShallowRows))
	out = append(out, p.runtimeStartupShallowRows...)
	return normalizeRuntimeStartupShallowDeclaredRows(out)
}

func (p *SourceBackedPack) StandardFlowDeveloperNames() []string {
	names := make([]string, 0, len(p.standardFlowPayloads))
	for fileName := range p.standardFlowPayloads {
		name := strings.TrimSpace(strings.TrimSuffix(fileName, ".flow.json"))
		if name != "" {
			names = append(names, name)
		}
	}
	sort.Strings(names)
	return names
}

func (p *SourceBackedPack) appendRuntimeStartupShallowKey(kind ComponentKind, businessKey string) {
	p.appendRuntimeStartupShallowDeclaration(kind, businessKey, "")
}

func (p *SourceBackedPack) appendRuntimeStartupShallowDeclaration(kind ComponentKind, businessKey string, projection string) {
	businessKey = strings.TrimSpace(businessKey)
	projection = strings.TrimSpace(projection)
	if kind == "" || businessKey == "" {
		return
	}
	p.runtimeStartupShallowRows = append(p.runtimeStartupShallowRows, runtimeStartupShallowDeclaredRow{
		Kind:        kind,
		BusinessKey: businessKey,
		Projection:  projection,
	})
}

func (p *SourceBackedPack) appendRuntimeStartupShallowInactiveValidationRuleKey(businessKey string) {
	businessKey = strings.TrimSpace(businessKey)
	if businessKey == "" {
		return
	}
	if p.runtimeStartupShallowInactiveValidationRules == nil {
		p.runtimeStartupShallowInactiveValidationRules = map[string]bool{}
	}
	p.runtimeStartupShallowInactiveValidationRules[businessKey] = true
}

func (p *SourceBackedPack) ValidationRuleBusinessKeys() []ValidationRuleBusinessKey {
	out := append([]ValidationRuleBusinessKey(nil), p.validationRuleKeys...)
	sort.Slice(out, func(i, j int) bool {
		if out[i].ObjectAPIName != out[j].ObjectAPIName {
			return out[i].ObjectAPIName < out[j].ObjectAPIName
		}
		return out[i].Name < out[j].Name
	})
	return out
}

func (p *SourceBackedPack) ConnectedAppOwnershipKeys() []ConnectedAppOwnershipKey {
	out := cloneSourceSlice(p.connectedAppOwnershipKeys)
	for i := range out {
		out[i].RedirectURIs = cloneSourceSlice(out[i].RedirectURIs)
		out[i].Scopes = cloneSourceSlice(out[i].Scopes)
		out[i].AllowedGrantTypes = cloneSourceSlice(out[i].AllowedGrantTypes)
	}
	sort.Slice(out, func(i, j int) bool {
		if out[i].ID != out[j].ID {
			return out[i].ID < out[j].ID
		}
		if out[i].Name != out[j].Name {
			return out[i].Name < out[j].Name
		}
		return out[i].ConsumerKey < out[j].ConsumerKey
	})
	return out
}

func (p *SourceBackedPack) ObjectsRequiringDerivedViews() []string {
	seen := map[string]string{}
	for _, component := range p.components {
		objectComponent, ok := component.(*sourceBackedObjectSpecComponent)
		if !ok {
			continue
		}
		for _, spec := range objectComponent.specs {
			requiresDerivedView := false
			for _, field := range spec.Fields {
				resolved, err := formulastorage.Resolve(formulastorage.Spec{
					FormulaMode:         field.FormulaMode,
					FormulaStorageMode:  field.FormulaStorageMode,
					Formula:             field.Formula,
					HasProjectionConfig: field.ProjectionConfig != nil,
				})
				_ = err
				if resolved.RequiresFormulaView() {
					requiresDerivedView = true
					break
				}
			}
			if !requiresDerivedView {
				continue
			}
			name := strings.TrimSpace(spec.APIName)
			if name != "" {
				seen[strings.ToLower(name)] = name
			}
		}
	}
	keys := make([]string, 0, len(seen))
	for key := range seen {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	out := make([]string, 0, len(keys))
	for _, key := range keys {
		out = append(out, seen[key])
	}
	return out
}
