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

package stackstate

import (
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"github.com/hashicorp/terraform/internal/addrs"
	"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
	"github.com/hashicorp/terraform/internal/stacks/stackstate/statekeys"
	"github.com/hashicorp/terraform/internal/stacks/tfstackdata1"
	"github.com/hashicorp/terraform/internal/states"
	"github.com/zclconf/go-cty/cty"
)

func TestLoader_basic(t *testing.T) {
	aComponentInstAddr := stackaddrs.AbsComponentInstance{
		Stack: stackaddrs.RootStackInstance,
		Item: stackaddrs.ComponentInstance{
			Component: stackaddrs.Component{
				Name: "a",
			},
		},
	}
	aResourceInstAddr := stackaddrs.AbsResourceInstance{
		Component: aComponentInstAddr,
		Item: addrs.AbsResourceInstance{
			Module: addrs.RootModuleInstance,
			Resource: addrs.ResourceInstance{
				Resource: addrs.Resource{
					Mode: addrs.ManagedResourceMode,
					Type: "test",
					Name: "foo",
				},
			},
		},
	}
	providerAddr := addrs.NewBuiltInProvider("test")
	providerInstAddr := addrs.AbsProviderConfig{
		Module:   addrs.RootModule,
		Provider: providerAddr,
	}

	loader := NewLoader()
	loader.AddDirectProto(
		statekeys.String(statekeys.ComponentInstance{
			ComponentInstanceAddr: aComponentInstAddr,
		}),
		&tfstackdata1.StateComponentInstanceV1{
			OutputValues: make(map[string]*tfstackdata1.DynamicValue),
		},
	)
	attrs := `
    {
        "for_module": "a",
        "arg": null,
        "result": "result for \"a\""
	}
`
	loader.AddDirectProto(
		statekeys.String(statekeys.ResourceInstanceObject{
			ResourceInstance: aResourceInstAddr,
		}),
		&tfstackdata1.StateResourceInstanceObjectV1{
			Status:             tfstackdata1.StateResourceInstanceObjectV1_READY,
			ProviderConfigAddr: providerInstAddr.String(),
			ValueJson:          []byte(attrs),
		},
	)
	state := loader.State()

	if !state.HasComponentInstance(aComponentInstAddr) {
		t.Errorf("component instance %s not found in state", aComponentInstAddr)
	}

	got := state.ResourceInstanceObjectSrc(
		stackaddrs.AbsResourceInstanceObject{
			Component: aComponentInstAddr,
			Item:      aResourceInstAddr.Item.CurrentObject(),
		},
	)
	want := &states.ResourceInstanceObjectSrc{
		AttrsJSON:          []byte(attrs),
		AttrSensitivePaths: []cty.Path{},
		Status:             states.ObjectReady,
	}

	if diff := cmp.Diff(got, want, cmpopts.IgnoreUnexported(states.ResourceInstanceObjectSrc{})); diff != "" {
		t.Errorf("unexpected resource instance object\ndiff: %s", diff)
	}
}

func TestLoader_consumed(t *testing.T) {
	loader := NewLoader()
	loader.State()
	err := loader.AddRaw("foo", nil)
	if err == nil {
		t.Error("expected error on mutating consumed loader")
	}
}
