package queryd

import (
	"strings"
	"testing"

	"git.agentview.dev/profit/golangLAKEHOUSE/internal/secrets"
	"git.agentview.dev/profit/golangLAKEHOUSE/internal/shared"
)

// Closes R-008: db.go owns sqlEscape + redactCreds + buildBootstrap,
// none of which had tests. The first two are pure functions trivial
// to table-test; buildBootstrap is also pure (S3Config + creds → SQL
// strings) so we can exercise its endpoint-normalization branches
// without booting DuckDB.

func TestSqlEscape(t *testing.T) {
	cases := []struct {
		name string
		in   string
		want string
	}{
		{"no quotes", "hello", "hello"},
		{"single quote", "O'Reilly", "O''Reilly"},
		{"double quote pair", "''", "''''"},
		{"trailing quote", "foo'", "foo''"},
		{"leading quote", "'foo", "''foo"},
		{"empty string", "", ""},
		{"only quotes", "'''", "''''''"},
		{"mixed punctuation", "it's a 'test'", "it''s a ''test''"},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := sqlEscape(tc.in)
			if got != tc.want {
				t.Errorf("sqlEscape(%q) = %q, want %q", tc.in, got, tc.want)
			}
		})
	}
}

func TestRedactCreds(t *testing.T) {
	cases := []struct {
		name  string
		creds secrets.S3Credentials
		msg   string
		want  string
	}{
		{
			"both keys redacted",
			secrets.S3Credentials{AccessKeyID: "AKIATEST", SecretAccessKey: "topsecret"},
			"failed: KEY_ID 'AKIATEST' SECRET 'topsecret'",
			"failed: KEY_ID '[REDACTED-KEY]' SECRET '[REDACTED-SECRET]'",
		},
		{
			"only access key present",
			secrets.S3Credentials{AccessKeyID: "AKIATEST", SecretAccessKey: ""},
			"echo: AKIATEST again",
			"echo: [REDACTED-KEY] again",
		},
		{
			"only secret present",
			secrets.S3Credentials{AccessKeyID: "", SecretAccessKey: "mysecret"},
			"echo: mysecret here",
			"echo: [REDACTED-SECRET] here",
		},
		{
			"empty creds = no change",
			secrets.S3Credentials{},
			"failed: nothing to scrub",
			"failed: nothing to scrub",
		},
		{
			"value appears multiple times",
			secrets.S3Credentials{AccessKeyID: "AKIATEST"},
			"AKIATEST failed because AKIATEST",
			"[REDACTED-KEY] failed because [REDACTED-KEY]",
		},
		{
			"key value collision with placeholder string is lossy but safe",
			secrets.S3Credentials{AccessKeyID: "[REDACTED-KEY]"},
			"loop: [REDACTED-KEY]",
			"loop: [REDACTED-KEY]",
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got := redactCreds(tc.msg, tc.creds)
			if got != tc.want {
				t.Errorf("redactCreds:\n  msg=%q\n  got=%q\n  want=%q", tc.msg, got, tc.want)
			}
		})
	}
}

func TestBuildBootstrap_StatementOrder(t *testing.T) {
	stmts := buildBootstrap(
		shared.S3Config{Endpoint: "http://localhost:9000", Region: "us-east-1", UsePathStyle: true},
		secrets.S3Credentials{AccessKeyID: "key", SecretAccessKey: "secret"},
	)
	if len(stmts) != 3 {
		t.Fatalf("want 3 statements, got %d: %v", len(stmts), stmts)
	}
	if stmts[0] != "INSTALL httpfs" {
		t.Errorf("stmt[0] = %q, want INSTALL httpfs", stmts[0])
	}
	if stmts[1] != "LOAD httpfs" {
		t.Errorf("stmt[1] = %q, want LOAD httpfs", stmts[1])
	}
	if !strings.HasPrefix(stmts[2], "CREATE OR REPLACE SECRET") {
		t.Errorf("stmt[2] should start with CREATE OR REPLACE SECRET, got %q", stmts[2])
	}
}

func TestBuildBootstrap_EndpointSchemes(t *testing.T) {
	cases := []struct {
		name           string
		endpoint       string
		wantHostInSQL  string
		wantUseSSLTrue bool
	}{
		{"http strips scheme, USE_SSL false",
			"http://minio:9000", "minio:9000", false},
		{"https keeps SSL true",
			"https://s3.example.com", "s3.example.com", true},
		{"no scheme defaults SSL true (ambient prod)",
			"s3.amazonaws.com", "s3.amazonaws.com", true},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			stmts := buildBootstrap(
				shared.S3Config{Endpoint: tc.endpoint, Region: "us-east-1"},
				secrets.S3Credentials{AccessKeyID: "k", SecretAccessKey: "s"},
			)
			secret := stmts[2]
			wantEndpointFrag := "ENDPOINT '" + tc.wantHostInSQL + "'"
			if !strings.Contains(secret, wantEndpointFrag) {
				t.Errorf("secret SQL missing %q\n  got: %s", wantEndpointFrag, secret)
			}
			wantSSL := "USE_SSL false"
			if tc.wantUseSSLTrue {
				wantSSL = "USE_SSL true"
			}
			if !strings.Contains(secret, wantSSL) {
				t.Errorf("secret SQL missing %q\n  got: %s", wantSSL, secret)
			}
		})
	}
}

func TestBuildBootstrap_URLStyle(t *testing.T) {
	pathStmts := buildBootstrap(
		shared.S3Config{Endpoint: "http://m:9000", UsePathStyle: true},
		secrets.S3Credentials{AccessKeyID: "k", SecretAccessKey: "s"},
	)
	if !strings.Contains(pathStmts[2], "URL_STYLE 'path'") {
		t.Errorf("UsePathStyle=true should produce URL_STYLE 'path'\n  got: %s", pathStmts[2])
	}

	vhostStmts := buildBootstrap(
		shared.S3Config{Endpoint: "https://m", UsePathStyle: false},
		secrets.S3Credentials{AccessKeyID: "k", SecretAccessKey: "s"},
	)
	if !strings.Contains(vhostStmts[2], "URL_STYLE 'vhost'") {
		t.Errorf("UsePathStyle=false should produce URL_STYLE 'vhost'\n  got: %s", vhostStmts[2])
	}
}

func TestBuildBootstrap_EscapesCredentialQuotes(t *testing.T) {
	// Per the inline comment: "creds shouldn't contain ' but a future
	// SSO token might." This is the test that asserts the belt holds
	// when the suspenders snap.
	stmts := buildBootstrap(
		shared.S3Config{Endpoint: "https://m", Region: "us-east-1"},
		secrets.S3Credentials{
			AccessKeyID:     "key'with'quotes",
			SecretAccessKey: "secret",
		},
	)
	secret := stmts[2]
	// Escaped form: each ' became ''.
	want := "KEY_ID 'key''with''quotes'"
	if !strings.Contains(secret, want) {
		t.Errorf("expected escaped key in SQL\n  want fragment: %s\n  got: %s", want, secret)
	}
}
