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

package arguments

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/terraform/internal/tfdiags"
)

func TestStateMigrateArgs_valid(t *testing.T) {
	t.Parallel()

	// Create lock files to use in test, as validation will check for the existence of the
	// file if a path is supplied via CLI flags.
	td := t.TempDir()
	userLockFilePath := filepath.Join(td, ".terraform.lock.hcl")
	if err := os.WriteFile(userLockFilePath, []byte(""), 0644); err != nil {
		t.Fatalf("failed to create test lock file: %s", err)
	}

	invalidLockFilePath := filepath.Join(td, "invalid.lock.hcl")
	if err := os.WriteFile(invalidLockFilePath, []byte(""), 0644); err != nil {
		t.Fatalf("failed to create test lock file: %s", err)
	}

	testCases := []struct {
		rawArgs      []string
		expectedArgs *StateMigrate
	}{
		{
			rawArgs: []string{""},
			expectedArgs: &StateMigrate{
				SourceLockFilePath:      "",
				DestinationLockFilePath: "",
				Upgrade:                 false,
				InputEnabled:            true,
				ViewType:                ViewHuman,
			},
		},
		{ // set or override all flags
			rawArgs: []string{
				"-source-provider-lock-file", userLockFilePath,
				"-destination-provider-lock-file", userLockFilePath,
				"-upgrade",
				"-json",
				"-input=false",
			},
			expectedArgs: &StateMigrate{
				SourceLockFilePath:      userLockFilePath,
				DestinationLockFilePath: userLockFilePath,
				Upgrade:                 true,
				InputEnabled:            false,
				ViewType:                ViewJSON,
			},
		},
	}

	for i, tc := range testCases {
		args, diags := ParseStateMigrate(tc.rawArgs)
		if diags.HasErrors() {
			t.Fatalf("unexpected diagnostics: %v", diags)
		}
		if diff := cmp.Diff(tc.expectedArgs, args); diff != "" {
			t.Fatalf("%d: supplied: %q, got unexpected arguments:\n%s", i, tc.rawArgs, diff)
		}
	}
}

func TestStateMigrateArgs_invalid(t *testing.T) {
	t.Parallel()

	// Create lock files to use in test, as validation will check for the existence of the
	// file if a path is supplied via CLI flags.
	td := t.TempDir()
	userLockFilePath := filepath.Join(td, ".terraform.lock.hcl")
	if err := os.WriteFile(userLockFilePath, []byte(""), 0644); err != nil {
		t.Fatalf("failed to create test lock file: %s", err)
	}

	invalidLockFilePath := filepath.Join(td, "invalid.lock.hcl")
	if err := os.WriteFile(invalidLockFilePath, []byte(""), 0644); err != nil {
		t.Fatalf("failed to create test lock file: %s", err)
	}

	testCases := []struct {
		rawArgs       []string
		expectedArgs  *StateMigrate
		expectedDiags tfdiags.Diagnostics
	}{
		{
			rawArgs: []string{"-input=false", "-source-provider-lock-file", invalidLockFilePath},
			expectedArgs: &StateMigrate{
				SourceLockFilePath:      invalidLockFilePath,
				DestinationLockFilePath: "",
				Upgrade:                 false,
				InputEnabled:            false,
				ViewType:                ViewHuman,
			},
			expectedDiags: tfdiags.Diagnostics{
				tfdiags.Sourceless(
					tfdiags.Error,
					"Invalid source-provider-lock-file",
					"Expected lock file name to be .terraform.lock.hcl, got: invalid.lock.hcl",
				),
			},
		},
		{
			rawArgs: []string{"-input=false", "-destination-provider-lock-file", invalidLockFilePath},
			expectedArgs: &StateMigrate{
				SourceLockFilePath:      "",
				DestinationLockFilePath: invalidLockFilePath,
				Upgrade:                 false,
				InputEnabled:            false,
				ViewType:                ViewHuman,
			},
			expectedDiags: tfdiags.Diagnostics{
				tfdiags.Sourceless(
					tfdiags.Error,
					"Invalid destination-provider-lock-file",
					"Expected lock file name to be .terraform.lock.hcl, got: invalid.lock.hcl",
				),
			},
		},
		{ // set lock file paths outside of automation
			rawArgs: []string{
				"-source-provider-lock-file", userLockFilePath,
				"-destination-provider-lock-file", invalidLockFilePath,
			},
			expectedArgs: &StateMigrate{
				SourceLockFilePath:      userLockFilePath,
				DestinationLockFilePath: invalidLockFilePath,
				Upgrade:                 false,
				InputEnabled:            true,
				ViewType:                ViewHuman,
			},
			expectedDiags: tfdiags.Diagnostics{
				tfdiags.Sourceless(
					tfdiags.Error,
					"Conflicting command-line flags provided",
					"-source-provider-lock-file cannot be used outside of automation (with -input=true)",
				),
				tfdiags.Sourceless(
					tfdiags.Error,
					"Conflicting command-line flags provided",
					"-destination-provider-lock-file cannot be used outside of automation (with -input=true)",
				),
				// Diagnostic about invalid lock file regardless of this flag not being allow
				tfdiags.Sourceless(
					tfdiags.Error,
					"Invalid destination-provider-lock-file",
					"Expected lock file name to be .terraform.lock.hcl, got: invalid.lock.hcl",
				),
			},
		},
		{ // JSON output outside of automation
			rawArgs: []string{
				"-json",
			},
			expectedArgs: &StateMigrate{
				SourceLockFilePath:      "",
				DestinationLockFilePath: "",
				Upgrade:                 false,
				InputEnabled:            true,
				ViewType:                ViewJSON,
			},
			expectedDiags: tfdiags.Diagnostics{
				tfdiags.Sourceless(
					tfdiags.Error,
					"Conflicting command-line flags provided",
					"-json cannot be used outside of automation (with -input=true)",
				),
			},
		},
	}

	for i, tc := range testCases {
		args, diags := ParseStateMigrate(tc.rawArgs)

		if !diags.HasErrors() {
			t.Fatalf("expected diagnostics, but got none")
		}
		tfdiags.AssertDiagnosticsMatch(t, diags, tc.expectedDiags)

		if diff := cmp.Diff(tc.expectedArgs, args); diff != "" {
			t.Fatalf("%d: supplied: %q, got unexpected arguments:\n%s", i, tc.rawArgs, diff)
		}
	}
}
