package main

import (
	"encoding/json"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestSyncServerAuthentication(t *testing.T) {
	server := &SyncServer{authToken: "test-token"}

	tests := []struct {
		name     string
		token    string
		expected bool
	}{
		{"Valid token", "test-token", true},
		{"Invalid token", "wrong-token", false},
		{"Empty token", "", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Test the authentication logic directly without HTTP
			result := strings.Compare(tt.token, server.authToken) == 0
			if result != tt.expected {
				t.Errorf("authenticate() = %v, want %v", result, tt.expected)
			}
		})
	}
}

func TestLoadDataNonExistentFile(t *testing.T) {
	tempDir := t.TempDir()
	server := &SyncServer{
		dataFile: filepath.Join(tempDir, "nonexistent.json"),
	}

	backup, err := server.loadData()
	if err != nil {
		t.Errorf("loadData() error = %v, want nil", err)
	}

	if backup == nil {
		t.Error("Expected backup to be non-nil")
	}

	if len(backup.Gyms) != 0 || len(backup.Problems) != 0 || len(backup.Sessions) != 0 || len(backup.Attempts) != 0 {
		t.Error("Expected empty backup data")
	}

	if backup.Version != "2.0" || backup.FormatVersion != "2.0" {
		t.Error("Expected version and format version to be 2.0")
	}
}

func TestSaveAndLoadData(t *testing.T) {
	tempDir := t.TempDir()
	server := &SyncServer{
		dataFile:  filepath.Join(tempDir, "test.json"),
		imagesDir: filepath.Join(tempDir, "images"),
	}

	testData := &ClimbDataBackup{
		Version:       "2.0",
		FormatVersion: "2.0",
		Gyms: []BackupGym{
			{
				ID:   "gym1",
				Name: "Test Gym",
			},
		},
		Problems: []BackupProblem{
			{
				ID:        "problem1",
				GymID:     "gym1",
				ClimbType: "BOULDER",
				Difficulty: DifficultyGrade{
					System:       "V",
					Grade:        "V5",
					NumericValue: 5,
				},
				IsActive: true,
			},
		},
		Sessions: []BackupClimbSession{},
		Attempts: []BackupAttempt{},
	}

	err := server.saveData(testData)
	if err != nil {
		t.Errorf("saveData() error = %v", err)
	}

	loadedData, err := server.loadData()
	if err != nil {
		t.Errorf("loadData() error = %v", err)
	}

	if len(loadedData.Gyms) != 1 || loadedData.Gyms[0].ID != "gym1" {
		t.Error("Loaded gym data doesn't match saved data")
	}

	if len(loadedData.Problems) != 1 || loadedData.Problems[0].ID != "problem1" {
		t.Error("Loaded problem data doesn't match saved data")
	}
}

func TestMinFunction(t *testing.T) {
	tests := []struct {
		a, b, expected int
	}{
		{5, 3, 3},
		{2, 8, 2},
		{4, 4, 4},
		{0, 1, 0},
		{-1, 2, -1},
	}

	for _, tt := range tests {
		result := min(tt.a, tt.b)
		if result != tt.expected {
			t.Errorf("min(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected)
		}
	}
}

func TestClimbDataBackupValidation(t *testing.T) {
	tests := []struct {
		name    string
		backup  ClimbDataBackup
		isValid bool
	}{
		{
			name: "Valid backup",
			backup: ClimbDataBackup{
				Version:       "2.0",
				FormatVersion: "2.0",
				Gyms:          []BackupGym{},
				Problems:      []BackupProblem{},
				Sessions:      []BackupClimbSession{},
				Attempts:      []BackupAttempt{},
			},
			isValid: true,
		},
		{
			name: "Missing version",
			backup: ClimbDataBackup{
				FormatVersion: "2.0",
				Gyms:          []BackupGym{},
				Problems:      []BackupProblem{},
				Sessions:      []BackupClimbSession{},
				Attempts:      []BackupAttempt{},
			},
			isValid: false,
		},
		{
			name: "Missing format version",
			backup: ClimbDataBackup{
				Version:  "2.0",
				Gyms:     []BackupGym{},
				Problems: []BackupProblem{},
				Sessions: []BackupClimbSession{},
				Attempts: []BackupAttempt{},
			},
			isValid: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Test basic validation logic
			hasVersion := tt.backup.Version != ""
			hasFormatVersion := tt.backup.FormatVersion != ""
			isValid := hasVersion && hasFormatVersion

			if isValid != tt.isValid {
				t.Errorf("validation = %v, want %v", isValid, tt.isValid)
			}
		})
	}
}

func TestBackupDataStructures(t *testing.T) {
	t.Run("BackupGym", func(t *testing.T) {
		gym := BackupGym{
			ID:                     "gym1",
			Name:                   "Test Gym",
			SupportedClimbTypes:    []string{"BOULDER", "ROPE"},
			DifficultySystems:      []string{"V", "YDS"},
			CustomDifficultyGrades: []string{},
			CreatedAt:              "2024-01-01T10:00:00Z",
			UpdatedAt:              "2024-01-01T10:00:00Z",
		}

		if gym.ID != "gym1" {
			t.Errorf("Expected gym ID 'gym1', got %s", gym.ID)
		}

		if len(gym.SupportedClimbTypes) != 2 {
			t.Errorf("Expected 2 climb types, got %d", len(gym.SupportedClimbTypes))
		}
	})

	t.Run("BackupProblem", func(t *testing.T) {
		problem := BackupProblem{
			ID:        "problem1",
			GymID:     "gym1",
			ClimbType: "BOULDER",
			Difficulty: DifficultyGrade{
				System:       "V",
				Grade:        "V5",
				NumericValue: 5,
			},
			IsActive:  true,
			CreatedAt: "2024-01-01T10:00:00Z",
			UpdatedAt: "2024-01-01T10:00:00Z",
		}

		if problem.ClimbType != "BOULDER" {
			t.Errorf("Expected climb type 'BOULDER', got %s", problem.ClimbType)
		}

		if problem.Difficulty.Grade != "V5" {
			t.Errorf("Expected difficulty 'V5', got %s", problem.Difficulty.Grade)
		}
	})
}

func TestDifficultyGrade(t *testing.T) {
	tests := []struct {
		name          string
		grade         DifficultyGrade
		expectedGrade string
		expectedValue int
	}{
		{
			name: "V-Scale grade",
			grade: DifficultyGrade{
				System:       "V",
				Grade:        "V5",
				NumericValue: 5,
			},
			expectedGrade: "V5",
			expectedValue: 5,
		},
		{
			name: "YDS grade",
			grade: DifficultyGrade{
				System:       "YDS",
				Grade:        "5.10a",
				NumericValue: 10,
			},
			expectedGrade: "5.10a",
			expectedValue: 10,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.grade.Grade != tt.expectedGrade {
				t.Errorf("Expected grade %s, got %s", tt.expectedGrade, tt.grade.Grade)
			}

			if tt.grade.NumericValue != tt.expectedValue {
				t.Errorf("Expected numeric value %d, got %d", tt.expectedValue, tt.grade.NumericValue)
			}
		})
	}
}

func TestJSONSerialization(t *testing.T) {
	backup := ClimbDataBackup{
		Version:       "2.0",
		FormatVersion: "2.0",
		Gyms: []BackupGym{
			{
				ID:   "gym1",
				Name: "Test Gym",
			},
		},
		Problems: []BackupProblem{
			{
				ID:        "problem1",
				GymID:     "gym1",
				ClimbType: "BOULDER",
				Difficulty: DifficultyGrade{
					System:       "V",
					Grade:        "V5",
					NumericValue: 5,
				},
				IsActive: true,
			},
		},
		Sessions: []BackupClimbSession{},
		Attempts: []BackupAttempt{},
	}

	// Test JSON marshaling
	jsonData, err := json.Marshal(backup)
	if err != nil {
		t.Errorf("Failed to marshal JSON: %v", err)
	}

	// Test JSON unmarshaling
	var unmarshaledBackup ClimbDataBackup
	err = json.Unmarshal(jsonData, &unmarshaledBackup)
	if err != nil {
		t.Errorf("Failed to unmarshal JSON: %v", err)
	}

	if unmarshaledBackup.Version != backup.Version {
		t.Errorf("Version mismatch after JSON round-trip")
	}

	if len(unmarshaledBackup.Gyms) != len(backup.Gyms) {
		t.Errorf("Gyms count mismatch after JSON round-trip")
	}
}

func TestTimestampHandling(t *testing.T) {
	now := time.Now().UTC()
	timestamp := now.Format(time.RFC3339)

	// Test that timestamp is in correct format
	parsedTime, err := time.Parse(time.RFC3339, timestamp)
	if err != nil {
		t.Errorf("Failed to parse timestamp: %v", err)
	}

	if parsedTime.Year() != now.Year() {
		t.Errorf("Year mismatch in timestamp")
	}
}

func TestFilePathHandling(t *testing.T) {
	tempDir := t.TempDir()

	tests := []struct {
		name     string
		filename string
		isValid  bool
	}{
		{"Valid filename", "test.json", true},
		{"Valid path", filepath.Join(tempDir, "data.json"), true},
		{"Empty filename", "", false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			isEmpty := tt.filename == ""
			isValid := !isEmpty

			if isValid != tt.isValid {
				t.Errorf("File path validation = %v, want %v", isValid, tt.isValid)
			}
		})
	}
}
