package utils

import (
	"testing"
)

func TestParseSelectionFilename(t *testing.T) {
	tests := []struct {
		name             string
		filename         string
		expectedBase     string
		expectedStart    float64
		expectedEnd      float64
		expectError      bool
		errorContains    string
	}{
		{
			name:          "Simple filename with integer times",
			filename:      "20250517_214501-102-133.wav",
			expectedBase:  "20250517_214501",
			expectedStart: 102.0,
			expectedEnd:   133.0,
			expectError:   false,
		},
		{
			name:          "Filename with decimal times",
			filename:      "20250517_214501-102.5-133.7.wav",
			expectedBase:  "20250517_214501",
			expectedStart: 102.5,
			expectedEnd:   133.7,
			expectError:   false,
		},
		{
			name:          "Filename with prefix and dashes",
			filename:      "A05-20250517_214501-102-133.wav",
			expectedBase:  "A05-20250517_214501",
			expectedStart: 102.0,
			expectedEnd:   133.0,
			expectError:   false,
		},
		{
			name:          "Filename with multiple dashes in base",
			filename:      "Site-A-05-20250517_214501-102.5-133.7.wav",
			expectedBase:  "Site-A-05-20250517_214501",
			expectedStart: 102.5,
			expectedEnd:   133.7,
			expectError:   false,
		},
		{
			name:          "PNG extension",
			filename:      "A05-20250517_214501-102-133.png",
			expectedBase:  "A05-20250517_214501",
			expectedStart: 102.0,
			expectedEnd:   133.0,
			expectError:   false,
		},
		{
			name:          "Zero start time",
			filename:      "20250517_214501-0-10.5.wav",
			expectedBase:  "20250517_214501",
			expectedStart: 0.0,
			expectedEnd:   10.5,
			expectError:   false,
		},
		{
			name:          "Invalid format - too few parts",
			filename:      "20250517_214501-102.wav",
			expectError:   true,
			errorContains: "expected at least 3 dash-separated parts",
		},
		{
			name:          "Invalid format - no dashes",
			filename:      "20250517_214501.wav",
			expectError:   true,
			errorContains: "expected at least 3 dash-separated parts",
		},
		{
			name:          "Invalid start time - not a number",
			filename:      "20250517_214501-abc-133.wav",
			expectError:   true,
			errorContains: "invalid start time",
		},
		{
			name:          "Invalid end time - not a number",
			filename:      "20250517_214501-102-xyz.wav",
			expectError:   true,
			errorContains: "invalid end time",
		},
		{
			name:          "Start time equals end time",
			filename:      "20250517_214501-102-102.wav",
			expectError:   true,
			errorContains: "start time",
		},
		{
			name:          "Start time greater than end time",
			filename:      "20250517_214501-133-102.wav",
			expectError:   true,
			errorContains: "start time",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			base, start, end, err := ParseSelectionFilename(tt.filename)

			if tt.expectError {
				if err == nil {
					t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
				} else if tt.errorContains != "" && !containsString(err.Error(), tt.errorContains) {
					t.Errorf("Expected error containing '%s', got: %v", tt.errorContains, err)
				}
				return
			}

			if err != nil {
				t.Errorf("Unexpected error: %v", err)
				return
			}

			if base != tt.expectedBase {
				t.Errorf("Expected base '%s', got '%s'", tt.expectedBase, base)
			}
			if start != tt.expectedStart {
				t.Errorf("Expected start %.2f, got %.2f", tt.expectedStart, start)
			}
			if end != tt.expectedEnd {
				t.Errorf("Expected end %.2f, got %.2f", tt.expectedEnd, end)
			}
		})
	}
}

func TestParseMLFolderName(t *testing.T) {
	tests := []struct {
		name               string
		folderName         string
		expectedFilterName string
		expectedDate       string
		expectError        bool
		errorContains      string
	}{
		{
			name:               "Standard folder name",
			folderName:         "Clips_opensoundscape-kiwi-1.0_2025-11-14",
			expectedFilterName: "opensoundscape-kiwi-1.0",
			expectedDate:       "2025-11-14",
			expectError:        false,
		},
		{
			name:               "Filter name with underscores",
			folderName:         "Clips_my_model_v2_2025-01-15",
			expectedFilterName: "my_model_v2",
			expectedDate:       "2025-01-15",
			expectError:        false,
		},
		{
			name:               "Simple filter name",
			folderName:         "Clips_kiwi_2024-12-31",
			expectedFilterName: "kiwi",
			expectedDate:       "2024-12-31",
			expectError:        false,
		},
		{
			name:          "Missing Clips_ prefix",
			folderName:    "opensoundscape-kiwi-1.0_2025-11-14",
			expectError:   true,
			errorContains: "invalid ML folder name format",
		},
		{
			name:          "Missing date",
			folderName:    "Clips_opensoundscape-kiwi-1.0",
			expectError:   true,
			errorContains: "invalid ML folder name format",
		},
		{
			name:          "Invalid date format",
			folderName:    "Clips_opensoundscape-kiwi-1.0_11-14-2025",
			expectError:   true,
			errorContains: "invalid ML folder name format",
		},
		{
			name:          "Missing filter name",
			folderName:    "Clips__2025-11-14",
			expectError:   true,
			errorContains: "invalid ML folder name format",
		},
		{
			name:          "Extra underscores at end",
			folderName:    "Clips_kiwi_2025-11-14_extra",
			expectError:   true,
			errorContains: "invalid ML folder name format",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			filterName, date, err := ParseMLFolderName(tt.folderName)

			if tt.expectError {
				if err == nil {
					t.Errorf("Expected error containing '%s', got nil", tt.errorContains)
				} else if tt.errorContains != "" && !containsString(err.Error(), tt.errorContains) {
					t.Errorf("Expected error containing '%s', got: %v", tt.errorContains, err)
				}
				return
			}

			if err != nil {
				t.Errorf("Unexpected error: %v", err)
				return
			}

			if filterName != tt.expectedFilterName {
				t.Errorf("Expected filter name '%s', got '%s'", tt.expectedFilterName, filterName)
			}
			if date != tt.expectedDate {
				t.Errorf("Expected date '%s', got '%s'", tt.expectedDate, date)
			}
		})
	}
}

func TestValidateWAVPNGPairs(t *testing.T) {
	tests := []struct {
		name              string
		wavFiles          []string
		pngFiles          []string
		expectedPaired    []string
		expectedMismatched []string
	}{
		{
			name:           "All files paired",
			wavFiles:       []string{"file1-10-20.wav", "file2-30-40.wav"},
			pngFiles:       []string{"file1-10-20.png", "file2-30-40.png"},
			expectedPaired: []string{"file1-10-20", "file2-30-40"},
			expectedMismatched: []string{},
		},
		{
			name:              "Missing PNG for one WAV",
			wavFiles:          []string{"file1-10-20.wav", "file2-30-40.wav", "file3-50-60.wav"},
			pngFiles:          []string{"file1-10-20.png", "file2-30-40.png"},
			expectedPaired:    []string{"file1-10-20", "file2-30-40"},
			expectedMismatched: []string{"file3-50-60.wav"},
		},
		{
			name:              "Missing all PNGs",
			wavFiles:          []string{"file1-10-20.wav", "file2-30-40.wav"},
			pngFiles:          []string{},
			expectedPaired:    []string{},
			expectedMismatched: []string{"file1-10-20.wav", "file2-30-40.wav"},
		},
		{
			name:           "Extra PNG files (no WAV)",
			wavFiles:       []string{"file1-10-20.wav"},
			pngFiles:       []string{"file1-10-20.png", "file2-30-40.png", "file3-50-60.png"},
			expectedPaired: []string{"file1-10-20"},
			expectedMismatched: []string{},
		},
		{
			name:           "No files",
			wavFiles:       []string{},
			pngFiles:       []string{},
			expectedPaired: []string{},
			expectedMismatched: []string{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			paired, mismatched := ValidateWAVPNGPairs(tt.wavFiles, tt.pngFiles)

			if !stringSlicesEqual(paired, tt.expectedPaired) {
				t.Errorf("Expected paired %v, got %v", tt.expectedPaired, paired)
			}
			if !stringSlicesEqual(mismatched, tt.expectedMismatched) {
				t.Errorf("Expected mismatched %v, got %v", tt.expectedMismatched, mismatched)
			}
		})
	}
}

func TestExtractDateTimePattern(t *testing.T) {
	tests := []struct {
		name            string
		filename        string
		expectedPattern string
		expectedFound   bool
	}{
		{
			name:            "8-digit date (YYYYMMDD)",
			filename:        "20250517_214501",
			expectedPattern: "20250517_214501",
			expectedFound:   true,
		},
		{
			name:            "8-digit date with prefix",
			filename:        "A05-20250517_214501",
			expectedPattern: "20250517_214501",
			expectedFound:   true,
		},
		{
			name:            "8-digit date with suffix",
			filename:        "20250517_214501-extra",
			expectedPattern: "20250517_214501",
			expectedFound:   true,
		},
		{
			name:            "6-digit date (YYMMDD or DDMMYY)",
			filename:        "250517_214501",
			expectedPattern: "250517_214501",
			expectedFound:   true,
		},
		{
			name:            "6-digit date with prefix",
			filename:        "Site-170525_214501",
			expectedPattern: "170525_214501",
			expectedFound:   true,
		},
		{
			name:            "No date pattern",
			filename:        "file_without_date",
			expectedPattern: "",
			expectedFound:   false,
		},
		{
			name:            "Incomplete pattern (missing time)",
			filename:        "20250517",
			expectedPattern: "",
			expectedFound:   false,
		},
		{
			name:            "Incomplete pattern (missing date)",
			filename:        "214501",
			expectedPattern: "",
			expectedFound:   false,
		},
		{
			name:            "Wrong separator",
			filename:        "20250517-214501",
			expectedPattern: "",
			expectedFound:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			pattern, found := ExtractDateTimePattern(tt.filename)

			if found != tt.expectedFound {
				t.Errorf("Expected found=%v, got found=%v", tt.expectedFound, found)
			}
			if pattern != tt.expectedPattern {
				t.Errorf("Expected pattern '%s', got '%s'", tt.expectedPattern, pattern)
			}
		})
	}
}

// Helper functions

func containsString(s, substr string) bool {
	return len(substr) == 0 || (len(s) > 0 && len(substr) > 0 &&
		len(s) >= len(substr) && contains(s, substr))
}

func contains(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}

func stringSlicesEqual(a, b []string) bool {
	if len(a) != len(b) {
		return false
	}
	if len(a) == 0 {
		return true
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}