package models

import (
	"fmt"
	"strings"
)

// FlowAtomicHistoryPrelock declares fields whose history-sequence rows an
// atomic flow must lock as one deterministic set on its first transaction
// attempt.
type FlowAtomicHistoryPrelock struct {
	Object string   `json:"object"`
	Fields []string `json:"fields"`
}

// ValidateFlowAtomicHistoryPrelocks enforces the static first-attempt planning
// contract shared by authoring and runtime.
func ValidateFlowAtomicHistoryPrelocks(meta FlowMetadata) error {
	if len(meta.AtomicHistoryPrelocks) == 0 {
		return nil
	}
	if !meta.RunAtomically {
		return fmt.Errorf("atomicHistoryPrelocks requires runAtomically=true")
	}
	seen := make(map[string]struct{})
	for i, prelock := range meta.AtomicHistoryPrelocks {
		objectAPIName := strings.TrimSpace(prelock.Object)
		if !flowAtomicParentPrelockIdentifier.MatchString(objectAPIName) {
			return fmt.Errorf("atomicHistoryPrelocks[%d] requires a valid object", i)
		}
		if len(prelock.Fields) == 0 {
			return fmt.Errorf("atomic history prelock for %s requires at least one field", objectAPIName)
		}
		for j, declaredField := range prelock.Fields {
			fieldAPIName := strings.TrimSpace(declaredField)
			if !flowAtomicParentPrelockIdentifier.MatchString(fieldAPIName) {
				return fmt.Errorf("atomic history prelock for %s fields[%d] requires a valid field", objectAPIName, j)
			}
			key := strings.ToLower(objectAPIName + "." + fieldAPIName)
			if _, exists := seen[key]; exists {
				return fmt.Errorf("atomicHistoryPrelocks contains duplicate field %s.%s", objectAPIName, fieldAPIName)
			}
			seen[key] = struct{}{}
		}
	}

	businessPrelockObjects := make(map[string]struct{}, len(meta.AtomicParentPrelocks))
	for _, prelock := range meta.AtomicParentPrelocks {
		if objectAPIName := strings.TrimSpace(prelock.ParentObject); objectAPIName != "" {
			businessPrelockObjects[strings.ToLower(objectAPIName)] = struct{}{}
		}
	}
	for _, prelock := range meta.AtomicHistoryPrelocks {
		objectAPIName := strings.TrimSpace(prelock.Object)
		if _, ok := businessPrelockObjects[strings.ToLower(objectAPIName)]; !ok {
			return fmt.Errorf("atomic history prelock for %s requires a matching atomicParentPrelocks business-row prelock", objectAPIName)
		}
	}
	return nil
}
