package handlers

import (
	"testing"

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

func TestDeviceInfo_DeviceTypeSyncURLs(t *testing.T) {
	baseURL := "http://localhost:8080"

	tests := []struct {
		name            string
		deviceType      string
		authToken       string
		expectedURLs    map[string]string
		expectedHasURLs bool
	}{
		{
			name:       "Kobo device",
			deviceType: "kobo",
			authToken:  "dev_abc123",
			expectedURLs: map[string]string{
				"sync_url": "http://localhost:8080/api/sync/kobo/dev_abc123",
				"markup":   "http://localhost:8080/api/sync/kobo/dev_abc123/markup",
				"bookmark": "http://localhost:8080/api/sync/kobo/dev_abc123/bookmark",
				"init":     "http://localhost:8080/api/sync/kobo/dev_abc123/v1/initialization",
			},
			expectedHasURLs: true,
		},
		{
			name:       "KOReader device",
			deviceType: "koreader",
			authToken:  "dev_xyz789",
			expectedURLs: map[string]string{
				"progress":  "http://localhost:8080/api/sync/koreader/progress",
				"metadata":  "http://localhost:8080/api/sync/koreader/metadata",
				"bookmarks": "http://localhost:8080/api/sync/koreader/bookmarks",
			},
			expectedHasURLs: true,
		},
		{
			name:            "Unknown device type",
			deviceType:      "unknown",
			authToken:       "dev_test",
			expectedURLs:    map[string]string{},
			expectedHasURLs: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Build sync URLs as done in RegenerateDeviceToken
			syncURLs := map[string]string{}

			switch tt.deviceType {
			case "kobo":
				syncURLs["sync_url"] = baseURL + "/api/sync/kobo/" + tt.authToken
				syncURLs["markup"] = baseURL + "/api/sync/kobo/" + tt.authToken + "/markup"
				syncURLs["bookmark"] = baseURL + "/api/sync/kobo/" + tt.authToken + "/bookmark"
				syncURLs["init"] = baseURL + "/api/sync/kobo/" + tt.authToken + "/v1/initialization"
			case "koreader":
				syncURLs["progress"] = baseURL + "/api/sync/koreader/progress"
				syncURLs["metadata"] = baseURL + "/api/sync/koreader/metadata"
				syncURLs["bookmarks"] = baseURL + "/api/sync/koreader/bookmarks"
			}

			if tt.expectedHasURLs {
				assert.Len(t, syncURLs, len(tt.expectedURLs))
				for key, expectedURL := range tt.expectedURLs {
					actualURL, ok := syncURLs[key]
					assert.True(t, ok, "URL key %s should exist", key)
					assert.Equal(t, expectedURL, actualURL)
				}
			} else {
				assert.Empty(t, syncURLs)
			}
		})
	}
}

func TestGenerateDeviceToken(t *testing.T) {
	// Test that generateDeviceToken produces valid tokens
	tokens := make(map[string]bool)

	// Generate multiple tokens and verify they're unique
	for i := 0; i < 100; i++ {
		token, err := generateDeviceToken()
		assert.NoError(t, err, "Should generate token without error")
		assert.NotEmpty(t, token, "Token should not be empty")

		// Verify token starts with "dev_"
		assert.True(t, len(token) > 4, "Token should be longer than prefix")
		assert.Contains(t, token, "dev_", "Token should start with dev_ prefix")

		// Verify tokens are unique
		assert.False(t, tokens[token], "Token should be unique")
		tokens[token] = true
	}

	// Verify we got 100 unique tokens
	assert.Len(t, tokens, 100, "All generated tokens should be unique")
}

func TestDeviceInfo_SyncEnabledValidation(t *testing.T) {
	tests := []struct {
		name               string
		syncEnabled        bool
		syncEnabledValid   bool
		expectedFinalValue bool
	}{
		{
			name:               "Sync enabled and valid",
			syncEnabled:        true,
			syncEnabledValid:   true,
			expectedFinalValue: true,
		},
		{
			name:               "Sync disabled but valid",
			syncEnabled:        false,
			syncEnabledValid:   true,
			expectedFinalValue: false,
		},
		{
			name:               "Sync enabled but not valid",
			syncEnabled:        true,
			syncEnabledValid:   false,
			expectedFinalValue: false,
		},
		{
			name:               "Sync disabled and not valid",
			syncEnabled:        false,
			syncEnabledValid:   false,
			expectedFinalValue: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Simulate the logic in RegenerateDeviceToken
			finalValue := tt.syncEnabled && tt.syncEnabledValid
			assert.Equal(t, tt.expectedFinalValue, finalValue)
		})
	}
}

func TestDeviceInfo_IDParsing(t *testing.T) {
	// Test UUID parsing logic from device ID
	deviceID := uuid.New()
	deviceIDBytes := [16]byte(deviceID)

	// Verify we can convert back
	parsedUUID := uuid.UUID(deviceIDBytes)
	assert.Equal(t, deviceID, parsedUUID, "UUID should be preserved through byte array conversion")

	// Test that we can get the string representation
	deviceIDStr := deviceID.String()
	assert.NotEmpty(t, deviceIDStr, "UUID string should not be empty")

	// Test that parsing the string gives us the same UUID
	parsedFromStr, err := uuid.Parse(deviceIDStr)
	assert.NoError(t, err, "Should parse UUID string without error")
	assert.Equal(t, deviceID, parsedFromStr, "Parsed UUID should match original")
}

func TestDeviceUpdateRequest_Validation(t *testing.T) {
	tests := []struct {
		name          string
		syncFrequency int32
		expectedValid bool
	}{
		{
			name:          "Valid sync frequency",
			syncFrequency: 5,
			expectedValid: true,
		},
		{
			name:          "Zero sync frequency",
			syncFrequency: 0,
			expectedValid: true,
		},
		{
			name:          "High sync frequency",
			syncFrequency: 1440, // 1 day
			expectedValid: true,
		},
		{
			name:          "Negative sync frequency",
			syncFrequency: -1,
			expectedValid: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Simulate validation logic
			isValid := tt.syncFrequency >= 0
			assert.Equal(t, tt.expectedValid, isValid)
		})
	}
}

func TestValidateDeviceToken(t *testing.T) {
	hashedToken := []byte("$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi")
	plainToken := "password"

	result := ValidateDeviceToken(string(hashedToken), plainToken)
	assert.True(t, result, "Valid token should return true")
}

func TestValidateDeviceToken_Invalid(t *testing.T) {
	hashedToken := []byte("$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi")
	plainToken := "wrongpassword"

	result := ValidateDeviceToken(string(hashedToken), plainToken)
	assert.False(t, result, "Invalid token should return false")
}

func TestValidateDeviceToken_Empty(t *testing.T) {
	result := ValidateDeviceToken("", "anypassword")
	assert.False(t, result, "Empty hash should return false")
}
