package caddy

import (
	"os"
	"testing"
	"time"

	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/Wikid82/charon/backend/pkg/dnsprovider"
	"github.com/stretchr/testify/require"

	_ "github.com/Wikid82/charon/backend/pkg/dnsprovider/builtin" // Auto-register DNS providers
)

type multiCredTestProvider struct{}

func (p *multiCredTestProvider) Type() string { return "testmulti" }
func (p *multiCredTestProvider) Metadata() dnsprovider.ProviderMetadata {
	return dnsprovider.ProviderMetadata{Type: p.Type(), Name: "Test Multi", IsBuiltIn: true}
}
func (p *multiCredTestProvider) Init() error    { return nil }
func (p *multiCredTestProvider) Cleanup() error { return nil }
func (p *multiCredTestProvider) RequiredCredentialFields() []dnsprovider.CredentialFieldSpec {
	return nil
}
func (p *multiCredTestProvider) OptionalCredentialFields() []dnsprovider.CredentialFieldSpec {
	return nil
}
func (p *multiCredTestProvider) ValidateCredentials(creds map[string]string) error { return nil }
func (p *multiCredTestProvider) TestCredentials(creds map[string]string) error     { return nil }
func (p *multiCredTestProvider) SupportsMultiCredential() bool                     { return true }
func (p *multiCredTestProvider) BuildCaddyConfig(creds map[string]string) map[string]any {
	return map[string]any{"name": p.Type(), "token": creds["token"]}
}
func (p *multiCredTestProvider) BuildCaddyConfigForZone(baseDomain string, creds map[string]string) map[string]any {
	return map[string]any{"name": p.Type(), "zone": baseDomain, "token": creds["token"]}
}
func (p *multiCredTestProvider) PropagationTimeout() time.Duration { return 2 * time.Second }
func (p *multiCredTestProvider) PollingInterval() time.Duration    { return 1 * time.Second }

func mustProviderID(v uint) *uint { return &v }

func TestGenerateConfig_DNSChallenge_LetsEncrypt_StagingCAAndPropagationTimeout(t *testing.T) {
	providerID := uint(1)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.example.com,example.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		true,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                 providerID,
			ProviderType:       "cloudflare",
			PropagationTimeout: 120,
			Credentials:        map[string]string{"api_token": "tok"},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotNil(t, conf.Apps.TLS.Automation)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	// Find a policy that includes the wildcard subject
	var foundIssuer map[string]any
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, s := range p.Subjects {
			if s != "*.example.com" {
				continue
			}
			require.NotEmpty(t, p.IssuersRaw)
			for _, it := range p.IssuersRaw {
				if m, ok := it.(map[string]any); ok {
					if m["module"] == "acme" {
						foundIssuer = m
						break
					}
				}
			}
		}
		if foundIssuer != nil {
			break
		}
	}

	require.NotNil(t, foundIssuer)
	require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", foundIssuer["ca"])

	challenges, ok := foundIssuer["challenges"].(map[string]any)
	require.True(t, ok)
	dns, ok := challenges["dns"].(map[string]any)
	require.True(t, ok)
	require.Equal(t, int64(120)*1_000_000_000, dns["propagation_timeout"])
}

func TestGenerateConfig_DNSChallenge_ZeroSSL_IssuerShape(t *testing.T) {
	providerID := uint(2)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.example.net",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"zerossl",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                 providerID,
			ProviderType:       "cloudflare",
			PropagationTimeout: 5,
			Credentials:        map[string]string{"api_token": "tok"},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	// Expect at least one issuer with module zerossl
	found := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			if m, ok := it.(map[string]any); ok {
				if m["module"] == "zerossl" {
					found = true
				}
			}
		}
	}
	require.True(t, found)
}

func TestGenerateConfig_DNSChallenge_SkipsPolicyWhenProviderConfigMissing(t *testing.T) {
	providerID := uint(3)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.example.org",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		nil, // no provider configs available
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	// No policy should include the wildcard subject since provider config was missing
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, s := range p.Subjects {
			require.NotEqual(t, "*.example.org", s)
		}
	}
}

func TestGenerateConfig_HTTPChallenge_ExcludesIPDomains(t *testing.T) {
	host := models.ProxyHost{Enabled: true, DomainNames: "example.com,192.168.1.1"}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		nil,
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, s := range p.Subjects {
			require.NotEqual(t, "192.168.1.1", s)
		}
	}
}

func TestGetCrowdSecAPIKey_EnvPriority(t *testing.T) {
	_ = os.Unsetenv("CROWDSEC_API_KEY")
	_ = os.Unsetenv("CROWDSEC_BOUNCER_API_KEY")

	t.Setenv("CROWDSEC_BOUNCER_API_KEY", "bouncer")
	t.Setenv("CROWDSEC_API_KEY", "primary")
	require.Equal(t, "primary", getCrowdSecAPIKey())

	_ = os.Unsetenv("CROWDSEC_API_KEY")
	require.Equal(t, "bouncer", getCrowdSecAPIKey())
}

func TestHasWildcard_TrueFalse(t *testing.T) {
	require.True(t, hasWildcard([]string{"*.example.com"}))
	require.False(t, hasWildcard([]string{"example.com"}))
}

// TestGenerateConfig_MultiCredential_ZoneSpecificPolicies verifies that multi-credential DNS providers
// create separate TLS automation policies per zone with zone-specific credentials.
func TestGenerateConfig_MultiCredential_ZoneSpecificPolicies(t *testing.T) {
	providerID := uint(10)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.zone1.com,zone1.com,*.zone2.com,zone2.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "cloudflare",
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"zone1.com": {"api_token": "token-zone1"},
				"zone2.com": {"api_token": "token-zone2"},
			},
			Credentials: map[string]string{"api_token": "fallback-token"},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	// Should have at least 2 policies for the 2 zones
	policyCount := 0
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, s := range p.Subjects {
			if s == "*.zone1.com" || s == "zone1.com" || s == "*.zone2.com" || s == "zone2.com" {
				policyCount++
				break
			}
		}
	}
	require.GreaterOrEqual(t, policyCount, 2, "expected at least 2 policies for multi-credential zones")
}

// TestGenerateConfig_MultiCredential_ZeroSSL_Issuer verifies multi-credential with ZeroSSL issuer.
func TestGenerateConfig_MultiCredential_ZeroSSL_Issuer(t *testing.T) {
	providerID := uint(11)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.zerossl-test.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"zerossl", // Use ZeroSSL provider
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "cloudflare",
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"zerossl-test.com": {"api_token": "zerossl-token"},
			},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)

	// Find ZeroSSL issuer in policies
	foundZeroSSL := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			if m, ok := it.(map[string]any); ok {
				if m["module"] == "zerossl" {
					foundZeroSSL = true
					break
				}
			}
		}
	}
	require.True(t, foundZeroSSL, "expected ZeroSSL issuer in multi-credential policy")
}

// TestGenerateConfig_MultiCredential_BothIssuers verifies multi-credential with both ACME and ZeroSSL issuers.
func TestGenerateConfig_MultiCredential_BothIssuers(t *testing.T) {
	providerID := uint(12)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.both-test.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"both", // Use both providers
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "cloudflare",
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"both-test.com": {"api_token": "both-token"},
			},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)

	// Find both ACME and ZeroSSL issuers in policies
	foundACME := false
	foundZeroSSL := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			if m, ok := it.(map[string]any); ok {
				switch m["module"] {
				case "acme":
					foundACME = true
				case "zerossl":
					foundZeroSSL = true
				}
			}
		}
	}
	require.True(t, foundACME, "expected ACME issuer in multi-credential policy")
	require.True(t, foundZeroSSL, "expected ZeroSSL issuer in multi-credential policy")
}

// TestGenerateConfig_MultiCredential_ACMEStaging verifies multi-credential with ACME staging CA.
func TestGenerateConfig_MultiCredential_ACMEStaging(t *testing.T) {
	providerID := uint(13)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.staging-test.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		true, // ACME staging
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "cloudflare",
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"staging-test.com": {"api_token": "staging-token"},
			},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)

	// Find ACME issuer with staging CA
	foundStagingCA := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			if m, ok := it.(map[string]any); ok {
				if m["module"] == "acme" {
					if ca, ok := m["ca"].(string); ok && ca == "https://acme-staging-v02.api.letsencrypt.org/directory" {
						foundStagingCA = true
						break
					}
				}
			}
		}
	}
	require.True(t, foundStagingCA, "expected ACME staging CA in multi-credential policy")
}

// TestGenerateConfig_MultiCredential_NoMatchingDomains verifies that zones with no matching domains are skipped.
func TestGenerateConfig_MultiCredential_NoMatchingDomains(t *testing.T) {
	providerID := uint(14)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.actual.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "cloudflare",
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"unmatched.com": {"api_token": "unmatched-token"}, // This zone won't match any domains
				"actual.com":    {"api_token": "actual-token"},    // This zone will match
			},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)

	// Should only have policy for actual.com, not unmatched.com
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, s := range p.Subjects {
			require.NotContains(t, s, "unmatched", "unmatched domain should not appear in policies")
		}
	}
}

// TestGenerateConfig_MultiCredential_ProviderTypeNotFound verifies graceful handling when provider type is not in registry.
func TestGenerateConfig_MultiCredential_ProviderTypeNotFound(t *testing.T) {
	providerID := uint(15)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.unknown-provider.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "nonexistent_provider"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "nonexistent_provider", // Not in registry
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"unknown-provider.com": {"api_token": "token"},
			},
		}},
	)
	// Should not error, just skip the provider
	require.NoError(t, err)
	require.NotNil(t, conf)
}

func TestGenerateConfig_MultiCredential_SupportsMultiCredential_UsesZoneConfigAndStagingBothIssuers(t *testing.T) {
	if err := dnsprovider.Global().Register(&multiCredTestProvider{}); err == nil {
		t.Cleanup(func() { dnsprovider.Global().Unregister("testmulti") })
	}

	providerID := uint(16)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.z1.com,z1.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "testmulti"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"both",
		true,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                  providerID,
			ProviderType:        "testmulti",
			UseMultiCredentials: true,
			ZoneCredentials: map[string]map[string]string{
				"z1.com": {"token": "tok-z1"},
			},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotNil(t, conf.Apps.TLS.Automation)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	var foundCA string
	var foundProvider map[string]any
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			issuer, ok := it.(map[string]any)
			if !ok || issuer["module"] != "acme" {
				continue
			}
			if ca, ok := issuer["ca"].(string); ok {
				foundCA = ca
			}
			ch, _ := issuer["challenges"].(map[string]any)
			dnsCh, _ := ch["dns"].(map[string]any)
			prov, _ := dnsCh["provider"].(map[string]any)
			foundProvider = prov
			break
		}
		if foundProvider != nil {
			break
		}
	}

	require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", foundCA)
	require.NotNil(t, foundProvider)
	require.Equal(t, "z1.com", foundProvider["zone"], "expected zone-specific provider config")
}

func TestGenerateConfig_DNSChallenge_SingleCredential_BothIssuers_ACMEStaging(t *testing.T) {
	providerID := uint(17)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.both-staging.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"both",
		true,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                 providerID,
			ProviderType:       "cloudflare",
			PropagationTimeout: 1,
			Credentials:        map[string]string{"api_token": "tok"},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)

	var foundIssuer map[string]any
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, s := range p.Subjects {
			if s != "*.both-staging.com" {
				continue
			}
			for _, it := range p.IssuersRaw {
				if m, ok := it.(map[string]any); ok && m["module"] == "acme" {
					foundIssuer = m
					break
				}
			}
		}
		if foundIssuer != nil {
			break
		}
	}

	require.NotNil(t, foundIssuer)
	require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", foundIssuer["ca"])
}

func TestGenerateConfig_DNSChallenge_SingleCredential_ProviderTypeNotFound_SkipsPolicy(t *testing.T) {
	providerID := uint(18)
	host := models.ProxyHost{
		Enabled:       true,
		DomainNames:   "*.missing-registry.com",
		DNSProvider:   &models.DNSProvider{ID: providerID, ProviderType: "not_registered"},
		DNSProviderID: mustProviderID(providerID),
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		[]DNSProviderConfig{{
			ID:                 providerID,
			ProviderType:       "not_registered",
			PropagationTimeout: 1,
			Credentials:        map[string]string{"token": "x"},
		}},
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
}

func TestGenerateConfig_DefaultPolicy_LetsEncrypt_StagingCA(t *testing.T) {
	host := models.ProxyHost{Enabled: true, DomainNames: "192.0.2.1"}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"letsencrypt",
		true,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		nil,
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotNil(t, conf.Apps.TLS.Automation)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)

	found := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			if m, ok := it.(map[string]any); ok && m["module"] == "acme" {
				require.Equal(t, "https://acme-staging-v02.api.letsencrypt.org/directory", m["ca"])
				found = true
				break
			}
		}
		if found {
			break
		}
	}
	require.True(t, found)
}

func TestGenerateConfig_DefaultPolicy_ZeroSSL_Issuer(t *testing.T) {
	host := models.ProxyHost{Enabled: true, DomainNames: "192.0.2.2"}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"zerossl",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		nil,
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotNil(t, conf.Apps.TLS.Automation)

	found := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			if m, ok := it.(map[string]any); ok && m["module"] == "zerossl" {
				found = true
				break
			}
		}
		if found {
			break
		}
	}
	require.True(t, found)
}

func TestGenerateConfig_DefaultPolicy_BothIssuers_StagingCA(t *testing.T) {
	host := models.ProxyHost{Enabled: true, DomainNames: "192.0.2.3"}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"acme@example.com",
		"",
		"",
		true,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		nil,
	)
	require.NoError(t, err)
	require.NotNil(t, conf)

	caFound := false
	zeroFound := false
	for _, p := range conf.Apps.TLS.Automation.Policies {
		if p == nil {
			continue
		}
		for _, it := range p.IssuersRaw {
			m, ok := it.(map[string]any)
			if !ok {
				continue
			}
			switch m["module"] {
			case "acme":
				if m["ca"] == "https://acme-staging-v02.api.letsencrypt.org/directory" {
					caFound = true
				}
			case "zerossl":
				zeroFound = true
			}
		}
	}
	require.True(t, caFound)
	require.True(t, zeroFound)
}

func TestGenerateConfig_IPSubjects_InitializesTLSAppAndAutomation(t *testing.T) {
	providerID := uint(19)
	host := models.ProxyHost{
		Enabled:     true,
		UUID:        "ip-host",
		DomainNames: "1.2.3.4",
		ForwardHost: "app",
		ForwardPort: 8080,
		DNSProvider: &models.DNSProvider{ID: providerID, ProviderType: "cloudflare"},
	}

	conf, err := GenerateConfig(
		[]models.ProxyHost{host},
		t.TempDir(),
		"",
		"",
		"",
		false,
		false, false, false, false,
		"",
		nil,
		nil,
		nil,
		&models.SecurityConfig{},
		nil,
	)
	require.NoError(t, err)
	require.NotNil(t, conf)
	require.NotNil(t, conf.Apps.TLS)
	require.NotNil(t, conf.Apps.TLS.Automation)
	require.NotEmpty(t, conf.Apps.TLS.Automation.Policies)
}

func TestGetAccessLogPath_DockerEnv_UsesCrowdSecPath(t *testing.T) {
	const dockerMarker = "/.dockerenv"
	if err := os.WriteFile(dockerMarker, []byte("test"), 0o600); err != nil {
		t.Skipf("cannot create %s: %v", dockerMarker, err)
	}
	t.Cleanup(func() { _ = os.Remove(dockerMarker) })

	path := getAccessLogPath(t.TempDir(), false)
	require.Equal(t, "/var/log/caddy/access.log", path)
}
