package test_test

import (
	"fmt"
	"path/filepath"
	"testing"

	"github.com/gruntwork-io/terragrunt/test/helpers"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	testFixtureQueueStrictInclude             = "fixtures/queue-strict-include"
	testFixtureQueueStrictIncludeUnitsReading = "fixtures/queue-strict-include-units-reading"
)

// TestQueueStrictIncludeWithDependencyNotInQueue tests that when using --queue-include-dir
// or --filter, units can run even when their dependencies are not in the queue (but have existing state).
func TestQueueStrictIncludeWithDependencyNotInQueue(t *testing.T) {
	t.Parallel()

	setup := func(t *testing.T) string {
		t.Helper()

		// Create test fixture with dependency chain: transitive-dependency -> dependency -> dependent
		tmpEnvPath := helpers.CopyEnvironment(t, testFixtureQueueStrictInclude)
		helpers.CleanupTerraformFolder(t, tmpEnvPath)
		testPath := filepath.Join(tmpEnvPath, testFixtureQueueStrictInclude)

		// First, apply all units to create state
		// This simulates the scenario where units have been previously applied
		stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t,
			fmt.Sprintf(
				"terragrunt run --log-level debug --all --non-interactive --working-dir %s -- apply -auto-approve",
				testPath,
			),
		)
		require.NoError(t, err, "Failed to apply all units initially\nstdout: %s\nstderr: %s", stdout, stderr)

		// Verify all units were applied
		assert.Contains(t, stdout+stderr, "transitive-dependency", "transitive-dependency should be applied")
		assert.Contains(t, stdout+stderr, "dependency", "dependency should be applied")
		assert.Contains(t, stdout+stderr, "dependent", "dependent should be applied")

		return testPath
	}

	t.Run("queue-include-dir", func(t *testing.T) {
		t.Parallel()

		testPath := setup(t)

		helpers.CleanupTerraformFolder(t, testPath)

		// Test with --queue-include-dir to only include dependency
		// The dependency unit depends on transitive-dependency, which should not be in the queue
		// but should be considered ready because it has existing state
		cmd := fmt.Sprintf(
			"terragrunt run --log-level debug --all --non-interactive --working-dir %s --queue-include-dir 'dependency' -- plan",
			testPath,
		)
		stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)

		// The command should succeed
		require.NoError(
			t,
			err,
			"Command should succeed when dependency has existing state\nstdout: %s\nstderr: %s",
			stdout,
			stderr,
		)

		// Verify that dependency unit was processed
		output := stdout + stderr
		assert.Contains(t, output, "dependency", "dependency unit should be processed")

		// Verify that transitive-dependency is NOT in the queue (filtered out)
		// but dependency still runs successfully
		assert.Contains(t, output, "found 1 readyEntries tasks",
			"Should show 'found 1 readyEntries tasks' - dependency should run even though transitive-dependency is not in queue")
	})

	t.Run("queue-include-dir and destroy", func(t *testing.T) {
		t.Parallel()

		testPath := setup(t)

		helpers.CleanupTerraformFolder(t, testPath)

		// Test with --queue-include-dir to only include dependency
		// The dependency unit depends on transitive-dependency, which should not be in the queue
		// but should be considered ready because it has existing state
		cmd := fmt.Sprintf(
			"terragrunt run --log-level debug --all --non-interactive --working-dir %s --queue-include-dir 'dependency' -- destroy -auto-approve",
			testPath,
		)
		stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)

		// The command should succeed
		require.NoError(
			t,
			err,
			"Command should succeed when dependency has existing state\nstdout: %s\nstderr: %s",
			stdout,
			stderr,
		)

		// Verify that dependency unit was processed
		output := stdout + stderr
		assert.Contains(t, output, "dependency", "dependency unit should be processed")

		// Verify that transitive-dependency is NOT in the queue (filtered out)
		// but dependency still runs successfully
		assert.Contains(
			t,
			output,
			"found 1 readyEntries tasks",
			"Should show 'found 1 readyEntries tasks' - dependency should run even though transitive-dependency is not in queue",
		)
	})

	t.Run("filter flag", func(t *testing.T) {
		t.Parallel()

		testPath := setup(t)

		helpers.CleanupTerraformFolder(t, testPath)

		// Test with --filter to only include dependency
		cmd := fmt.Sprintf(
			"terragrunt run --log-level debug --all --non-interactive --working-dir %s --filter './dependency' -- plan",
			testPath,
		)
		stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)

		// The command should succeed
		require.NoError(
			t,
			err,
			"Command should succeed when dependency has existing state\nstdout: %s\nstderr: %s",
			stdout,
			stderr,
		)

		// Verify that dependency unit was processed
		output := stdout + stderr
		assert.Contains(t, output, "dependency", "dependency unit should be processed")

		// Verify that transitive-dependency is NOT in the queue (filtered out)
		// but dependency still runs successfully
		assert.Contains(
			t,
			output,
			"found 1 readyEntries tasks",
			"Should show 'found 1 readyEntries tasks' - dependency should run even though transitive-dependency is not in queue",
		)
	})

	t.Run("filter flag and destroy", func(t *testing.T) {
		t.Parallel()

		testPath := setup(t)

		helpers.CleanupTerraformFolder(t, testPath)

		// Test with --filter to only include dependency
		cmd := fmt.Sprintf(
			"terragrunt run --log-level debug --all --non-interactive --working-dir %s --filter './dependency' -- destroy -auto-approve",
			testPath,
		)
		stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)

		// The command should succeed
		require.NoError(
			t,
			err,
			"Command should succeed when dependency has existing state\nstdout: %s\nstderr: %s",
			stdout,
			stderr,
		)

		// Verify that dependency unit was processed
		output := stdout + stderr
		assert.Contains(t, output, "dependency", "dependency unit should be processed")

		// Verify that transitive-dependency is NOT in the queue (filtered out)
		// but dependency still runs successfully
		assert.Contains(
			t,
			output,
			"found 1 readyEntries tasks",
			"Should show 'found 1 readyEntries tasks' - dependency should run even though transitive-dependency is not in queue",
		)
	})
}

// TestQueueStrictIncludeWithUnitsReadingWithoutIncludeDir reproduces the bug where
// --queue-include-units-reading (but no --queue-include-dir)
// fails to discover units that read the specified file.
func TestQueueStrictIncludeWithUnitsReadingWithoutIncludeDir(t *testing.T) {
	t.Parallel()

	tmpEnvPath := helpers.CopyEnvironment(t, testFixtureQueueStrictIncludeUnitsReading)
	helpers.CleanupTerraformFolder(t, tmpEnvPath)
	testPath := filepath.Join(tmpEnvPath, testFixtureQueueStrictIncludeUnitsReading)

	// This reproduces the bug: --queue-include-units-reading
	// without --queue-include-dir should still only include units that read the file
	cmd := fmt.Sprintf("terragrunt run --all --non-interactive --working-dir %s --queue-include-units-reading=sources/source.hcl -- plan", testPath)
	stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, cmd)

	// The command should succeed and discover the unit
	require.NoError(t, err, "Command should succeed\nstdout: %s\nstderr: %s", stdout, stderr)

	output := stdout + stderr

	// Verify that the unit reading sources/source.hcl is discovered and included
	// This should pass after the fix, but currently fails due to the bug
	assert.Contains(t, output, "live/foo", "Unit live/foo that reads sources/source.hcl should be included")
	assert.NotContains(t, output, "No units discovered", "Should discover units reading sources/source.hcl")
}
