package tools

import (
	"os"
	"path/filepath"
	"testing"

	"skraak/utils"
)

// BirdNET Tests

func TestCallsFromBirda_NewDataFile(t *testing.T) {
	tmpDir := t.TempDir()

	// Create a minimal WAV file
	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	// Create BirdNET results file
	birdaPath := filepath.Join(tmpDir, "test.BirdNET.results.csv")
	birdaContent := "\ufeffStart (s),End (s),Scientific name,Common name,Confidence,File\n0.0,3.0,Turdus migratorius,American Robin,0.85,/some/path/test.WAV\n"
	if err := os.WriteFile(birdaPath, []byte(birdaContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromBirdaInput{
		File: birdaPath,
	}

	output, err := CallsFromBirda(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.DataFilesWritten != 1 {
		t.Errorf("expected 1 data file written, got %d", output.DataFilesWritten)
	}
	if output.Filter != "BirdNET" {
		t.Errorf("expected filter 'BirdNET', got '%s'", output.Filter)
	}
	if output.TotalCalls != 1 {
		t.Errorf("expected 1 call, got %d", output.TotalCalls)
	}

	// Verify .data file was created
	dataPath := wavPath + ".data"
	df, err := utils.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse .data file: %v", err)
	}
	if len(df.Segments) != 1 {
		t.Errorf("expected 1 segment, got %d", len(df.Segments))
	}
	if df.Segments[0].Labels[0].Filter != "BirdNET" {
		t.Errorf("expected filter 'BirdNET', got '%s'", df.Segments[0].Labels[0].Filter)
	}
	if df.Segments[0].Labels[0].Certainty != 85 {
		t.Errorf("expected certainty 85, got %d", df.Segments[0].Labels[0].Certainty)
	}
}

func TestCallsFromBirda_ExistingSameFilter(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	dataPath := wavPath + ".data"
	existingData := `[{"Operator": "Test", "Duration": 60.0}, [5.0, 10.0, 0, 16000, [{"species": "Existing Bird", "certainty": 90, "filter": "BirdNET"}]]]`
	if err := os.WriteFile(dataPath, []byte(existingData), 0644); err != nil {
		t.Fatal(err)
	}

	birdaPath := filepath.Join(tmpDir, "test.BirdNET.results.csv")
	birdaContent := "\ufeffStart (s),End (s),Scientific name,Common name,Confidence,File\n0.0,3.0,New Bird,New Bird,0.85,test.WAV\n"
	if err := os.WriteFile(birdaPath, []byte(birdaContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromBirdaInput{File: birdaPath}
	output, err := CallsFromBirda(input)

	if err == nil {
		t.Error("expected error for same filter, got nil")
	}
	if output.Error == nil {
		t.Error("expected error message in output")
	}
}

func TestCallsFromBirda_ExistingDifferentFilter(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	dataPath := wavPath + ".data"
	existingData := `[{"Operator": "Test", "Duration": 60.0}, [5.0, 10.0, 0, 16000, [{"species": "Kiwi", "certainty": 90, "filter": "Manual"}]]]`
	if err := os.WriteFile(dataPath, []byte(existingData), 0644); err != nil {
		t.Fatal(err)
	}

	birdaPath := filepath.Join(tmpDir, "test.BirdNET.results.csv")
	birdaContent := "\ufeffStart (s),End (s),Scientific name,Common name,Confidence,File\n0.0,3.0,Robin,Robin,0.85,test.WAV\n"
	if err := os.WriteFile(birdaPath, []byte(birdaContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromBirdaInput{File: birdaPath}
	output, err := CallsFromBirda(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.DataFilesWritten != 1 {
		t.Errorf("expected 1 data file written, got %d", output.DataFilesWritten)
	}

	df, err := utils.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse .data file: %v", err)
	}
	if len(df.Segments) != 2 {
		t.Errorf("expected 2 segments after merge, got %d", len(df.Segments))
	}
}

func TestCallsFromBirda_DeleteOption(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	birdaPath := filepath.Join(tmpDir, "test.BirdNET.results.csv")
	birdaContent := "\ufeffStart (s),End (s),Scientific name,Common name,Confidence,File\n0.0,3.0,Robin,Robin,0.85,test.WAV\n"
	if err := os.WriteFile(birdaPath, []byte(birdaContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromBirdaInput{File: birdaPath, Delete: true}
	output, err := CallsFromBirda(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.FilesDeleted != 1 {
		t.Errorf("expected 1 file deleted, got %d", output.FilesDeleted)
	}
	if _, err := os.Stat(birdaPath); !os.IsNotExist(err) {
		t.Error("expected BirdNET file to be deleted")
	}
}

func TestCallsFromBirda_FolderMode(t *testing.T) {
	tmpDir := t.TempDir()

	for i := range 2 {
		wavPath := filepath.Join(tmpDir, "test"+string(rune('0'+i))+".WAV")
		createMinimalWAV(t, wavPath, 16000, 60.0)

		birdaPath := filepath.Join(tmpDir, "test"+string(rune('0'+i))+".BirdNET.results.csv")
		birdaContent := "\ufeffStart (s),End (s),Scientific name,Common name,Confidence,File\n0.0,3.0,Bird,Bird,0.85,test.WAV\n"
		if err := os.WriteFile(birdaPath, []byte(birdaContent), 0644); err != nil {
			t.Fatal(err)
		}
	}

	input := CallsFromBirdaInput{Folder: tmpDir}
	output, err := CallsFromBirda(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.FilesProcessed != 2 {
		t.Errorf("expected 2 files processed, got %d", output.FilesProcessed)
	}
	if output.DataFilesWritten != 2 {
		t.Errorf("expected 2 data files written, got %d", output.DataFilesWritten)
	}
}

// Raven Tests

func TestCallsFromRaven_NewDataFile(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	ravenPath := filepath.Join(tmpDir, "test.Table.1.selections.txt")
	ravenContent := "Selection\tView\tChannel\tBegin Time (s)\tEnd Time (s)\tLow Freq (Hz)\tHigh Freq (Hz)\tSpecies\n1\tSpectrogram 1\t1\t0.0\t5.0\t1000\t5000\tKiwi\n"
	if err := os.WriteFile(ravenPath, []byte(ravenContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromRavenInput{File: ravenPath}
	output, err := CallsFromRaven(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.DataFilesWritten != 1 {
		t.Errorf("expected 1 data file written, got %d", output.DataFilesWritten)
	}
	if output.Filter != "Raven" {
		t.Errorf("expected filter 'Raven', got '%s'", output.Filter)
	}

	dataPath := wavPath + ".data"
	df, err := utils.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse .data file: %v", err)
	}
	if df.Segments[0].FreqLow != 1000 {
		t.Errorf("expected freq_low 1000, got %f", df.Segments[0].FreqLow)
	}
	if df.Segments[0].FreqHigh != 5000 {
		t.Errorf("expected freq_high 5000, got %f", df.Segments[0].FreqHigh)
	}
}

func TestCallsFromRaven_ExistingSameFilter(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	dataPath := wavPath + ".data"
	existingData := `[{"Operator": "Test", "Duration": 60.0}, [5.0, 10.0, 0, 16000, [{"species": "Existing", "certainty": 90, "filter": "Raven"}]]]`
	if err := os.WriteFile(dataPath, []byte(existingData), 0644); err != nil {
		t.Fatal(err)
	}

	ravenPath := filepath.Join(tmpDir, "test.Table.1.selections.txt")
	ravenContent := "Selection\tView\tChannel\tBegin Time (s)\tEnd Time (s)\tLow Freq (Hz)\tHigh Freq (Hz)\tSpecies\n1\tSpectrogram 1\t1\t0.0\t5.0\t1000\t5000\tNew\n"
	if err := os.WriteFile(ravenPath, []byte(ravenContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromRavenInput{File: ravenPath}
	output, err := CallsFromRaven(input)

	if err == nil {
		t.Error("expected error for same filter, got nil")
	}
	if output.Error == nil {
		t.Error("expected error message in output")
	}
}

func TestCallsFromRaven_ExistingDifferentFilter(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	dataPath := wavPath + ".data"
	existingData := `[{"Operator": "Test", "Duration": 60.0}, [5.0, 10.0, 0, 16000, [{"species": "Kiwi", "certainty": 90, "filter": "BirdNET"}]]]`
	if err := os.WriteFile(dataPath, []byte(existingData), 0644); err != nil {
		t.Fatal(err)
	}

	ravenPath := filepath.Join(tmpDir, "test.Table.1.selections.txt")
	ravenContent := "Selection\tView\tChannel\tBegin Time (s)\tEnd Time (s)\tLow Freq (Hz)\tHigh Freq (Hz)\tSpecies\n1\tSpectrogram 1\t1\t0.0\t5.0\t1000\t5000\tMorepork\n"
	if err := os.WriteFile(ravenPath, []byte(ravenContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromRavenInput{File: ravenPath}
	output, err := CallsFromRaven(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.DataFilesWritten != 1 {
		t.Errorf("expected 1 data file written, got %d", output.DataFilesWritten)
	}

	df, err := utils.ParseDataFile(dataPath)
	if err != nil {
		t.Fatalf("failed to parse .data file: %v", err)
	}
	if len(df.Segments) != 2 {
		t.Errorf("expected 2 segments after merge, got %d", len(df.Segments))
	}
}

func TestCallsFromRaven_DeleteOption(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	ravenPath := filepath.Join(tmpDir, "test.Table.1.selections.txt")
	ravenContent := "Selection\tView\tChannel\tBegin Time (s)\tEnd Time (s)\tLow Freq (Hz)\tHigh Freq (Hz)\tSpecies\n1\tSpectrogram 1\t1\t0.0\t5.0\t1000\t5000\tKiwi\n"
	if err := os.WriteFile(ravenPath, []byte(ravenContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromRavenInput{File: ravenPath, Delete: true}
	output, err := CallsFromRaven(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.FilesDeleted != 1 {
		t.Errorf("expected 1 file deleted, got %d", output.FilesDeleted)
	}
	if _, err := os.Stat(ravenPath); !os.IsNotExist(err) {
		t.Error("expected Raven file to be deleted")
	}
}

func TestCallsFromRaven_MultipleSelections(t *testing.T) {
	tmpDir := t.TempDir()

	wavPath := filepath.Join(tmpDir, "test.WAV")
	createMinimalWAV(t, wavPath, 16000, 60.0)

	ravenPath := filepath.Join(tmpDir, "test.Table.1.selections.txt")
	ravenContent := "Selection\tView\tChannel\tBegin Time (s)\tEnd Time (s)\tLow Freq (Hz)\tHigh Freq (Hz)\tSpecies\n1\tSpectrogram 1\t1\t0.0\t5.0\t1000\t5000\tKiwi\n2\tSpectrogram 1\t1\t10.0\t15.0\t2000\t6000\tMorepork\n3\tSpectrogram 1\t1\t20.0\t25.0\t1500\t4500\tTui\n"
	if err := os.WriteFile(ravenPath, []byte(ravenContent), 0644); err != nil {
		t.Fatal(err)
	}

	input := CallsFromRavenInput{File: ravenPath}
	output, err := CallsFromRaven(input)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if output.TotalCalls != 3 {
		t.Errorf("expected 3 calls, got %d", output.TotalCalls)
	}
	if output.SpeciesCount["Kiwi"] != 1 || output.SpeciesCount["Morepork"] != 1 || output.SpeciesCount["Tui"] != 1 {
		t.Errorf("unexpected species count: %v", output.SpeciesCount)
	}
}