package utils_test

import (
	"fmt"
	"os"
	"reflect"
	"testing"
	"tinyauth/internal/types"
	"tinyauth/internal/utils"
)

func TestParseUsers(t *testing.T) {
	t.Log("Testing parse users with a valid string")

	users := "user1:pass1,user2:pass2"
	expected := types.Users{
		{
			Username: "user1",
			Password: "pass1",
		},
		{
			Username: "user2",
			Password: "pass2",
		},
	}

	result, err := utils.ParseUsers(users)
	if err != nil {
		t.Fatalf("Error parsing users: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestGetUpperDomain(t *testing.T) {
	t.Log("Testing get upper domain with a valid url")

	url := "https://sub1.sub2.domain.com:8080"
	expected := "sub2.domain.com"

	result, err := utils.GetUpperDomain(url)
	if err != nil {
		t.Fatalf("Error getting root url: %v", err)
	}

	if expected != result {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestReadFile(t *testing.T) {
	t.Log("Creating a test file")

	err := os.WriteFile("/tmp/test.txt", []byte("test"), 0644)
	if err != nil {
		t.Fatalf("Error creating test file: %v", err)
	}

	t.Log("Testing read file with a valid file")

	data, err := utils.ReadFile("/tmp/test.txt")
	if err != nil {
		t.Fatalf("Error reading file: %v", err)
	}

	if data != "test" {
		t.Fatalf("Expected test, got %v", data)
	}

	t.Log("Cleaning up test file")

	err = os.Remove("/tmp/test.txt")
	if err != nil {
		t.Fatalf("Error cleaning up test file: %v", err)
	}
}

func TestParseFileToLine(t *testing.T) {
	t.Log("Testing parse file to line with a valid string")

	content := "\nuser1:pass1\nuser2:pass2\n"
	expected := "user1:pass1,user2:pass2"

	result := utils.ParseFileToLine(content)

	if expected != result {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestGetSecret(t *testing.T) {
	t.Log("Testing get secret with an empty config and file")

	conf := ""
	file := "/tmp/test.txt"
	expected := "test"

	err := os.WriteFile(file, []byte(fmt.Sprintf("\n\n    \n\n\n  %s   \n\n    \n  ", expected)), 0644)
	if err != nil {
		t.Fatalf("Error creating test file: %v", err)
	}

	result := utils.GetSecret(conf, file)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing get secret with an empty file and a valid config")

	result = utils.GetSecret(expected, "")

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing get secret with both a valid config and file")

	result = utils.GetSecret(expected, file)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Cleaning up test file")

	err = os.Remove(file)
	if err != nil {
		t.Fatalf("Error cleaning up test file: %v", err)
	}
}

func TestGetUsers(t *testing.T) {
	t.Log("Testing get users with a config and no file")

	conf := "user1:pass1,user2:pass2"
	file := ""
	expected := types.Users{
		{
			Username: "user1",
			Password: "pass1",
		},
		{
			Username: "user2",
			Password: "pass2",
		},
	}

	result, err := utils.GetUsers(conf, file)
	if err != nil {
		t.Fatalf("Error getting users: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing get users with a file and no config")

	conf = ""
	file = "/tmp/test.txt"
	expected = types.Users{
		{
			Username: "user1",
			Password: "pass1",
		},
		{
			Username: "user2",
			Password: "pass2",
		},
	}

	err = os.WriteFile(file, []byte("user1:pass1\nuser2:pass2"), 0644)
	if err != nil {
		t.Fatalf("Error creating test file: %v", err)
	}

	result, err = utils.GetUsers(conf, file)
	if err != nil {
		t.Fatalf("Error getting users: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing get users with both a config and file")

	conf = "user3:pass3"
	expected = types.Users{
		{
			Username: "user3",
			Password: "pass3",
		},
		{
			Username: "user1",
			Password: "pass1",
		},
		{
			Username: "user2",
			Password: "pass2",
		},
	}

	result, err = utils.GetUsers(conf, file)
	if err != nil {
		t.Fatalf("Error getting users: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Cleaning up test file")

	err = os.Remove(file)
	if err != nil {
		t.Fatalf("Error cleaning up test file: %v", err)
	}
}

func TestGetLabels(t *testing.T) {
	t.Log("Testing get labels with a valid map")

	labels := map[string]string{
		"tinyauth.users":           "user1,user2",
		"tinyauth.oauth.whitelist": "/regex/",
		"tinyauth.allowed":         "random",
		"tinyauth.headers":         "X-Header=value",
		"tinyauth.oauth.groups":    "group1,group2",
	}

	expected := types.Labels{
		Users:   "user1,user2",
		Allowed: "random",
		Headers: []string{"X-Header=value"},
		OAuth: types.OAuthLabels{
			Whitelist: "/regex/",
			Groups:    "group1,group2",
		},
	}

	result, err := utils.GetLabels(labels)
	if err != nil {
		t.Fatalf("Error getting labels: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestParseUser(t *testing.T) {
	t.Log("Testing parse user with a valid user")

	user := "user:pass:secret"
	expected := types.User{
		Username:   "user",
		Password:   "pass",
		TotpSecret: "secret",
	}

	result, err := utils.ParseUser(user)
	if err != nil {
		t.Fatalf("Error parsing user: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing parse user with an escaped user")

	user = "user:p$$ass$$:secret"
	expected = types.User{
		Username:   "user",
		Password:   "p$ass$",
		TotpSecret: "secret",
	}

	result, err = utils.ParseUser(user)
	if err != nil {
		t.Fatalf("Error parsing user: %v", err)
	}

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing parse user with an invalid user")

	user = "user::pass"

	_, err = utils.ParseUser(user)
	if err == nil {
		t.Fatalf("Expected error parsing user")
	}
}

func TestCheckFilter(t *testing.T) {
	t.Log("Testing check filter with a comma separated list")

	filter := "user1,user2,user3"
	str := "user1"
	expected := true

	result := utils.CheckFilter(filter, str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing check filter with a regex filter")

	filter = "/^user[0-9]+$/"
	str = "user1"
	expected = true

	result = utils.CheckFilter(filter, str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing check filter with an empty filter")

	filter = ""
	str = "user1"
	expected = true

	result = utils.CheckFilter(filter, str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing check filter with an invalid regex filter")

	filter = "/^user[0-9+$/"
	str = "user1"
	expected = false

	result = utils.CheckFilter(filter, str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing check filter with a non matching list")

	filter = "user1,user2,user3"
	str = "user4"
	expected = false

	result = utils.CheckFilter(filter, str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestSanitizeHeader(t *testing.T) {
	t.Log("Testing sanitize header with a valid string")

	str := "X-Header=value"
	expected := "X-Header=value"

	result := utils.SanitizeHeader(str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing sanitize header with an invalid string")

	str = "X-Header=val\nue"
	expected = "X-Header=value"

	result = utils.SanitizeHeader(str)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestParseHeaders(t *testing.T) {
	t.Log("Testing parse headers with a valid string")

	headers := []string{"X-Hea\x00der1=value1", "X-Header2=value\n2"}
	expected := map[string]string{
		"X-Header1": "value1",
		"X-Header2": "value2",
	}

	result := utils.ParseHeaders(headers)

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing parse headers with an invalid string")

	headers = []string{"X-Header1=", "X-Header2", "=value", "X-Header3=value3"}
	expected = map[string]string{"X-Header3": "value3"}

	result = utils.ParseHeaders(headers)

	if !reflect.DeepEqual(expected, result) {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestParseSecretFile(t *testing.T) {
	t.Log("Testing parse secret file with a valid file")

	content := "\n\n    \n\n\n  secret   \n\n    \n  "
	expected := "secret"

	result := utils.ParseSecretFile(content)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestFilterIP(t *testing.T) {
	t.Log("Testing filter IP with an IP and a valid CIDR")

	ip := "10.10.10.10"
	filter := "10.10.10.0/24"
	expected := true

	result, err := utils.FilterIP(filter, ip)
	if err != nil {
		t.Fatalf("Error filtering IP: %v", err)
	}

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing filter IP with an IP and a valid IP")

	filter = "10.10.10.10"
	expected = true

	result, err = utils.FilterIP(filter, ip)
	if err != nil {
		t.Fatalf("Error filtering IP: %v", err)
	}

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing filter IP with an IP and an non matching CIDR")

	filter = "10.10.15.0/24"
	expected = false

	result, err = utils.FilterIP(filter, ip)
	if err != nil {
		t.Fatalf("Error filtering IP: %v", err)
	}

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing filter IP with a non matching IP and a valid CIDR")

	filter = "10.10.10.11"
	expected = false

	result, err = utils.FilterIP(filter, ip)

	if err != nil {
		t.Fatalf("Error filtering IP: %v", err)
	}

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing filter IP with an IP and an invalid CIDR")

	filter = "10.../83"

	_, err = utils.FilterIP(filter, ip)
	if err == nil {
		t.Fatalf("Expected error filtering IP")
	}
}

func TestDeriveKey(t *testing.T) {
	t.Log("Testing the derive key function")

	master := "master"
	info := "info"
	expected := "gdrdU/fXzclYjiSXRexEatVgV13qQmKl"

	result, err := utils.DeriveKey(master, info)

	if err != nil {
		t.Fatalf("Error deriving key: %v", err)
	}

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}

func TestCoalesceToString(t *testing.T) {
	t.Log("Testing coalesce to string with a string")

	value := any("test")
	expected := "test"

	result := utils.CoalesceToString(value)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing coalesce to string with a slice of strings")

	value = []any{any("test1"), any("test2"), any(123)}
	expected = "test1,test2"

	result = utils.CoalesceToString(value)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}

	t.Log("Testing coalesce to string with an unsupported type")

	value = 12345
	expected = ""

	result = utils.CoalesceToString(value)

	if result != expected {
		t.Fatalf("Expected %v, got %v", expected, result)
	}
}
