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

package ephemeral

import (
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/zclconf/go-cty-debug/ctydebug"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/internal/configs/configschema"
	"github.com/hashicorp/terraform/internal/lang/marks"
)

func TestStripWriteOnlyAttributes(t *testing.T) {
	tcs := map[string]struct {
		val    cty.Value
		schema *configschema.Block
		want   cty.Value
	}{
		"primitive": {
			val: cty.ObjectVal(map[string]cty.Value{
				"value": cty.StringVal("value"),
			}),
			schema: &configschema.Block{
				Attributes: map[string]*configschema.Attribute{
					"value": {
						Type:      cty.String,
						WriteOnly: true,
					},
				},
			},
			want: cty.ObjectVal(map[string]cty.Value{
				"value": cty.NullVal(cty.String),
			}),
		},
		"complex": {
			val: cty.ObjectVal(map[string]cty.Value{
				"value": cty.ObjectVal(map[string]cty.Value{
					"value": cty.StringVal("value"),
				}),
			}),
			schema: &configschema.Block{
				Attributes: map[string]*configschema.Attribute{
					"value": {
						NestedType: &configschema.Object{
							Attributes: map[string]*configschema.Attribute{
								"value": {
									Type: cty.String,
								},
							},
							Nesting: configschema.NestingSingle,
						},
						WriteOnly: true,
					},
				},
			},
			want: cty.ObjectVal(map[string]cty.Value{
				"value": cty.NullVal(cty.Object(map[string]cty.Type{
					"value": cty.String,
				})),
			}),
		},
		"nested in object": {
			val: cty.ObjectVal(map[string]cty.Value{
				"value": cty.ObjectVal(map[string]cty.Value{
					"value": cty.StringVal("value"),
				}),
			}),
			schema: &configschema.Block{
				Attributes: map[string]*configschema.Attribute{
					"value": {
						NestedType: &configschema.Object{
							Attributes: map[string]*configschema.Attribute{
								"value": {
									Type:      cty.String,
									WriteOnly: true,
								},
							},
							Nesting: configschema.NestingSingle,
						},
					},
				},
			},
			want: cty.ObjectVal(map[string]cty.Value{
				"value": cty.ObjectVal(map[string]cty.Value{
					"value": cty.NullVal(cty.String),
				}),
			}),
		},
		"nested in list": {
			val: cty.ObjectVal(map[string]cty.Value{
				"value": cty.ListVal([]cty.Value{
					cty.ObjectVal(map[string]cty.Value{
						"value": cty.StringVal("value"),
					}),
					cty.ObjectVal(map[string]cty.Value{
						"value": cty.StringVal("value"),
					}),
				}),
			}),
			schema: &configschema.Block{
				Attributes: map[string]*configschema.Attribute{
					"value": {
						NestedType: &configschema.Object{
							Attributes: map[string]*configschema.Attribute{
								"value": {
									Type:      cty.String,
									WriteOnly: true,
								},
							},
							Nesting: configschema.NestingList,
						},
					},
				},
			},
			want: cty.ObjectVal(map[string]cty.Value{
				"value": cty.ListVal([]cty.Value{
					cty.ObjectVal(map[string]cty.Value{
						"value": cty.NullVal(cty.String),
					}),
					cty.ObjectVal(map[string]cty.Value{
						"value": cty.NullVal(cty.String),
					}),
				}),
			}),
		},
		"nested in map": {
			val: cty.ObjectVal(map[string]cty.Value{
				"value": cty.MapVal(map[string]cty.Value{
					"one": cty.ObjectVal(map[string]cty.Value{
						"value": cty.StringVal("value"),
					}),
					"two": cty.ObjectVal(map[string]cty.Value{
						"value": cty.StringVal("value"),
					}),
				}),
			}),
			schema: &configschema.Block{
				Attributes: map[string]*configschema.Attribute{
					"value": {
						NestedType: &configschema.Object{
							Attributes: map[string]*configschema.Attribute{
								"value": {
									Type:      cty.String,
									WriteOnly: true,
								},
							},
							Nesting: configschema.NestingMap,
						},
					},
				},
			},
			want: cty.ObjectVal(map[string]cty.Value{
				"value": cty.MapVal(map[string]cty.Value{
					"one": cty.ObjectVal(map[string]cty.Value{
						"value": cty.NullVal(cty.String),
					}),
					"two": cty.ObjectVal(map[string]cty.Value{
						"value": cty.NullVal(cty.String),
					}),
				}),
			}),
		},
		"preserves marks": {
			val: cty.ObjectVal(map[string]cty.Value{
				"value": cty.StringVal("value"),
			}).Mark(marks.Sensitive),
			schema: &configschema.Block{
				Attributes: map[string]*configschema.Attribute{
					"value": {
						Type:      cty.String,
						WriteOnly: true,
					},
				},
			},
			want: cty.ObjectVal(map[string]cty.Value{
				"value": cty.NullVal(cty.String),
			}).Mark(marks.Sensitive),
		},
	}

	for name, tc := range tcs {
		t.Run(name, func(t *testing.T) {
			got := StripWriteOnlyAttributes(tc.val, tc.schema)
			if diff := cmp.Diff(got, tc.want, ctydebug.CmpOptions); len(diff) > 0 {
				t.Errorf("got diff:\n%s", diff)
			}
		})
	}
}
