package internal

import (
	"os"
	"testing"
)

func TestNewHashCrafter(t *testing.T) {
	hc := NewHashCrafter("test.txt", " ,|,-", "sha256", "testhash", false)

	if hc.hashType != "sha256" {
		t.Errorf("Expected hashType to be sha256, got %s", hc.hashType)
	}

	if hc.targetHash != "testhash" {
		t.Errorf("Expected targetHash to be testhash, got %s", hc.targetHash)
	}

	if hc.verbose {
		t.Errorf("Expected verbose to be false, got %v", hc.verbose)
	}
}

func TestParseSeparators(t *testing.T) {
	tests := []struct {
		input    string
		expected []string
	}{
		{" ,|,-", []string{" ", "|", "-"}},
		{"a,b,c", []string{"a", "b", "c"}},
		{"", []string{""}},
		{"  ,  ,  ", []string{"  ", "  ", "  "}},
	}

	for _, test := range tests {
		result := parseSeparators(test.input)
		if !compareStringSlices(result, test.expected) {
			t.Errorf("parseSeparators(%q) = %v (len: %d), expected %v (len: %d)", test.input, result, len(result), test.expected, len(test.expected))
		}
	}
}

func TestLoadStrings(t *testing.T) {
	// Create temporary file
	tmpfile, err := os.CreateTemp("", "test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	// Write test data
	testData := "hello\nworld\ntest\n"
	if _, err := tmpfile.Write([]byte(testData)); err != nil {
		t.Fatal(err)
	}
	tmpfile.Close()

	// Test loading
	hc := NewHashCrafter(tmpfile.Name(), " ,|", "sha256", "test", false)
	err = hc.LoadStrings(tmpfile.Name())
	if err != nil {
		t.Fatalf("LoadStrings failed: %v", err)
	}

	expected := []string{"hello", "world", "test"}
	if !compareStringSlices(hc.strings, expected) {
		t.Errorf("Expected strings %v, got %v", expected, hc.strings)
	}
}

func TestLoadStringsEmptyFile(t *testing.T) {
	// Create temporary empty file
	tmpfile, err := os.CreateTemp("", "test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())
	tmpfile.Close()

	hc := NewHashCrafter(tmpfile.Name(), " ,|", "sha256", "test", false)
	err = hc.LoadStrings(tmpfile.Name())
	if err == nil {
		t.Error("Expected error for empty file, got nil")
	}
}

func TestGetHashFunction(t *testing.T) {
	hc := NewHashCrafter("test.txt", " ,|", "sha256", "test", false)

	hashFunc := hc.getHashFunction()
	result := hashFunc("test")

	// SHA256 of "test" should be 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
	expected := "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
	if result != expected {
		t.Errorf("Expected hash %s, got %s", expected, result)
	}
}

func TestGeneratePermutations(t *testing.T) {
	hc := NewHashCrafter("test.txt", " ,|", "sha256", "test", false)
	hc.strings = []string{"admin", "password"}

	permutations := hc.generatePermutations()

	// For 2 strings with 2 separators, we expect:
	// - Subsets: [], [admin], [password], [admin,password]
	// - For [admin]: admin, admin , admin|
	// - For [password]: password, password , password|
	// - For [admin,password]: adminpassword, admin password, admin|password, passwordadmin, password admin, password|admin
	// - Total: 2 + 2 + 6 = 10 combinations
	expected := []string{
		"admin",          // [admin] with no separator
		"password",       // [password] with no separator
		"adminpassword",  // [admin,password] with no separator
		"admin password", // [admin,password] with space
		"admin|password", // [admin,password] with pipe
		"passwordadmin",  // [password,admin] with no separator
		"password admin", // [password,admin] with space
		"password|admin", // [password,admin] with pipe
	}

	if !compareStringSlices(permutations, expected) {
		t.Errorf("Expected permutations %v, got %v", expected, permutations)
	}
}

func TestFindMatch(t *testing.T) {
	// Create temporary file
	tmpfile, err := os.CreateTemp("", "test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	// Write test data
	testData := "test\n"
	if _, err := tmpfile.Write([]byte(testData)); err != nil {
		t.Fatal(err)
	}
	tmpfile.Close()

	// SHA256 of "test" is 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
	hc := NewHashCrafter(tmpfile.Name(), " ,|", "sha256", "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", false)

	err = hc.LoadStrings(tmpfile.Name())
	if err != nil {
		t.Fatalf("LoadStrings failed: %v", err)
	}

	matches, err := hc.FindMatch()
	if err != nil {
		t.Fatalf("FindMatch failed: %v", err)
	}

	if len(matches) == 0 {
		t.Error("Expected to find at least one match for 'test'")
	}

	// Check that "test" is in the matches
	found := false
	for _, match := range matches {
		if match == "test" {
			found = true
			break
		}
	}

	if !found {
		t.Error("Expected 'test' to be in matches")
	}
}

func TestFindMatchNoMatch(t *testing.T) {
	// Create temporary file
	tmpfile, err := os.CreateTemp("", "test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(tmpfile.Name())

	// Write test data
	testData := "hello\nworld\n"
	if _, err := tmpfile.Write([]byte(testData)); err != nil {
		t.Fatal(err)
	}
	tmpfile.Close()

	// Use a hash that won't match
	hc := NewHashCrafter(tmpfile.Name(), " ,|", "sha256", "0000000000000000000000000000000000000000000000000000000000000000", false)

	err = hc.LoadStrings(tmpfile.Name())
	if err != nil {
		t.Fatalf("LoadStrings failed: %v", err)
	}

	matches, err := hc.FindMatch()
	if err != nil {
		t.Fatalf("FindMatch failed: %v", err)
	}

	if len(matches) != 0 {
		t.Errorf("Expected no matches, got %d", len(matches))
	}
}

// Helper function to compare string slices
func compareStringSlices(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}

func TestGenerateSubsets(t *testing.T) {
	hc := NewHashCrafter("test.txt", " ,|", "sha256", "test", false)
	hc.strings = []string{"admin", "password", "123"}

	subsets := hc.generateSubsets(hc.strings)

	// For 3 strings, we expect 2^3 = 8 subsets
	expected := [][]string{
		{},                           // empty subset
		{"admin"},                    // single element
		{"password"},                 // single element
		{"admin", "password"},        // two elements
		{"123"},                      // single element
		{"admin", "123"},             // two elements
		{"password", "123"},          // two elements
		{"admin", "password", "123"}, // all elements
	}

	if len(subsets) != len(expected) {
		t.Errorf("Expected %d subsets, got %d", len(expected), len(subsets))
	}

	// Check that all expected subsets are present
	for _, expectedSubset := range expected {
		found := false
		for _, subset := range subsets {
			if compareStringSlices(subset, expectedSubset) {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("Expected subset %v not found", expectedSubset)
		}
	}
}
