package machine

import (
	
	
	
	
)

// ///// ///// /////

// ///// STATES & SCHEMA

// ///// ///// /////

type StatesBase struct {
	// Exception is the only built-in state and mean a global error. All errors
	// have to [State.Require] the Exception state. If [Machine.PanicToErr] is
	// true, Exception will receive it.
	Exception   string
	names       S
	groups      map[string][]int
	groupsOrder []string
}

var _ States = &StatesBase{}

func ( *StatesBase) () S {
	return .names
}

func ( *StatesBase) () (map[string][]int, []string) {
	return .groups, .groupsOrder
}

func ( *StatesBase) ( S) {
	.names = slicesUniq()
}

func ( *StatesBase) ( map[string][]int,  []string) {
	.groups = 
	.groupsOrder = 
}

// States is the vase interface for schema states.
type States interface {
	// Names returns the state names of the state machine.
	Names() S
	// TODO
	StateGroups() (map[string][]int, []string)
	SetNames(S)
	SetStateGroups(map[string][]int, []string)
}

func [ States]( )  {
	// read and assign names of all the embedded structs
	 := S{}
	 := map[string][]int{}
	 := reflect.ValueOf(&).Elem()
	 := []string{}
	parseStateNames(, &, "self", , &)
	.SetNames()
	.SetStateGroups(, )

	return 
}

func parseStateNames(
	 reflect.Value,  *S,  string,  map[string][]int,
	 *[]string,
) {
	if  != "StatesBase" {
		[] = []int{}
		* = append(*, )
	}
	 := .Type()
	for  := 0;  < .NumField(); ++ {

		 := .Field()
		 := .Field()
		 := .Type.Kind()

		if .Anonymous &&  == reflect.Ptr &&
			// embedded struct (inherit states)
			.Type.Elem().Kind() == reflect.Struct {

			if .IsNil() {
				 := reflect.New(.Type.Elem())
				.Set()
			}
			(.Elem(), , .Name, , )

		} else if .CanSet() &&  == reflect.String {
			// local state name
			.SetString(.Name)
			if !slices.Contains(*, .Name) {
				if  != "StatesBase" {
					[] = append([], len(*))
				}
				* = append(*, .Name)
			}
		}
	}
}

// NewStateGroups accepts the target group with values (FooGroupsDef) and
// inherited GroupsDefs (optionally).
func [ any]( ,  ...any)  {
	// init nil embeds
	 := reflect.ValueOf(&).Elem()
	initNilEmbeds()

	// assign values from parent mixins into the local instance
	for  := range  {
		copyFields([], &)
	}

	return 
}

func initNilEmbeds( reflect.Value) {
	 := .Type()
	for  := 0;  < .NumField(); ++ {

		 := .Field()
		 := .Field()
		 := .Type.Kind()

		if .Anonymous &&  == reflect.Ptr &&
			.Type.Elem().Kind() == reflect.Struct {

			if .IsNil() {
				 := reflect.New(.Type.Elem())
				.Set()
			}
			(.Elem())
		}
	}
}

func copyFields(,  interface{}) {
	if  == nil {
		return
	}
	 := reflect.ValueOf()
	 := reflect.ValueOf()

	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}

	for  := 0;  < .NumField(); ++ {
		 := .Type().Field().Name
		 := .Field()
		 := .FieldByName()

		if .Kind() == reflect.Struct {
			(.Addr().Interface(), .Addr().Interface())
		} else {
			if .CanSet() {
				.Set()
			}
		}
	}
}

// TODO refac to Schema.StatesByTag
func ( Schema,  string) S {
	 := S{}
	for ,  := range  {
		for ,  := range .Tags {
			if  ==  || strings.HasPrefix(, +":") {
				 = append(, )
				break
			}
		}
	}

	return 
}

// ///// ///// /////

// ///// ARGS

// ///// ///// /////

const APrefix = "_am"

// ArgsApi is the base interface for typed arguments.
type ArgsApi interface {
	// ArgsPrefix returns the argument prefix inside the [A] map. Should be
	// provided by the implementation.
	ArgsPrefix() string
	// ArgsState represents the state this argument struct belongs to.
	// Defaults to [StateAny].
	ArgsState() string
	// TODO deep clone interface, optional
	// ArgsClone() G
}

// ArgsBase is the base implementation of [ArgsApi] interface.
type ArgsBase struct {
	ArgsApi
}

// ArgsPrefix is a fallback to a stack trace hash of the implementation
// symbol. Each
// pkg should overwrite this method.
func ( ArgsBase) () string {
	 := make([]byte, 4024)
	 := runtime.Stack(, false)
	 := string([:])
	 := strings.Split(, "\n")
	 := Hash([3], 10)

	return 
}

// State is the default Any state.
func ( ArgsBase) () string {
	return StateAny
}

// Clone for deep cloning.
func ( ArgsBase) () ArgsApi {
	// shallow clone
	return 
}

// pkg args

// Args for this pkg. Do not reuse.
type Args struct {
	ArgsBase
}

func (Args) () string {
	return APrefix
}

// -----

// ExceptionArgsPanic is an optional argument ["panic"] for the StateException
// state which describes a panic within a Transition handler.
type ExceptionArgsPanic struct {
	CalledStates S
	StatesBefore S
	Transition   *Transition
	LastStep     *Step
	StackTrace   string
}

type AException struct {
	Args
	Err      error `log:"err"`
	ErrTrace string
	Panic    *ExceptionArgsPanic

	// TODO handle
	Fn string `log:"fn"`

	// deadline

	TargetStates S
	CalledStates S
	TimeBefore   Time
	TimeAfter    Time
	Event        *Event
}

func (AException) () string {
	return StateException
}

// ACheck with a CheckDone chan which gets closed by the machine once it's
// processed. Can cause chan leaks when misused. Only for Can* checks.
type ACheck struct {
	Args
	// TODO close these on dispose and deadline
	CheckDone chan struct{}
	// Was the mutation canceled?
	Canceled bool
}

// -----

// PassMerge merged one or more [A] into [A]. Technically a `map[string]any`
// merge.
func ( ...A) A {
	 := A{}
	for ,  := range  {
		for ,  := range  {
			[] = 
		}
	}

	return 
}

// Pass accepts pointers of [ArgsBase] to pass to the machine as [A].
func ( ...ArgsApi) A {
	 := A{}
	for ,  := range  {
		[ArgIndex()] = 
	}

	return 
}

// ParseArgs is [ParseArgsCheck] but without the check.
func [ ArgsApi]( A) * {
	,  := ParseArgsCheck[]()
	return 
}

// ParseArgsCheck parses [A] into typed arguments described by the
// passed generic
// type. Returns
func [ ArgsApi]( A) (*, bool) {
	var  
	 := ArgIndex()
	,  := [].(*)
	if ! {
		// RPC support
		,  := [].()
		if ! {
			// fallback
			return &, false
		}
		 = &
	}

	return , true
}

// ArgIndex return an index of [arg] inside [A].
func ( ArgsApi) string {
	 := .ArgsPrefix()
	 := .ArgsState()
	return  + "__" + 
}