package services

import (
	"net"
	"testing"

	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
)

// TestCoverageBoost_ErrorPaths tests various error handling paths to increase coverage
func TestCoverageBoost_ErrorPaths(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	// Migrate all tables
	err = db.AutoMigrate(
		&models.ProxyHost{},
		&models.RemoteServer{},
		&models.SecurityConfig{},
		&models.SecurityRuleSet{},
		&models.NotificationTemplate{},
		&models.Setting{},
	)
	require.NoError(t, err)

	t.Run("ProxyHostService_GetByUUID_Error", func(t *testing.T) {
		svc := NewProxyHostService(db)

		// Test with non-existent UUID
		_, err := svc.GetByUUID("non-existent-uuid")
		assert.Error(t, err)
	})

	t.Run("ProxyHostService_List_WithValidDB", func(t *testing.T) {
		svc := NewProxyHostService(db)

		// Should not error even with empty db
		hosts, err := svc.List()
		assert.NoError(t, err)
		assert.NotNil(t, hosts)
	})

	t.Run("RemoteServerService_GetByUUID_Error", func(t *testing.T) {
		svc := NewRemoteServerService(db)

		// Test with non-existent UUID
		_, err := svc.GetByUUID("non-existent-uuid")
		assert.Error(t, err)
	})

	t.Run("RemoteServerService_List_WithValidDB", func(t *testing.T) {
		svc := NewRemoteServerService(db)

		// Should not error with empty db
		servers, err := svc.List(false)
		assert.NoError(t, err)
		assert.NotNil(t, servers)
	})

	t.Run("SecurityService_Get_NotFound", func(t *testing.T) {
		svc := NewSecurityService(db)

		// No config exists yet
		_, err := svc.Get()
		assert.ErrorIs(t, err, ErrSecurityConfigNotFound)
	})

	t.Run("SecurityService_ListRuleSets_EmptyDB", func(t *testing.T) {
		svc := NewSecurityService(db)

		// Should not error with empty db
		rulesets, err := svc.ListRuleSets()
		assert.NoError(t, err)
		assert.NotNil(t, rulesets)
		assert.Empty(t, rulesets)
	})

	t.Run("SecurityService_DeleteRuleSet_NotFound", func(t *testing.T) {
		svc := NewSecurityService(db)

		// Test with non-existent ID
		err := svc.DeleteRuleSet(999)
		assert.Error(t, err)
	})

	t.Run("SecurityService_VerifyBreakGlass_MissingConfig", func(t *testing.T) {
		svc := NewSecurityService(db)

		// No config exists
		valid, err := svc.VerifyBreakGlassToken("default", "anytoken")
		assert.Error(t, err)
		assert.False(t, valid)
	})

	t.Run("SecurityService_GenerateBreakGlassToken_Success", func(t *testing.T) {
		svc := NewSecurityService(db)

		// Generate token
		token, err := svc.GenerateBreakGlassToken("test-config")
		assert.NoError(t, err)
		assert.NotEmpty(t, token)

		// Verify it was created
		var cfg models.SecurityConfig
		err = db.Where("name = ?", "test-config").First(&cfg).Error
		assert.NoError(t, err)
		assert.NotEmpty(t, cfg.BreakGlassHash)
	})

	t.Run("NotificationService_ListTemplates_EmptyDB", func(t *testing.T) {
		svc := NewNotificationService(db)

		// Should not error with empty db
		templates, err := svc.ListTemplates()
		assert.NoError(t, err)
		assert.NotNil(t, templates)
		assert.Empty(t, templates)
	})

	t.Run("NotificationService_GetTemplate_NotFound", func(t *testing.T) {
		svc := NewNotificationService(db)

		// Test with non-existent ID
		_, err := svc.GetTemplate("nonexistent")
		assert.Error(t, err)
	})
}

// TestCoverageBoost_SecurityService_AdditionalPaths tests more security service paths
func TestCoverageBoost_SecurityService_AdditionalPaths(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityRuleSet{})
	require.NoError(t, err)

	svc := NewSecurityService(db)

	t.Run("Upsert_Create", func(t *testing.T) {
		// Create initial config
		cfg := &models.SecurityConfig{
			Name:         "default",
			CrowdSecMode: "local",
		}
		err := svc.Upsert(cfg)
		require.NoError(t, err)
	})

	t.Run("UpsertRuleSet_Create", func(t *testing.T) {
		ruleset := &models.SecurityRuleSet{
			Name:      "test-ruleset-new",
			SourceURL: "https://example.com",
		}
		err := svc.UpsertRuleSet(ruleset)
		assert.NoError(t, err)

		// Verify created
		var found models.SecurityRuleSet
		err = db.Where("name = ?", "test-ruleset-new").First(&found).Error
		assert.NoError(t, err)
	})
}

// TestCoverageBoost_MinInt tests the minInt helper
func TestCoverageBoost_MinInt(t *testing.T) {
	t.Run("minInt_FirstSmaller", func(t *testing.T) {
		result := minInt(5, 10)
		assert.Equal(t, 5, result)
	})

	t.Run("minInt_SecondSmaller", func(t *testing.T) {
		result := minInt(10, 5)
		assert.Equal(t, 5, result)
	})

	t.Run("minInt_Equal", func(t *testing.T) {
		result := minInt(5, 5)
		assert.Equal(t, 5, result)
	})
}

// TestCoverageBoost_MailService_ErrorPaths tests mail service error handling
func TestCoverageBoost_MailService_ErrorPaths(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.Setting{})
	require.NoError(t, err)

	svc := NewMailService(db)

	t.Run("GetSMTPConfig_EmptyDB", func(t *testing.T) {
		// Empty DB should return config with defaults
		config, err := svc.GetSMTPConfig()
		assert.NoError(t, err)
		assert.NotNil(t, config)
	})

	t.Run("IsConfigured_NoConfig", func(t *testing.T) {
		// With empty DB, should return false
		configured := svc.IsConfigured()
		assert.False(t, configured)
	})

	t.Run("TestConnection_NoConfig", func(t *testing.T) {
		// With empty config, should error
		err := svc.TestConnection()
		assert.Error(t, err)
	})

	t.Run("SendEmail_NoConfig", func(t *testing.T) {
		// With empty config, should error
		err := svc.SendEmail("test@example.com", "Subject", "Body")
		assert.Error(t, err)
	})
}

// TestCoverageBoost_AccessListService_Paths tests access list error paths
func TestCoverageBoost_AccessListService_Paths(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.AccessList{})
	require.NoError(t, err)

	svc := NewAccessListService(db)

	t.Run("GetByID_NotFound", func(t *testing.T) {
		_, err := svc.GetByID(999)
		assert.ErrorIs(t, err, ErrAccessListNotFound)
	})

	t.Run("GetByUUID_NotFound", func(t *testing.T) {
		_, err := svc.GetByUUID("nonexistent-uuid")
		assert.ErrorIs(t, err, ErrAccessListNotFound)
	})

	t.Run("List_EmptyDB", func(t *testing.T) {
		// Should not error with empty db
		lists, err := svc.List()
		assert.NoError(t, err)
		assert.NotNil(t, lists)
		assert.Empty(t, lists)
	})
}

// TestCoverageBoost_HelperFunctions tests utility helper functions
func TestCoverageBoost_HelperFunctions(t *testing.T) {
	t.Run("extractPort_HTTP", func(t *testing.T) {
		port := extractPort("http://example.com:8080/path")
		assert.Equal(t, "8080", port)
	})

	t.Run("extractPort_HTTPS", func(t *testing.T) {
		port := extractPort("https://example.com:443")
		assert.Equal(t, "443", port)
	})

	t.Run("extractPort_Invalid", func(t *testing.T) {
		port := extractPort("not-a-url")
		assert.Equal(t, "", port)
	})

	t.Run("hasHeader_Found", func(t *testing.T) {
		headers := map[string][]string{
			"X-Test-Header": {"value1", "value2"},
			"Content-Type":  {"application/json"},
		}
		assert.True(t, hasHeader(headers, "X-Test-Header"))
		assert.True(t, hasHeader(headers, "Content-Type"))
	})

	t.Run("hasHeader_NotFound", func(t *testing.T) {
		headers := map[string][]string{
			"X-Test-Header": {"value1"},
		}
		assert.False(t, hasHeader(headers, "X-Missing-Header"))
	})

	t.Run("hasHeader_EmptyMap", func(t *testing.T) {
		headers := map[string][]string{}
		assert.False(t, hasHeader(headers, "Any-Header"))
	})

	t.Run("isPrivateIP_PrivateRanges", func(t *testing.T) {
		assert.True(t, isPrivateIP(net.ParseIP("192.168.1.1")))
		assert.True(t, isPrivateIP(net.ParseIP("10.0.0.1")))
		assert.True(t, isPrivateIP(net.ParseIP("172.16.0.1")))
		assert.True(t, isPrivateIP(net.ParseIP("127.0.0.1")))
	})

	t.Run("isPrivateIP_PublicIP", func(t *testing.T) {
		assert.False(t, isPrivateIP(net.ParseIP("8.8.8.8")))
		assert.False(t, isPrivateIP(net.ParseIP("1.1.1.1")))
	})
}

// TestCoverageBoost_ProxyHostService_DB tests DB accessor
func TestCoverageBoost_ProxyHostService_DB(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	svc := NewProxyHostService(db)

	t.Run("DB_ReturnsValidDB", func(t *testing.T) {
		dbInstance := svc.DB()
		assert.NotNil(t, dbInstance)
		assert.Equal(t, db, dbInstance)
	})
}

// TestCoverageBoost_DNSProviderService_SupportedTypes tests provider type queries
func TestCoverageBoost_DNSProviderService_SupportedTypes(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.DNSProvider{})
	require.NoError(t, err)

	svc := NewDNSProviderService(db, nil)

	t.Run("GetSupportedProviderTypes", func(t *testing.T) {
		types := svc.GetSupportedProviderTypes()
		assert.NotNil(t, types)
		// Should include at least some built-in types
		assert.NotEmpty(t, types)
	})

	t.Run("GetProviderCredentialFields_ValidProvider", func(t *testing.T) {
		types := svc.GetSupportedProviderTypes()
		if len(types) > 0 {
			// Test with first available provider
			fields, err := svc.GetProviderCredentialFields(types[0])
			assert.NoError(t, err)
			assert.NotNil(t, fields)
		}
	})

	t.Run("GetProviderCredentialFields_InvalidProvider", func(t *testing.T) {
		fields, err := svc.GetProviderCredentialFields("invalid-provider-type-12345")
		assert.Error(t, err)
		assert.Nil(t, fields)
		assert.Contains(t, err.Error(), "unsupported provider type")
	})
}

// TestCoverageBoost_SecurityService_Close tests service cleanup
func TestCoverageBoost_SecurityService_Close(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	svc := NewSecurityService(db)

	t.Run("Close_Success", func(t *testing.T) {
		svc.Close()
		// Close doesn't return error, just ensure it doesn't panic
	})

	t.Run("Flush_Success", func(t *testing.T) {
		svc.Flush()
		// Flush doesn't return error, just ensure it doesn't panic
	})
}

// TestCoverageBoost_BackupService_GetAvailableSpace tests disk space checking
func TestCoverageBoost_BackupService_GetAvailableSpace(t *testing.T) {
	// Skip these tests as they require full config setup
	t.Skip("BackupService requires full config.Config, tested elsewhere")
}

// TestCoverageBoost_CertificateService_ListCertificates tests certificate listing with errors
func TestCoverageBoost_CertificateService_ListCertificates(t *testing.T) {
	// Skip these tests as they require proper model imports
	t.Skip("Certificate models tested in certificate_service_test.go")
}

// TestCoverageBoost_MailService_SendSSL tests SSL mail sending error paths
func TestCoverageBoost_MailService_SendSSL(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.Setting{})
	require.NoError(t, err)

	svc := NewMailService(db)

	t.Run("SendEmail_SSL_InvalidHost", func(t *testing.T) {
		// Save invalid config
		config := &SMTPConfig{
			Host:        "invalid-mail-server-12345.example.com",
			Port:        465,
			Username:    "test",
			Password:    "test",
			FromAddress: "test@example.com",
			Encryption:  "ssl",
		}
		err := svc.SaveSMTPConfig(config)
		require.NoError(t, err)

		// Try to send - should fail with connection error
		err = svc.SendEmail("test@example.com", "Test", "Body")
		assert.Error(t, err)
	})

	t.Run("SendEmail_STARTTLS_InvalidHost", func(t *testing.T) {
		// Save invalid config with STARTTLS
		config := &SMTPConfig{
			Host:        "invalid-mail-server-12345.example.com",
			Port:        587,
			Username:    "test",
			Password:    "test",
			FromAddress: "test@example.com",
			Encryption:  "starttls",
		}
		err := svc.SaveSMTPConfig(config)
		require.NoError(t, err)

		// Try to send - should fail with connection error
		err = svc.SendEmail("test@example.com", "Test", "Body")
		assert.Error(t, err)
	})
}

// TestCoverageBoost_CredentialService_ErrorPaths tests credential service error handling
func TestCoverageBoost_CredentialService_ErrorPaths(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.DNSProvider{}, &models.DNSProviderCredential{})
	require.NoError(t, err)

	// Note: CredentialService requires crypto.EncryptionService, tested in credential_service_test.go
	t.Skip("CredentialService requires crypto.EncryptionService, tested elsewhere")
}

// TestCoverageBoost_GeoIPService_ErrorPaths tests GeoIP service error handling
func TestCoverageBoost_GeoIPService_ErrorPaths(t *testing.T) {
	t.Run("NewGeoIPService_InvalidPath", func(t *testing.T) {
		svc, err := NewGeoIPService("/nonexistent/path/to/geoip.mmdb")
		assert.Error(t, err)
		assert.Nil(t, svc)
	})
}

// TestCoverageBoost_DockerService_ErrorPaths tests Docker service error handling
func TestCoverageBoost_DockerService_ErrorPaths(t *testing.T) {
	t.Skip("Docker service tests require specific setup, tested in docker_service_test.go")
}

// TestCoverageBoost_UptimeService_FlushNotifications tests notification flushing
func TestCoverageBoost_UptimeService_FlushNotifications(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.UptimeMonitor{}, &models.UptimeHost{})
	require.NoError(t, err)

	svc := NewUptimeService(db, nil)

	t.Run("FlushPendingNotifications", func(t *testing.T) {
		// Should not error even with empty pending notifications
		svc.FlushPendingNotifications()
	})
}

// TestCoverageBoost_LogService_NewLogService tests log service creation
func TestCoverageBoost_LogService_NewLogService(t *testing.T) {
	t.Skip("LogService requires full config, tested in log_service_test.go")
}

// TestCoverageBoost_UpdateService_ClearCache tests cache clearing
func TestCoverageBoost_UpdateService_ClearCache(t *testing.T) {
	svc := NewUpdateService()

	t.Run("ClearCache", func(t *testing.T) {
		svc.ClearCache()
	})

	t.Run("SetCurrentVersion", func(t *testing.T) {
		svc.SetCurrentVersion("v1.2.3")
	})
}

// TestCoverageBoost_NotificationService_Providers tests provider management
func TestCoverageBoost_NotificationService_Providers(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.NotificationProvider{})
	require.NoError(t, err)

	svc := NewNotificationService(db)

	t.Run("ListProviders_EmptyDB", func(t *testing.T) {
		providers, err := svc.ListProviders()
		assert.NoError(t, err)
		assert.NotNil(t, providers)
		assert.Empty(t, providers)
	})

	t.Run("CreateProvider", func(t *testing.T) {
		provider := &models.NotificationProvider{
			Name:    "test-provider",
			Type:    "webhook",
			Enabled: true,
			Config:  `{"url": "https://example.com/hook"}`,
		}
		err := svc.CreateProvider(provider)
		assert.NoError(t, err)
		assert.NotZero(t, provider.ID)
	})

	t.Run("UpdateProvider", func(t *testing.T) {
		// Create a provider first
		provider := &models.NotificationProvider{
			Name:    "update-test",
			Type:    "webhook",
			Enabled: true,
			Config:  `{"url": "https://example.com/hook"}`,
		}
		err := svc.CreateProvider(provider)
		require.NoError(t, err)

		// Update it
		provider.Name = "updated-name"
		err = svc.UpdateProvider(provider)
		assert.NoError(t, err)
	})

	t.Run("DeleteProvider", func(t *testing.T) {
		// Create a provider first
		provider := &models.NotificationProvider{
			Name:    "delete-test",
			Type:    "webhook",
			Enabled: true,
			Config:  `{"url": "https://example.com/hook"}`,
		}
		err := svc.CreateProvider(provider)
		require.NoError(t, err)

		// Delete it
		err = svc.DeleteProvider(provider.ID)
		assert.NoError(t, err)
	})
}

// TestCoverageBoost_NotificationService_CRUD tests notification CRUD operations
func TestCoverageBoost_NotificationService_CRUD(t *testing.T) {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
		Logger: gormlogger.Default.LogMode(gormlogger.Silent),
	})
	require.NoError(t, err)

	err = db.AutoMigrate(&models.Notification{})
	require.NoError(t, err)

	svc := NewNotificationService(db)

	t.Run("List_EmptyDB", func(t *testing.T) {
		notifs, err := svc.List(false)
		assert.NoError(t, err)
		assert.NotNil(t, notifs)
	})

	t.Run("MarkAllAsRead_Success", func(t *testing.T) {
		err := svc.MarkAllAsRead()
		assert.NoError(t, err)
	})
}
