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

import (
	"testing"

	"github.com/hashicorp/hcl/v2"
	"github.com/opentofu/opentofu/internal/addrs"
	"github.com/opentofu/opentofu/internal/command/arguments"
	"github.com/opentofu/opentofu/internal/lang/marks"
	"github.com/opentofu/opentofu/internal/tfdiags"
)

func TestDeprecationDiagnosticAllowed(t *testing.T) {
	localInstance := addrs.ModuleInstance{{Name: "local"}}
	remoteInstance := addrs.ModuleInstance{{Name: "remote"}}

	localCause := marks.DeprecationCauseOutput(addrs.AbsOutputValue{Module: localInstance}, "msg")
	remoteCause := marks.DeprecationCauseOutput(addrs.AbsOutputValue{Module: remoteInstance}, "msg")

	tests := map[string]struct {
		diag tfdiags.Diagnostics
		lvl  arguments.DeprecationWarningLevel
		want bool
	}{
		// ensure that diagnostics unrelated with the deprecation ones are not filtered out
		"diagnostic without extra info": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil)),
			lvl:  arguments.DeprecationWarningLevelNone,
			want: true,
		},
		"diagnostic with extra info but unrelated to deprecation": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: struct{ foo string }{foo: "bar"},
			}),
			lvl:  arguments.DeprecationWarningLevelNone,
			want: true,
		},
		// test the type of diagnostics that are generated by the deprecated outputs checks
		"remote deprecated output attribute with all level": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.Override(
				tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil),
				tfdiags.Warning,
				marks.DeprecatedDiagnosticOverride(remoteCause),
			)),
			lvl:  arguments.DeprecationWarningLevelAll,
			want: true,
		},
		"remote deprecated output attribute with local level": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.Override(
				tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil),
				tfdiags.Warning,
				marks.DeprecatedDiagnosticOverride(remoteCause),
			)),
			lvl:  arguments.DeprecationWarningLevelLocal,
			want: false,
		},
		"remote deprecated output attribute with none level": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.Override(
				tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil),
				tfdiags.Warning,
				marks.DeprecatedDiagnosticOverride(remoteCause),
			)),
			lvl:  arguments.DeprecationWarningLevelNone,
			want: false,
		},
		"local deprecated output attribute with all level": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.Override(
				tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil),
				tfdiags.Warning,
				marks.DeprecatedDiagnosticOverride(localCause),
			)),
			lvl:  arguments.DeprecationWarningLevelAll,
			want: true,
		},
		"local deprecated output attribute with local level": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.Override(
				tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil),
				tfdiags.Warning,
				marks.DeprecatedDiagnosticOverride(localCause),
			)),
			lvl:  arguments.DeprecationWarningLevelLocal,
			want: true,
		},
		"local deprecated output attribute with none level": {
			diag: tfdiags.Diagnostics{}.Append(tfdiags.Override(
				tfdiags.AttributeValue(tfdiags.Warning, "summary", "details", nil),
				tfdiags.Warning,
				marks.DeprecatedDiagnosticOverride(localCause),
			)),
			lvl:  arguments.DeprecationWarningLevelNone,
			want: false,
		},
		"remote deprecated output expr with all level": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: remoteCause,
			}),
			lvl:  arguments.DeprecationWarningLevelAll,
			want: true,
		},
		"remote deprecated output expr with local level": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: remoteCause,
			}),
			lvl:  arguments.DeprecationWarningLevelLocal,
			want: false,
		},
		"remote deprecated output expr with none level": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: remoteCause,
			}),
			lvl:  arguments.DeprecationWarningLevelNone,
			want: false,
		},
		"local deprecated output expr with all level": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: localCause,
			}),
			lvl:  arguments.DeprecationWarningLevelAll,
			want: true,
		},
		"local deprecated output expr with local level": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: localCause,
			}),
			lvl:  arguments.DeprecationWarningLevelLocal,
			want: true,
		},
		"local deprecated output expr with none level": {
			diag: tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
				Extra: localCause,
			}),
			lvl:  arguments.DeprecationWarningLevelNone,
			want: false,
		},
	}

	for name, tt := range tests {
		t.Run(name, func(t *testing.T) {
			seen := DeprecationDiagnosticAllowedSeen{}
			v := &View{
				ModuleDeprecationWarnLvl: tt.lvl,
				isRemoteModuleSource: func(path addrs.Module) bool {
					return path.Equal(remoteInstance.Module())
				},
			}

			if got, want := v.DeprecationDiagnosticAllowed(tt.diag[0], seen), tt.want; got != want {
				if want {
					t.Fatalf("expected diagnostic to be allowed but it is not")
				} else {
					t.Fatalf("expected diagnostic to not be allowed but it is")
				}
			}

			if tt.lvl != arguments.DeprecationWarningLevelNone {
				if got := v.DeprecationDiagnosticAllowed(tt.diag[0], seen); got == true {
					t.Fatalf("expected duplicate diagnostic to be ignored")
				}
			}
		})
	}
}
