package config

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

func TestReadConfig(t *testing.T) {
	const defaultMailFrom = "root@localhost"
	const defaultMaxEvents = 10000
	const defaultMaxRecipients = 200

	t.Run("NonExistentConfigFile", func(t *testing.T) {
		config := readConfig("/nonexistent", "file.conf")
		if config.MailFrom != defaultMailFrom {
			t.Errorf("Expected default MailFrom value to be '%s', got '%s'", defaultMailFrom, config.MailFrom)
		}
	})

	t.Run("ValidConfigFile", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `# Test configuration file
send_reminders=false
smtp_server=mail.example.com # default
smtp_ssl=true
smtp_username=testuser   # change?
smtp_password=testpass
mail_from=test@example.com
max_events_per_user=5000
max_recipients_per_event=150
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		if config.SendReminders != false {
			t.Errorf("Expected SendReminders to be false, got %v", config.SendReminders)
		}
		if config.SmtpServer != "mail.example.com" {
			t.Errorf("Expected SmtpServer to be 'mail.example.com', got '%s'", config.SmtpServer)
		}
		if config.SmtpSsl != true {
			t.Errorf("Expected SmtpSsl to be true, got %v", config.SmtpSsl)
		}
		if config.SmtpUsername != "testuser" {
			t.Errorf("Expected SmtpUsername to be 'testuser', got '%s'", config.SmtpUsername)
		}
		if config.SmtpPassword != "testpass" {
			t.Errorf("Expected SmtpPassword to be 'testpass', got '%s'", config.SmtpPassword)
		}
		if config.MailFrom != "test@example.com" {
			t.Errorf("Expected MailFrom to be 'test@example.com', got '%s'", config.MailFrom)
		}
		if config.MaxEventsPerUser != 5000 {
			t.Errorf("Expected MaxEventsPerUser to be 5000, got %d", config.MaxEventsPerUser)
		}
		if config.MaxRecipientsPerEvent != 150 {
			t.Errorf("Expected MaxRecipientsPerEvent to be 150, got %d", config.MaxRecipientsPerEvent)
		}
	})

	t.Run("ValidConfigFileBooleans", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `# Test configuration file
send_reminders=no
smtp_ssl=yes
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		if config.SendReminders != false {
			t.Errorf("Expected SendReminders to be false, got %v", config.SendReminders)
		}
		if config.SmtpSsl != true {
			t.Errorf("Expected SmtpSsl to be true, got %v", config.SmtpSsl)
		}
	})

	t.Run("ConfigWithCommentsAndWhitespace", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `# This is a comment
  # Another comment
send_reminders = 1   
  smtp_server  =  localhost  
# More comments with # marks in it too
smtp_ssl=0
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		if config.SendReminders != true {
			t.Errorf("Expected SendReminders to be true, got %v", config.SendReminders)
		}
		if config.SmtpServer != "localhost" {
			t.Errorf("Expected SmtpServer to be 'localhost', got '%s'", config.SmtpServer)
		}
		if config.SmtpSsl != false {
			t.Errorf("Expected SmtpSsl to be false, got %v", config.SmtpSsl)
		}
		if config.MailFrom != defaultMailFrom {
			t.Errorf("Expected default MailFrom value to be '%s', got '%s'", defaultMailFrom, config.MailFrom)
		}
	})

	t.Run("InvalidBooleanValues", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `send_reminders=invalid_bool

smtp_ssl=not_a_boolean

smtp_server=valid.server.com
max_xml_size_mb=1000
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Boolean values should remain default due to invalid input
		if config.SendReminders != true {
			t.Errorf("Expected SendReminders to be true (default due to invalid input), got %v", config.SendReminders)
		}
		if config.SmtpSsl != false {
			t.Errorf("Expected SmtpSsl to be false (default due to invalid input), got %v", config.SmtpSsl)
		}

		// Valid string value should be set
		if config.SmtpServer != "valid.server.com" {
			t.Errorf("Expected SmtpServer to be 'valid.server.com', got '%s'", config.SmtpServer)
		}

		// Valid integer value should be set
		const max_xml_bytes = 1_000 * 1024 * 1024
		if config.MaxXMLSizeBytes != max_xml_bytes {
			t.Errorf("Expected MaxXMLSizeBytes to be %d bytes, got %d bytes", max_xml_bytes, config.MaxXMLSizeBytes)
		}

	})

	t.Run("InvalidLineFormat", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `smtp_server=valid.server.com
invalid_line_without_equal_sign
smtp_ssl==1
mail_from=test@example.com
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Should process the first valid line but stop at invalid line
		if config.SmtpServer != "valid.server.com" {
			t.Errorf("Expected SmtpServer to be 'valid.server.com', got '%s'", config.SmtpServer)
		}

		if config.SmtpSsl != false {
			t.Errorf("Expected SmtpSsl to be false (default due to invalid input), got %v", config.SmtpSsl)
		}

		// The line after the invalid one should still be processed
		if config.MailFrom != "test@example.com" {
			t.Errorf("Expected MailFrom to be 'test@example.com', got '%s'", config.MailFrom)
		}
	})

	t.Run("InvalidSmtpHostname", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		// Get the default hostname for comparison
		hostname, err := os.Hostname()
		if err != nil {
			hostname = "localhost"
		}

		configContent := `smtp_server=.invalid.hostname
smtp_ssl=true
mail_from=test@localhost
`
		err = os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Invalid SMTP hostname should be rejected and default value kept
		if config.SmtpServer != hostname {
			t.Errorf("Expected SmtpServer to be '%s' (default due to invalid hostname), got '%s'", hostname, config.SmtpServer)
		}

		// Other valid values should still be set
		if config.SmtpSsl != true {
			t.Errorf("Expected SmtpSsl to be true, got %v", config.SmtpSsl)
		}
		if config.MailFrom != "test@localhost" {
			t.Errorf("Expected MailFrom to be 'test@localhost', got '%s'", config.MailFrom)
		}
	})

	t.Run("InvalidMailFrom", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `mail_from=user@@example.com
max_events_per_user=-10
max_recipients_per_event=100000
max_xml_size_mb=0
smtp_server=mail.example.com
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Invalid mail_from should be rejected, default should be used
		if config.MailFrom != defaultMailFrom {
			t.Errorf("Expected MailFrom to be default '%s', got '%s'", defaultMailFrom, config.MailFrom)
		}

		// Invalid max_events_per_user should be rejected, default should be used
		if config.MaxEventsPerUser != defaultMaxEvents {
			t.Errorf("Expected MaxEventsPerUser to be default %d, got '%d'", defaultMaxEvents, config.MaxEventsPerUser)
		}

		// Invalid max_recipients_per_event should be rejected, default should be used
		if config.MaxRecipientsPerEvent != defaultMaxRecipients {
			t.Errorf("Expected MaxRecipientsPerEvent to be default %d, got %d", defaultMaxRecipients, config.MaxRecipientsPerEvent)
		}

		// Invalid max_xml_size_mb should be rejected, default should be used
		const defaultMaxXML = 50 * 1024 * 1024
		if config.MaxXMLSizeBytes != defaultMaxXML {
			t.Errorf("Expected MaxXMLSizeBytes to be default %d, got %d", defaultMaxXML, config.MaxXMLSizeBytes)
		}

		// Other valid values should still be set
		if config.SmtpServer != "mail.example.com" {
			t.Errorf("Expected SmtpServer to be 'mail.example.com', got '%s'", config.SmtpServer)
		}
	})

	t.Run("MailFromWithDisplayName", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		// Try to use email with display name format
		configContent := `mail_from=John Doe <john@example.com>
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Display name format should be rejected, default should be used
		if config.MailFrom != defaultMailFrom {
			t.Errorf("Expected MailFrom to be default '%s' due to display name format, got '%s'", defaultMailFrom, config.MailFrom)
		}
	})

	t.Run("EmptyConfigFile", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		err := os.WriteFile(configFile, []byte(""), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// All values should be defaults
		if config.SendReminders != true {
			t.Errorf("Expected SendReminders to be true, got %v", config.SendReminders)
		}
		if config.MailFrom != defaultMailFrom {
			t.Errorf("Expected MailFrom to be '%s', got '%s'", defaultMailFrom, config.MailFrom)
		}
	})

	t.Run("OnlyCommentsConfigFile", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		configContent := `# This is a comment
# Another comment
# Yet another comment
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// All values should be defaults
		if config.SendReminders != true {
			t.Errorf("Expected SendReminders to be true, got %v", config.SendReminders)
		}
		if config.MailFrom != defaultMailFrom {
			t.Errorf("Expected MailFrom to be '%s', got '%s'", defaultMailFrom, config.MailFrom)
		}
	})

	t.Run("SmtpUsernameTooLong", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		longUsername := string(make([]byte, 129)) // 129 characters
		for i := range longUsername {
			longUsername = string([]byte{byte('a' + (i % 26))}) + longUsername[1:]
		}

		configContent := `smtp_username=` + longUsername + `
smtp_password=validpass
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Username that's too long should be rejected, default (empty string) should be used
		if config.SmtpUsername != "" {
			t.Errorf("Expected SmtpUsername to be empty (default) due to excessive length, got '%s'", config.SmtpUsername)
		}

		// Other valid values should still be set
		if config.SmtpPassword != "validpass" {
			t.Errorf("Expected SmtpPassword to be 'validpass', got '%s'", config.SmtpPassword)
		}
	})

	t.Run("SmtpPasswordTooLong", func(t *testing.T) {
		tmpDir := t.TempDir()
		configFile := filepath.Join(tmpDir, "test.conf")

		longPassword := string(make([]byte, 129)) // 129 characters long
		for i := range longPassword {
			longPassword = string([]byte{byte('A' + (i % 26))}) + longPassword[1:]
		}

		configContent := `smtp_username=validuser
smtp_password=` + longPassword + `
`
		err := os.WriteFile(configFile, []byte(configContent), 0644)
		if err != nil {
			t.Fatalf("Failed to create test config file: %v", err)
		}

		config := readConfig(tmpDir, "test.conf")

		// Password that's too long should be rejected, default (empty string) should be used
		if config.SmtpPassword != "" {
			t.Errorf("Expected SmtpPassword to be empty (default) due to excessive length, got '%s'", config.SmtpPassword)
		}

		// Other valid values should still be set
		if config.SmtpUsername != "validuser" {
			t.Errorf("Expected SmtpUsername to be 'validuser', got '%s'", config.SmtpUsername)
		}
	})
}

func TestGetUserConfigFilePath(t *testing.T) {
	t.Run("NoConfigFiles", func(t *testing.T) {
		tmpDir := t.TempDir()
		path := GetUserConfigFilename(tmpDir)

		if path != "" {
			t.Errorf("Expected path to be empty, got '%s'", path)
		}
	})

	t.Run("OnlyLegacyFile", func(t *testing.T) {
		tmpDir := t.TempDir()
		legacyPath := filepath.Join(tmpDir, LegacyUserConfigFile)

		if err := os.WriteFile(legacyPath, []byte("legacy content"), 0644); err != nil {
			t.Fatalf("Failed to create legacy config file: %v", err)
		}

		filename := GetUserConfigFilename(tmpDir)
		if filename != LegacyUserConfigFile {
			t.Errorf("Expected path to be '%s', got '%s'", LegacyUserConfigFile, filename)
		}
	})

	t.Run("OnlyPreferredFile", func(t *testing.T) {
		tmpDir := t.TempDir()
		if err := os.MkdirAll(filepath.Join(tmpDir, UserConfigDir), 0755); err != nil {
			t.Fatalf("Failed to create preferred config directory: %v", err)
		}

		preferredFilename := filepath.Join(UserConfigDir, UserConfigFile)
		if err := os.WriteFile(filepath.Join(tmpDir, preferredFilename), []byte("preferred content"), 0644); err != nil {
			t.Fatalf("Failed to create preferred config file: %v", err)
		}

		filename := GetUserConfigFilename(tmpDir)

		if filename != preferredFilename {
			t.Errorf("Expected path to be '%s', got '%s'", preferredFilename, filename)
		}
	})

	t.Run("BothFilesExist", func(t *testing.T) {
		tmpDir := t.TempDir()

		// Create legacy file
		legacyPath := filepath.Join(tmpDir, LegacyUserConfigFile)
		if err := os.WriteFile(legacyPath, []byte("legacy content"), 0644); err != nil {
			t.Fatalf("Failed to create legacy config file: %v", err)
		}

		// Create preferred file
		if err := os.MkdirAll(filepath.Join(tmpDir, UserConfigDir), 0755); err != nil {
			t.Fatalf("Failed to create preferred config directory: %v", err)
		}

		preferredFilename := filepath.Join(UserConfigDir, UserConfigFile)
		if err := os.WriteFile(filepath.Join(tmpDir, preferredFilename), []byte("preferred content"), 0644); err != nil {
			t.Fatalf("Failed to create preferred config file: %v", err)
		}

		filename := GetUserConfigFilename(tmpDir)

		// Should prefer the new config file over the legacy one
		if filename != preferredFilename {
			t.Errorf("Expected path to be '%s' (preferred), got '%s'", preferredFilename, filename)
		}
	})
}
