// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package statemgr

import (
	"testing"

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

	"github.com/opentofu/opentofu/internal/addrs"
	"github.com/opentofu/opentofu/internal/states"
	"github.com/opentofu/opentofu/internal/states/statefile"
)

func TestCheckValidImport(t *testing.T) {
	barState := states.BuildState(func(s *states.SyncState) {
		s.SetOutputValue(
			addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
			cty.StringVal("bar"), false, "",
		)
	})
	notBarState := states.BuildState(func(s *states.SyncState) {
		s.SetOutputValue(
			addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
			cty.StringVal("not bar"), false, "",
		)
	})
	emptyState := states.NewState()

	tests := map[string]struct {
		New      *statefile.File
		Existing *statefile.File
		WantErr  string
	}{
		"exact match": {
			New:      statefile.New(barState, "lineage", 1),
			Existing: statefile.New(barState, "lineage", 1),
			WantErr:  ``,
		},
		"overwrite unrelated empty state": {
			New:      statefile.New(barState, "lineage1", 1),
			Existing: statefile.New(emptyState, "lineage2", 1),
			WantErr:  ``,
		},
		"different state with same serial": {
			New:      statefile.New(barState, "lineage", 1),
			Existing: statefile.New(notBarState, "lineage", 1),
			WantErr:  `cannot overwrite existing state with serial 1 with a different state that has the same serial`,
		},
		"different state with newer serial": {
			New:      statefile.New(barState, "lineage", 2),
			Existing: statefile.New(notBarState, "lineage", 1),
			WantErr:  ``,
		},
		"different state with older serial": {
			New:      statefile.New(barState, "lineage", 1),
			Existing: statefile.New(notBarState, "lineage", 2),
			WantErr:  `cannot import state with serial 1 over newer state with serial 2`,
		},
		"different lineage with same serial": {
			New:      statefile.New(barState, "lineage1", 2),
			Existing: statefile.New(notBarState, "lineage2", 2),
			WantErr:  `cannot import state with lineage "lineage1" over unrelated state with lineage "lineage2"`,
		},
		"different lineage with different serial": {
			New:      statefile.New(barState, "lineage1", 3),
			Existing: statefile.New(notBarState, "lineage2", 2),
			WantErr:  `cannot import state with lineage "lineage1" over unrelated state with lineage "lineage2"`,
		},
		"new state is legacy": {
			New:      statefile.New(barState, "", 2),
			Existing: statefile.New(notBarState, "lineage", 2),
			WantErr:  ``,
		},
		"old state is legacy": {
			New:      statefile.New(barState, "lineage", 2),
			Existing: statefile.New(notBarState, "", 2),
			WantErr:  ``,
		},
		"both states are legacy": {
			New:      statefile.New(barState, "", 2),
			Existing: statefile.New(notBarState, "", 2),
			WantErr:  ``,
		},
	}

	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			gotErr := CheckValidImport(test.New, test.Existing)

			if test.WantErr == "" {
				if gotErr != nil {
					t.Errorf("unexpected error: %s", gotErr)
				}
			} else {
				if gotErr == nil {
					t.Errorf("succeeded, but want error: %s", test.WantErr)
				} else if got, want := gotErr.Error(), test.WantErr; got != want {
					t.Errorf("wrong error\ngot:  %s\nwant: %s", got, want)
				}
			}
		})
	}
}
