package models

import (
	"testing"
	"time"

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

func TestUser_SetPassword(t *testing.T) {
	u := &User{}
	err := u.SetPassword("password123")
	assert.NoError(t, err)
	assert.NotEmpty(t, u.PasswordHash)
	assert.NotEqual(t, "password123", u.PasswordHash)

	// Test with empty password (should still work but hash empty string)
	u2 := &User{}
	err = u2.SetPassword("")
	assert.NoError(t, err)
	assert.NotEmpty(t, u2.PasswordHash)

	// Test with special characters
	u3 := &User{}
	err = u3.SetPassword("P@ssw0rd!#$%^&*()")
	assert.NoError(t, err)
	assert.NotEmpty(t, u3.PasswordHash)
	assert.True(t, u3.CheckPassword("P@ssw0rd!#$%^&*()"))
}

func TestUser_CheckPassword(t *testing.T) {
	u := &User{}
	_ = u.SetPassword("password123")

	assert.True(t, u.CheckPassword("password123"))
	assert.False(t, u.CheckPassword("wrongpassword"))
}

func TestUser_HasPendingInvite(t *testing.T) {
	tests := []struct {
		name     string
		user     User
		expected bool
	}{
		{
			name:     "no invite token",
			user:     User{InviteToken: "", InviteStatus: ""},
			expected: false,
		},
		{
			name: "expired invite",
			user: User{
				InviteToken:   "token123",
				InviteExpires: timePtr(time.Now().Add(-1 * time.Hour)),
				InviteStatus:  "pending",
			},
			expected: false,
		},
		{
			name: "valid pending invite",
			user: User{
				InviteToken:   "token123",
				InviteExpires: timePtr(time.Now().Add(24 * time.Hour)),
				InviteStatus:  "pending",
			},
			expected: true,
		},
		{
			name: "already accepted invite",
			user: User{
				InviteToken:   "token123",
				InviteExpires: timePtr(time.Now().Add(24 * time.Hour)),
				InviteStatus:  "accepted",
			},
			expected: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.user.HasPendingInvite()
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestUser_CanAccessHost_AllowAll(t *testing.T) {
	// User with allow_all mode (blacklist) - can access everything except listed hosts
	user := User{
		Role:           "user",
		PermissionMode: PermissionModeAllowAll,
		PermittedHosts: []ProxyHost{
			{ID: 1}, // Blocked host
			{ID: 2}, // Blocked host
		},
	}

	// Should NOT be able to access hosts in the blacklist
	assert.False(t, user.CanAccessHost(1))
	assert.False(t, user.CanAccessHost(2))

	// Should be able to access other hosts
	assert.True(t, user.CanAccessHost(3))
	assert.True(t, user.CanAccessHost(100))
}

func TestUser_CanAccessHost_DenyAll(t *testing.T) {
	// User with deny_all mode (whitelist) - can only access listed hosts
	user := User{
		Role:           "user",
		PermissionMode: PermissionModeDenyAll,
		PermittedHosts: []ProxyHost{
			{ID: 5}, // Allowed host
			{ID: 6}, // Allowed host
		},
	}

	// Should be able to access hosts in the whitelist
	assert.True(t, user.CanAccessHost(5))
	assert.True(t, user.CanAccessHost(6))

	// Should NOT be able to access other hosts
	assert.False(t, user.CanAccessHost(1))
	assert.False(t, user.CanAccessHost(100))
}

func TestUser_CanAccessHost_AdminBypass(t *testing.T) {
	// Admin users should always have access regardless of permission mode
	adminUser := User{
		Role:           "admin",
		PermissionMode: PermissionModeDenyAll,
		PermittedHosts: []ProxyHost{}, // No hosts in whitelist
	}

	// Admin should still be able to access any host
	assert.True(t, adminUser.CanAccessHost(1))
	assert.True(t, adminUser.CanAccessHost(999))
}

func TestUser_CanAccessHost_DefaultBehavior(t *testing.T) {
	// User with empty/default permission mode should behave like allow_all
	user := User{
		Role:           "user",
		PermissionMode: "", // Empty = default
		PermittedHosts: []ProxyHost{
			{ID: 1}, // Should be blocked
		},
	}

	assert.False(t, user.CanAccessHost(1))
	assert.True(t, user.CanAccessHost(2))
}

func TestUser_CanAccessHost_EmptyPermittedHosts(t *testing.T) {
	tests := []struct {
		name           string
		permissionMode PermissionMode
		hostID         uint
		expected       bool
	}{
		{
			name:           "allow_all with no exceptions allows all",
			permissionMode: PermissionModeAllowAll,
			hostID:         1,
			expected:       true,
		},
		{
			name:           "deny_all with no exceptions denies all",
			permissionMode: PermissionModeDenyAll,
			hostID:         1,
			expected:       false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			user := User{
				Role:           "user",
				PermissionMode: tt.permissionMode,
				PermittedHosts: []ProxyHost{},
			}
			result := user.CanAccessHost(tt.hostID)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestPermissionMode_Constants(t *testing.T) {
	assert.Equal(t, PermissionMode("allow_all"), PermissionModeAllowAll)
	assert.Equal(t, PermissionMode("deny_all"), PermissionModeDenyAll)
}

// Helper function to create time pointers
func timePtr(t time.Time) *time.Time {
	return &t
}
