// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1

package instances

import (
	"fmt"
	"sort"

	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/internal/addrs"
)

// expansion is an internal interface used to represent the different
// ways expansion can operate depending on how repetition is configured for
// an object.
type expansion interface {
	// instanceKeys determines the full set of instance keys for whatever
	// object this expansion is associated with, or indicates that we
	// can't yet know what they are (using keysUnknown=true).
	//
	// if keysUnknown is true then the "keys" result is likely to be incomplete,
	// meaning that there might be other instance keys that we've not yet
	// calculated, but we know that they will be of type keyType.
	//
	// If len(keys) == 0 when keysUnknown is true then we don't yet know _any_
	// instance keys for this object. keyType might be [addrs.UnknownKeyType]
	// if we don't even have enough information to predict what type of
	// keys we will be using for this object.
	instanceKeys() (keyType addrs.InstanceKeyType, keys []addrs.InstanceKey, keysUnknown bool)
	repetitionData(addrs.InstanceKey) RepetitionData
}

// expansionSingle is the expansion corresponding to no repetition arguments
// at all, producing a single object with no key.
//
// expansionSingleVal is the only valid value of this type.
type expansionSingle uintptr

var singleKeys = []addrs.InstanceKey{addrs.NoKey}
var expansionSingleVal expansionSingle

func (e expansionSingle) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
	return addrs.NoKeyType, singleKeys, false
}

func (e expansionSingle) repetitionData(key addrs.InstanceKey) RepetitionData {
	if key != addrs.NoKey {
		panic("cannot use instance key with non-repeating object")
	}
	return RepetitionData{}
}

// expansionCount is the expansion corresponding to the "count" argument.
type expansionCount int

func (e expansionCount) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
	ret := make([]addrs.InstanceKey, int(e))
	for i := range ret {
		ret[i] = addrs.IntKey(i)
	}
	return addrs.IntKeyType, ret, false
}

func (e expansionCount) repetitionData(key addrs.InstanceKey) RepetitionData {
	i := int(key.(addrs.IntKey))
	if i < 0 || i >= int(e) {
		panic(fmt.Sprintf("instance key %d out of range for count %d", i, e))
	}
	return RepetitionData{
		CountIndex: cty.NumberIntVal(int64(i)),
	}
}

// expansionForEach is the expansion corresponding to the "for_each" argument.
type expansionForEach map[string]cty.Value

func (e expansionForEach) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
	ret := make([]addrs.InstanceKey, 0, len(e))
	for k := range e {
		ret = append(ret, addrs.StringKey(k))
	}
	sort.Slice(ret, func(i, j int) bool {
		return ret[i].(addrs.StringKey) < ret[j].(addrs.StringKey)
	})
	return addrs.StringKeyType, ret, false
}

func (e expansionForEach) repetitionData(key addrs.InstanceKey) RepetitionData {
	k := string(key.(addrs.StringKey))
	v, ok := e[k]
	if !ok {
		panic(fmt.Sprintf("instance key %q does not match any instance", k))
	}
	return RepetitionData{
		EachKey:   cty.StringVal(k),
		EachValue: v,
	}
}

// expansionDeferred is a special expansion which represents that we don't
// yet have enough information to calculate the expansion of a particular
// object.
//
// [expansionDeferredIntKey] and [expansionDeferredStringKey] are the only
// valid values of this type.
type expansionDeferred rune

const expansionDeferredIntKey = expansionDeferred(addrs.IntKeyType)
const expansionDeferredStringKey = expansionDeferred(addrs.StringKeyType)

func (e expansionDeferred) instanceKeys() (addrs.InstanceKeyType, []addrs.InstanceKey, bool) {
	return addrs.InstanceKeyType(e), nil, true
}

func (e expansionDeferred) repetitionData(key addrs.InstanceKey) RepetitionData {
	panic("no known instances for object with deferred expansion")
}

func expansionIsDeferred(exp expansion) bool {
	_, ret := exp.(expansionDeferred)
	return ret
}
