package detector

import (
	"strings"
	"testing"
)

func repeatStr(s string, n int) string { return strings.Repeat(s, n) }

// Secrets are split across string literals so GitHub secret scanning
// does not flag this test file as containing live credentials.
var (
	testAWSKey     = "key=" + "AKIA" + "IOSFODNN7EXAMPLE"
	testGHPat      = "token: " + "ghp_" + "ABCDeFgHiJkLmNoPqRsTuV" + "wXyZ1234567890"
	testGLPat      = "glpat-" + "ABCDEFGHIJ" + "KLMNOPQRST"
	testPGURL      = "postgres://" + "user:password@localhost:5432/mydb"
	testMongoURL   = "mongodb://" + "user:pass@cluster.mongodb.net/db"
	testJWT        = "eyJhbGciOiJIUzI1NiJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIn0." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
	testGenericSec = `password = "mySecretPass` + `word123"`
	testPyOAIKey   = `client = OpenAI(api_key="sk-test` + `key1234567890abcdefghij")`
)

func TestDetectSecrets(t *testing.T) {
	tests := []struct {
		name       string
		input      string
		wantN      int
		wantRuleID string // rule that must fire (empty = any)
		wantText   string // expected secret value (empty = any)
	}{
		{
			"aws access key",
			testAWSKey,
			1, "aws-access-key", "AKIA" + "IOSFODNN7EXAMPLE",
		},
		{
			"github pat",
			testGHPat,
			1, "github-pat", "",
		},
		{
			"gitlab pat",
			testGLPat,
			1, "gitlab-pat", "",
		},
		{
			"anthropic api key",
			"sk-ant-" + "api03-" + repeatStr("A", 32),
			1, "anthropic-api-key", "",
		},
		{
			"openai sk-proj",
			"sk-proj-" + repeatStr("A", 50),
			1, "openai-api-key-new", "",
		},
		{
			"huggingface token",
			"hf_" + repeatStr("a", 32),
			1, "huggingface-token", "",
		},
		{
			"groq api key",
			"gsk_" + repeatStr("A", 52),
			1, "groq-api-key", "",
		},
		{
			"postgres url",
			testPGURL,
			1, "db-postgres-url", "",
		},
		{
			"mongodb url",
			testMongoURL,
			1, "db-mongodb-url", "",
		},
		{
			"pem private key header",
			"-----BEGIN RSA " + "PRIVATE KEY-----",
			1, "private-key-header", "",
		},
		{
			"jwt token",
			testJWT,
			1, "jwt-token", "",
		},
		{
			"generic secret assignment",
			testGenericSec,
			1, "generic-secret", "mySecretPass" + "word123",
		},
		{
			"python inline openai key",
			testPyOAIKey,
			1, "python-openai-client-inline-key", "",
		},
		{
			"gcp service account",
			`{"type": "service_account", "project_id": "my-project"}`,
			1, "gcp-service-account", "",
		},
		{
			"only secret value redacted not key name",
			"ANTHROPIC_API_KEY=" + "sk-ant-" + "api03-" + repeatStr("B", 32),
			1, "anthropic-api-key-env", "",
		},
		{
			"no match plain text",
			"Hallo, das ist ein normaler Text ohne Secrets.",
			0, "", "",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := detectSecrets(tt.input)
			if len(got) < tt.wantN {
				t.Fatalf("got %d findings, want at least %d — %v", len(got), tt.wantN, got)
			}
			if tt.wantN == 0 {
				return
			}
			if tt.wantRuleID != "" {
				found := false
				for _, f := range got {
					if f.RuleID == tt.wantRuleID {
						found = true
						if tt.wantText != "" && f.Text != tt.wantText {
							t.Errorf("rule %q: text = %q, want %q", tt.wantRuleID, f.Text, tt.wantText)
						}
						break
					}
				}
				if !found {
					t.Errorf("rule %q not fired — got rules: %v", tt.wantRuleID, ruleIDs(got))
				}
			}
			for _, f := range got {
				if f.Type != PiiSecret {
					t.Errorf("type = %v, want SECRET", f.Type)
				}
				if f.Confidence <= 0 {
					t.Errorf("confidence = %v, want > 0", f.Confidence)
				}
			}
		})
	}
}

func ruleIDs(findings []Finding) []string {
	ids := make([]string, len(findings))
	for i, f := range findings {
		ids[i] = f.RuleID
	}
	return ids
}
