package scanner

import (
	"testing"

	"github.com/dd0c/drift-agent/pkg/models"
)

func TestScrubber_RedactsAWSAccessKey(t *testing.T) {
	s := NewScrubber("strict")
	input := `{"access_key": "AKIAIOSFODNN7EXAMPLE"}`
	result := s.ScrubString(input)
	if containsStr(result, "AKIAIOSFODNN7EXAMPLE") {
		t.Fatalf("AWS key not redacted: %s", result)
	}
}

func TestScrubber_RedactsRSAPrivateKey(t *testing.T) {
	s := NewScrubber("strict")
	input := `-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0Z3VS5JJcds3xfn
-----END RSA PRIVATE KEY-----`
	result := s.ScrubString(input)
	if containsStr(result, "MIIEpAIBAAKCAQEA") {
		t.Fatalf("RSA key not redacted: %s", result)
	}
}

func TestScrubber_HighEntropyStringRedacted(t *testing.T) {
	s := NewScrubber("strict")
	// 40-char hex string — looks like a custom API token
	token := "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"
	result := s.ScrubString(token)
	if containsStr(result, token) {
		t.Fatalf("High-entropy token not redacted: %s", result)
	}
}

func TestScrubber_NormalTextNotRedacted(t *testing.T) {
	s := NewScrubber("strict")
	input := "Hello world this is a normal log message"
	result := s.ScrubString(input)
	if result != input {
		t.Fatalf("Normal text was incorrectly redacted: %s", result)
	}
}

func TestScrubber_OffModePassesThrough(t *testing.T) {
	s := NewScrubber("off")
	input := "AKIAIOSFODNN7EXAMPLE"
	result := s.ScrubString(input)
	if result != input {
		t.Fatalf("Off mode should not redact: %s", result)
	}
}

func TestScrubResource_RedactsSensitiveAttributes(t *testing.T) {
	s := NewScrubber("strict")
	r := models.DriftedResource{
		ResourceType:    "aws_db_instance",
		ResourceAddress: "aws_db_instance.main",
		Diffs: []models.AttributeDiff{
			{AttributeName: "password", OldValue: "secret123", NewValue: "secret456"},
			{AttributeName: "engine_version", OldValue: "14.1", NewValue: "15.0"},
		},
	}
	scrubbed := s.ScrubResource(r)

	if scrubbed.Diffs[0].OldValue != "[REDACTED]" {
		t.Fatalf("Password old value not redacted: %s", scrubbed.Diffs[0].OldValue)
	}
	if scrubbed.Diffs[0].NewValue != "[REDACTED]" {
		t.Fatalf("Password new value not redacted: %s", scrubbed.Diffs[0].NewValue)
	}
	// Non-sensitive attribute should be preserved
	if scrubbed.Diffs[1].OldValue != "14.1" {
		t.Fatalf("Engine version should not be redacted: %s", scrubbed.Diffs[1].OldValue)
	}
}

func TestShannonEntropy_HighForRandomString(t *testing.T) {
	// Random hex string should have high entropy
	e := shannonEntropy("a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0")
	if e <= 3.5 {
		t.Fatalf("Expected entropy > 3.5 for random hex, got %f", e)
	}
}

func TestShannonEntropy_LowForRepeatedChars(t *testing.T) {
	e := shannonEntropy("aaaaaaaaaaaaaaaaaaaaaaaaa")
	if e > 0.1 {
		t.Fatalf("Expected near-zero entropy for repeated chars, got %f", e)
	}
}

func TestShannonEntropy_ZeroForEmpty(t *testing.T) {
	e := shannonEntropy("")
	if e != 0 {
		t.Fatalf("Expected 0 entropy for empty string, got %f", e)
	}
}

func containsStr(haystack, needle string) bool {
	return len(haystack) >= len(needle) && (haystack == needle || len(needle) > 0 && findStr(haystack, needle))
}

func findStr(s, sub string) bool {
	for i := 0; i <= len(s)-len(sub); i++ {
		if s[i:i+len(sub)] == sub {
			return true
		}
	}
	return false
}
