package secrets

import (
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestGenerateMasterKey(t *testing.T) {
	k1, err := GenerateMasterKey()
	if err != nil {
		t.Fatalf("GenerateMasterKey: %v", err)
	}
	if len(k1) != MasterKeyLen {
		t.Fatalf("key len = %d, want %d", len(k1), MasterKeyLen)
	}
	k2, err := GenerateMasterKey()
	if err != nil {
		t.Fatalf("GenerateMasterKey 2: %v", err)
	}
	if string(k1) == string(k2) {
		t.Fatalf("GenerateMasterKey returned identical bytes twice — crypto/rand not random?")
	}
}

func TestSaveLoadMasterKey_0600(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "master.key")
	key, err := GenerateMasterKey()
	if err != nil {
		t.Fatalf("GenerateMasterKey: %v", err)
	}
	if err := SaveMasterKey(path, key); err != nil {
		t.Fatalf("SaveMasterKey: %v", err)
	}
	info, err := os.Stat(path)
	if err != nil {
		t.Fatalf("stat: %v", err)
	}
	if info.Mode().Perm() != 0o600 {
		t.Fatalf("perm = %04o, want 0600", info.Mode().Perm())
	}
	loaded, err := LoadMasterKey(path)
	if err != nil {
		t.Fatalf("LoadMasterKey: %v", err)
	}
	if string(loaded) != string(key) {
		t.Fatalf("loaded key differs from saved key")
	}
}

func TestLoadMasterKey_RefusesLoose(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "master.key")
	key, err := GenerateMasterKey()
	if err != nil {
		t.Fatalf("GenerateMasterKey: %v", err)
	}
	if err := SaveMasterKey(path, key); err != nil {
		t.Fatalf("SaveMasterKey: %v", err)
	}
	if err := os.Chmod(path, 0o644); err != nil {
		t.Fatalf("chmod 0644: %v", err)
	}
	_, err = LoadMasterKey(path)
	if err == nil {
		t.Fatalf("LoadMasterKey succeeded with mode 0644, want refuse")
	}
	if !strings.Contains(err.Error(), "REFUSE") {
		t.Fatalf("error missing REFUSE marker: %v", err)
	}
}

func TestLoadMasterKey_WrongLen(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "master.key")
	if err := os.WriteFile(path, []byte("tooshort"), 0o600); err != nil {
		t.Fatalf("write: %v", err)
	}
	_, err := LoadMasterKey(path)
	if err == nil {
		t.Fatalf("LoadMasterKey succeeded with wrong length, want error")
	}
}

func TestDeriveNamespaceKey_Deterministic(t *testing.T) {
	mk, _ := GenerateMasterKey()
	a1, err := DeriveNamespaceKey(mk, "nsA")
	if err != nil {
		t.Fatalf("DeriveA: %v", err)
	}
	a2, err := DeriveNamespaceKey(mk, "nsA")
	if err != nil {
		t.Fatalf("DeriveA 2: %v", err)
	}
	if string(a1) != string(a2) {
		t.Fatalf("same (master,namespace) yielded different sub-keys")
	}
	if len(a1) != SubKeyLen {
		t.Fatalf("sub-key len = %d, want %d", len(a1), SubKeyLen)
	}
}

func TestDeriveNamespaceKey_DifferentNamespaces(t *testing.T) {
	mk, _ := GenerateMasterKey()
	a, err := DeriveNamespaceKey(mk, "nsA")
	if err != nil {
		t.Fatalf("DeriveA: %v", err)
	}
	b, err := DeriveNamespaceKey(mk, "nsB")
	if err != nil {
		t.Fatalf("DeriveB: %v", err)
	}
	if string(a) == string(b) {
		t.Fatalf("different namespaces yielded the same sub-key")
	}
}

func TestDeriveNamespaceKey_EmptyNamespace(t *testing.T) {
	mk, _ := GenerateMasterKey()
	if _, err := DeriveNamespaceKey(mk, ""); err == nil {
		t.Fatalf("DeriveNamespaceKey(empty) succeeded, want error")
	}
}

func TestEncryptDecryptLine_RoundTrip(t *testing.T) {
	mk, _ := GenerateMasterKey()
	nsKey, err := DeriveNamespaceKey(mk, "ns")
	if err != nil {
		t.Fatalf("Derive: %v", err)
	}
	pt := []byte("API_KEY=supersecret")
	enc, err := EncryptLine(nsKey, 1, pt)
	if err != nil {
		t.Fatalf("EncryptLine: %v", err)
	}
	dec, err := DecryptLine(nsKey, 1, enc)
	if err != nil {
		t.Fatalf("DecryptLine: %v", err)
	}
	if string(dec) != string(pt) {
		t.Fatalf("round-trip mismatch: got %q want %q", dec, pt)
	}
}

func TestNonceUniqueness(t *testing.T) {
	mk, _ := GenerateMasterKey()
	nsKey, _ := DeriveNamespaceKey(mk, "ns")
	pt := []byte("same plaintext")
	a, _ := EncryptLine(nsKey, 1, pt)
	b, _ := EncryptLine(nsKey, 1, pt)
	if a == b {
		t.Fatalf("encrypting same plaintext twice yielded the same ciphertext — nonce not random")
	}
}

func TestAADLineNumber_SwapFails(t *testing.T) {
	mk, _ := GenerateMasterKey()
	nsKey, _ := DeriveNamespaceKey(mk, "ns")
	line1 := []byte("A=1")
	line2 := []byte("B=2")
	enc1, _ := EncryptLine(nsKey, 1, line1)
	enc2, _ := EncryptLine(nsKey, 2, line2)
	if _, err := DecryptLine(nsKey, 1, enc1); err != nil {
		t.Fatalf("decrypt line1 at pos1 failed: %v", err)
	}
	if _, err := DecryptLine(nsKey, 2, enc2); err != nil {
		t.Fatalf("decrypt line2 at pos2 failed: %v", err)
	}
	if _, err := DecryptLine(nsKey, 1, enc2); err == nil {
		t.Fatalf("decrypted line2 ciphertext at pos1 — AAD line-swap NOT detected")
	}
	if _, err := DecryptLine(nsKey, 2, enc1); err == nil {
		t.Fatalf("decrypted line1 ciphertext at pos2 — AAD line-swap NOT detected")
	}
}

func TestEncryptEnvFile_RoundTrip(t *testing.T) {
	mk, _ := GenerateMasterKey()
	nsKey, _ := DeriveNamespaceKey(mk, "ns")
	lines := []string{
		"API_KEY=abc",
		"DB_PASSWORD=hunter2",
		"",
		"TOKEN=xyz",
	}
	enc, err := EncryptEnvFile(nsKey, lines)
	if err != nil {
		t.Fatalf("EncryptEnvFile: %v", err)
	}
	dec, err := DecryptEnvFile(nsKey, enc)
	if err != nil {
		t.Fatalf("DecryptEnvFile: %v", err)
	}
	if len(dec) != len(lines) {
		t.Fatalf("len(dec) = %d, want %d", len(dec), len(lines))
	}
	for i, want := range lines {
		if dec[i] != want {
			t.Fatalf("line %d: got %q want %q", i, dec[i], want)
		}
	}
}

func TestGenerateLoadCredentialFile(t *testing.T) {
	mk, _ := GenerateMasterKey()
	nsKey, _ := DeriveNamespaceKey(mk, "ns")
	lines := []string{
		"API_KEY=value1",
		"DB_PASSWORD=value2",
	}
	enc, err := EncryptEnvFile(nsKey, lines)
	if err != nil {
		t.Fatalf("EncryptEnvFile: %v", err)
	}
	encLines := strings.Split(strings.TrimRight(enc, "\n"), "\n")
	m, err := GenerateLoadCredentialFile(nsKey, encLines)
	if err != nil {
		t.Fatalf("GenerateLoadCredentialFile: %v", err)
	}
	if m["API_KEY"] != "value1" {
		t.Fatalf("API_KEY = %q, want value1", m["API_KEY"])
	}
	if m["DB_PASSWORD"] != "value2" {
		t.Fatalf("DB_PASSWORD = %q, want value2", m["DB_PASSWORD"])
	}
	if len(m) != 2 {
		t.Fatalf("map size = %d, want 2", len(m))
	}
}
