package utils

import (
	"testing"
	"time"
)

func TestGenerateFileID(t *testing.T) {
	t.Run("generates 21-character ID", func(t *testing.T) {
		id, err := GenerateLongID()
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(id) != 21 {
			t.Errorf("expected length 21, got %d: %q", len(id), id)
		}
	})

	t.Run("uses only valid alphabet characters", func(t *testing.T) {
		id, err := GenerateLongID()
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		// Default nanoid alphabet includes: 0-9, A-Z, a-z, _, -
		for _, c := range id {
			if !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || c == '-') {
				t.Errorf("invalid character %q in ID %q", string(c), id)
			}
		}
	})

	t.Run("generates unique IDs", func(t *testing.T) {
		seen := make(map[string]bool)
		for i := 0; i < 100; i++ {
			id, err := GenerateLongID()
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if seen[id] {
				t.Errorf("duplicate ID generated: %q", id)
			}
			seen[id] = true
		}
	})
}

func TestResolveTimestamp(t *testing.T) {
	t.Run("resolves AudioMoth timestamp", func(t *testing.T) {
		meta := &WAVMetadata{
			Comment: "Recorded at 21:00:00 24/02/2025 (UTC+13) by AudioMoth 248AB50153AB0549 at medium gain while battery was 4.3V and temperature was 15.8C.",
			Artist:  "AudioMoth",
		}
		result, err := ResolveTimestamp(meta, "20250224_210000.wav", "Pacific/Auckland", false)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if !result.IsAudioMoth {
			t.Error("expected IsAudioMoth to be true")
		}
		if result.MothData == nil {
			t.Error("expected MothData to be non-nil")
		}
		// AudioMoth parser returns UTC+13 fixed offset
		expectedUTC := time.Date(2025, 2, 24, 8, 0, 0, 0, time.UTC)
		if !result.Timestamp.UTC().Equal(expectedUTC) {
			t.Errorf("expected UTC timestamp %v, got %v", expectedUTC, result.Timestamp.UTC())
		}
	})

	t.Run("falls back to filename timestamp", func(t *testing.T) {
		meta := &WAVMetadata{
			Comment: "",
			Artist:  "",
		}
		result, err := ResolveTimestamp(meta, "20250224_210000.wav", "Pacific/Auckland", false)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if result.IsAudioMoth {
			t.Error("expected IsAudioMoth to be false")
		}
		if result.Timestamp.IsZero() {
			t.Error("expected non-zero timestamp")
		}
	})

	t.Run("falls back to file mod time when enabled", func(t *testing.T) {
		modTime := time.Date(2025, 1, 15, 10, 30, 0, 0, time.UTC)
		meta := &WAVMetadata{
			Comment:     "",
			Artist:      "",
			FileModTime: modTime,
		}
		result, err := ResolveTimestamp(meta, "nopattern.wav", "Pacific/Auckland", true)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if !result.Timestamp.Equal(modTime) {
			t.Errorf("expected timestamp %v, got %v", modTime, result.Timestamp)
		}
	})

	t.Run("errors when no timestamp available and file mod time disabled", func(t *testing.T) {
		meta := &WAVMetadata{
			Comment: "",
			Artist:  "",
		}
		_, err := ResolveTimestamp(meta, "nopattern.wav", "Pacific/Auckland", false)
		if err == nil {
			t.Error("expected error when no timestamp available")
		}
	})

	t.Run("errors when no timestamp available and no file mod time", func(t *testing.T) {
		meta := &WAVMetadata{
			Comment: "",
			Artist:  "",
		}
		_, err := ResolveTimestamp(meta, "nopattern.wav", "Pacific/Auckland", true)
		if err == nil {
			t.Error("expected error when no timestamp available")
		}
	})

	t.Run("AudioMoth detected but parse fails falls back to filename", func(t *testing.T) {
		meta := &WAVMetadata{
			Comment: "AudioMoth garbage data",
			Artist:  "",
		}
		result, err := ResolveTimestamp(meta, "20250224_210000.wav", "Pacific/Auckland", false)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if !result.IsAudioMoth {
			t.Error("expected IsAudioMoth to be true (detected even if parse failed)")
		}
		if result.MothData != nil {
			t.Error("expected MothData to be nil since parsing failed")
		}
		if result.Timestamp.IsZero() {
			t.Error("expected non-zero timestamp from filename fallback")
		}
	})
}