package config

import (
	"testing"

	"backend/pkg/permission/domain/token"

	"github.com/stretchr/testify/assert"
)

func TestTokenConfig_Validate(t *testing.T) {
	tests := []struct {
		name    string
		config  *TokenConfig
		wantErr bool
		check   func(*testing.T, *TokenConfig)
	}{
		{
			name: "valid config",
			config: &TokenConfig{
				Secret: "test-secret",
				Expired: ExpiredConfig{
					Seconds: 900,
				},
				RefreshExpires: ExpiredConfig{
					Seconds: 604800,
				},
				Issuer:             "test-issuer",
				MaxTokensPerUser:   10,
				MaxTokensPerDevice: 5,
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.Equal(t, "test-secret", c.Secret)
				assert.Equal(t, int64(900), c.Expired.Seconds)
				assert.Equal(t, int64(604800), c.RefreshExpires.Seconds)
			},
		},
		{
			name: "missing secret",
			config: &TokenConfig{
				Secret: "",
				Expired: ExpiredConfig{
					Seconds: 900,
				},
			},
			wantErr: true,
			check:   nil,
		},
		{
			name: "use default expiry",
			config: &TokenConfig{
				Secret: "test-secret",
				Expired: ExpiredConfig{
					Seconds: 0,
				},
				RefreshExpires: ExpiredConfig{
					Seconds: 0,
				},
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.Equal(t, int64(token.DefaultAccessTokenExpiry), c.Expired.Seconds)
				assert.Equal(t, int64(token.DefaultRefreshTokenExpiry), c.RefreshExpires.Seconds)
			},
		},
		{
			name: "use default issuer",
			config: &TokenConfig{
				Secret: "test-secret",
				Issuer: "",
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.Equal(t, "playone-backend", c.Issuer)
			},
		},
		{
			name: "use default token limits",
			config: &TokenConfig{
				Secret:             "test-secret",
				MaxTokensPerUser:   0,
				MaxTokensPerDevice: 0,
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.Equal(t, token.MaxTokensPerUser, c.MaxTokensPerUser)
				assert.Equal(t, token.MaxTokensPerDevice, c.MaxTokensPerDevice)
			},
		},
		{
			name: "negative expiry time",
			config: &TokenConfig{
				Secret: "test-secret",
				Expired: ExpiredConfig{
					Seconds: -100,
				},
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				// Negative values should be replaced with defaults
				assert.Equal(t, int64(token.DefaultAccessTokenExpiry), c.Expired.Seconds)
			},
		},
		{
			name: "custom token limits",
			config: &TokenConfig{
				Secret:             "test-secret",
				MaxTokensPerUser:   20,
				MaxTokensPerDevice: 10,
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.Equal(t, 20, c.MaxTokensPerUser)
				assert.Equal(t, 10, c.MaxTokensPerDevice)
			},
		},
		{
			name: "device tracking enabled",
			config: &TokenConfig{
				Secret:               "test-secret",
				EnableDeviceTracking: true,
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.True(t, c.EnableDeviceTracking)
			},
		},
		{
			name: "device tracking disabled",
			config: &TokenConfig{
				Secret:               "test-secret",
				EnableDeviceTracking: false,
			},
			wantErr: false,
			check: func(t *testing.T, c *TokenConfig) {
				assert.False(t, c.EnableDeviceTracking)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.config.Validate()

			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
				if tt.check != nil {
					tt.check(t, tt.config)
				}
			}
		})
	}
}

func TestExpiredConfig(t *testing.T) {
	tests := []struct {
		name    string
		seconds int64
	}{
		{
			name:    "900 seconds (15 minutes)",
			seconds: 900,
		},
		{
			name:    "3600 seconds (1 hour)",
			seconds: 3600,
		},
		{
			name:    "604800 seconds (7 days)",
			seconds: 604800,
		},
		{
			name:    "zero seconds",
			seconds: 0,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := ExpiredConfig{
				Seconds: tt.seconds,
			}

			assert.Equal(t, tt.seconds, config.Seconds)
		})
	}
}

func TestConfig_Struct(t *testing.T) {
	t.Run("full config", func(t *testing.T) {
		config := Config{
			Token: TokenConfig{
				Secret: "my-secret",
				Expired: ExpiredConfig{
					Seconds: 900,
				},
				RefreshExpires: ExpiredConfig{
					Seconds: 604800,
				},
				Issuer:               "my-app",
				MaxTokensPerUser:     15,
				MaxTokensPerDevice:   8,
				EnableDeviceTracking: true,
			},
		}

		assert.NotNil(t, config.Token)
		assert.Equal(t, "my-secret", config.Token.Secret)
		assert.Equal(t, int64(900), config.Token.Expired.Seconds)
		assert.Equal(t, int64(604800), config.Token.RefreshExpires.Seconds)
		assert.Equal(t, "my-app", config.Token.Issuer)
		assert.Equal(t, 15, config.Token.MaxTokensPerUser)
		assert.Equal(t, 8, config.Token.MaxTokensPerDevice)
		assert.True(t, config.Token.EnableDeviceTracking)
	})

	t.Run("empty config", func(t *testing.T) {
		config := Config{}

		assert.Empty(t, config.Token.Secret)
		assert.Equal(t, int64(0), config.Token.Expired.Seconds)
	})
}

func TestTokenConfig_AllDefaults(t *testing.T) {
	config := &TokenConfig{
		Secret: "test-secret", // Only required field
	}

	err := config.Validate()
	assert.NoError(t, err)

	// Check all defaults are applied
	assert.Equal(t, int64(token.DefaultAccessTokenExpiry), config.Expired.Seconds)
	assert.Equal(t, int64(token.DefaultRefreshTokenExpiry), config.RefreshExpires.Seconds)
	assert.Equal(t, "playone-backend", config.Issuer)
	assert.Equal(t, token.MaxTokensPerUser, config.MaxTokensPerUser)
	assert.Equal(t, token.MaxTokensPerDevice, config.MaxTokensPerDevice)
}
