package connector

import (
	"encoding/json"
	"reflect"
	"testing"
)

func TestApplyFieldMappingsValuePolicy(t *testing.T) {
	tests := []struct {
		name       string
		mapping    FieldMappingDefV2
		source     map[string]interface{}
		wantTarget map[string]interface{}
		wantError  bool
	}{
		{
			name:    "translated",
			mapping: mappingV2("Stage", "Status", `{"retired":"Current"}`, ""),
			source:  map[string]interface{}{"Stage": "retired"}, wantTarget: map[string]interface{}{"Status": "Current"},
		},
		{
			name:    "empty value map preserves v1 passthrough",
			mapping: mappingV2("Stage", "Status", "", ""),
			source:  map[string]interface{}{"Stage": "Current"}, wantTarget: map[string]interface{}{"Status": "Current"},
		},
		{
			name: "default follows the same authored translation as connector mappings",
			mapping: FieldMappingDefV2{
				FormatVersion: 2,
				Mapping:       FieldMappingDef{SourceField: "Stage", TargetField: "Status", DefaultValue: "retired"},
				ValueMap:      `{"retired":"Current"}`,
			},
			source: map[string]interface{}{}, wantTarget: map[string]interface{}{"Status": "Current"},
		},
		{
			name:    "skip leaves default owner absent",
			mapping: mappingV2("Stage", "Status", `{"active":"Current"}`, OnUnmappedValueSkip),
			source:  map[string]interface{}{"Stage": "retired"}, wantTarget: map[string]interface{}{},
		},
		{
			name:    "passthrough",
			mapping: mappingV2("Stage", "Status", `{"active":"Current"}`, OnUnmappedValuePassthrough),
			source:  map[string]interface{}{"Stage": "retired"}, wantTarget: map[string]interface{}{"Status": "retired"},
		},
		{
			name:    "error is fail closed",
			mapping: mappingV2("Stage", "Status", `{"active":"Current"}`, ""),
			source:  map[string]interface{}{"Stage": "retired"}, wantTarget: map[string]interface{}{}, wantError: true,
		},
		{
			name:    "authored blank is a value",
			mapping: mappingV2("Stage", "Status", `{"retired":""}`, ""),
			source:  map[string]interface{}{"Stage": "retired"}, wantTarget: map[string]interface{}{"Status": ""},
		},
		{
			name:    "blank source can be skipped",
			mapping: mappingV2("Stage", "Status", `{"active":"Current"}`, OnUnmappedValueSkip),
			source:  map[string]interface{}{"Stage": ""}, wantTarget: map[string]interface{}{},
		},
		{
			name:    "blank source can pass through",
			mapping: mappingV2("Stage", "Status", `{"active":"Current"}`, OnUnmappedValuePassthrough),
			source:  map[string]interface{}{"Stage": ""}, wantTarget: map[string]interface{}{"Status": ""},
		},
		{
			name:    "number uses canonical scalar key",
			mapping: mappingV2("Score", "Band", `{"1":"one"}`, ""),
			source:  map[string]interface{}{"Score": 1}, wantTarget: map[string]interface{}{"Band": "one"},
		},
		{
			name:    "non scalar fails even when policy is skip",
			mapping: mappingV2("Stage", "Status", `{"active":"Current"}`, OnUnmappedValueSkip),
			source:  map[string]interface{}{"Stage": []string{"active"}}, wantTarget: map[string]interface{}{}, wantError: true,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			target := map[string]interface{}{}
			err := ApplyFieldMappingsV2([]FieldMappingDefV2{test.mapping}, test.source, target)
			if (err != nil) != test.wantError {
				t.Fatalf("ApplyFieldMappings() error = %v, wantError %v", err, test.wantError)
			}
			if !reflect.DeepEqual(target, test.wantTarget) {
				t.Fatalf("target = %#v, want %#v", target, test.wantTarget)
			}
		})
	}
}

func TestApplyFieldMappingsV2RejectsUnknownFormatWithoutMutation(t *testing.T) {
	target := map[string]interface{}{"Existing": "kept"}
	err := ApplyFieldMappingsV2([]FieldMappingDefV2{{
		FormatVersion: 3,
		Mapping:       FieldMappingDef{SourceField: "Stage", TargetField: "Status"},
	}}, map[string]interface{}{"Stage": "Current"}, target)
	if err == nil || !reflect.DeepEqual(target, map[string]interface{}{"Existing": "kept"}) {
		t.Fatalf("unknown format result: target=%#v err=%v", target, err)
	}
}

func TestApplyFieldMappingsErrorDoesNotPartiallyMutateTarget(t *testing.T) {
	target := map[string]interface{}{"Existing": "kept"}
	err := ApplyFieldMappingsV2([]FieldMappingDefV2{
		mappingV2("One", "First", "", ""),
		mappingV2("Two", "Second", `{"known":"ok"}`, ""),
	}, map[string]interface{}{"One": "copied", "Two": "unknown"}, target)
	if err == nil {
		t.Fatal("ApplyFieldMappings() error = nil, want fail-closed translation error")
	}
	if want := map[string]interface{}{"Existing": "kept"}; !reflect.DeepEqual(target, want) {
		t.Fatalf("target mutated on error: %#v, want %#v", target, want)
	}
}

func TestTranslateMappedValuePreservesExactDecimal(t *testing.T) {
	want := json.Number("9007199254740993.000000000000000001")
	got, include, matched, err := TranslateMappedValue(`{"source":9007199254740993.000000000000000001}`, "", "source")
	if err != nil || !include || !matched || got != want {
		t.Fatalf("exact decimal = (%#v, %v, %v, %v), want (%#v, true, true, nil)", got, include, matched, err, want)
	}
}

func mappingV2(sourceField, targetField, valueMap, onUnmapped string) FieldMappingDefV2 {
	return FieldMappingDefV2{
		FormatVersion:   2,
		Mapping:         FieldMappingDef{SourceField: sourceField, TargetField: targetField},
		ValueMap:        valueMap,
		OnUnmappedValue: onUnmapped,
	}
}

func TestTranslateMappedValueReportsAuthoredTranslation(t *testing.T) {
	translated, include, matched, err := TranslateMappedValue(`{"same":"same"}`, "", "same")
	if err != nil || !include || !matched || translated != "same" {
		t.Fatalf("authored same-value translation = (%#v, %v, %v, %v)", translated, include, matched, err)
	}
	translated, include, matched, err = TranslateMappedValue(
		`{"known":"mapped"}`, OnUnmappedValuePassthrough, "unknown",
	)
	if err != nil || !include || matched || translated != "unknown" {
		t.Fatalf("passthrough = (%#v, %v, %v, %v)", translated, include, matched, err)
	}
}

func TestTranslateMappedValueRejectsInvalidPolicyBeforeAnyLookup(t *testing.T) {
	for _, rawMap := range []string{"", `{"known":"mapped"}`} {
		if _, _, _, err := TranslateMappedValue(rawMap, "bogus", "known"); err == nil {
			t.Fatalf("invalid policy was accepted with ValueMap %q", rawMap)
		}
	}
}
