package main

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

// TestReceivingTheSameNameTwiceKeepsBoth is the guarantee that matters most
// about receiving: a file already in the destination folder cannot be lost to
// an upload that happens to carry the same name.
//
// Phones reuse names relentlessly — IMG_0001.JPG, image.jpg, document.pdf — so
// this is the ordinary case, not an edge one. The first version of receive used
// os.Create, which truncates, so the second upload destroyed the first file
// with no warning and no way back.
func TestReceivingTheSameNameTwiceKeepsBoth(t *testing.T) {
	dir := t.TempDir()

	first, usedFirst, err := createWithoutReplacing(dir, "photo.jpg")
	if err != nil {
		t.Fatalf("first save: %v", err)
	}
	if _, err := first.WriteString("the original photo"); err != nil {
		t.Fatalf("writing the first file: %v", err)
	}
	first.Close()
	if usedFirst != "photo.jpg" {
		t.Errorf("the first upload should keep the name it arrived with, got %q", usedFirst)
	}

	second, usedSecond, err := createWithoutReplacing(dir, "photo.jpg")
	if err != nil {
		t.Fatalf("second save: %v", err)
	}
	if _, err := second.WriteString("a different photo, same name"); err != nil {
		t.Fatalf("writing the second file: %v", err)
	}
	second.Close()
	if usedSecond != "photo (2).jpg" {
		t.Errorf("the second upload should be saved beside the first, got %q", usedSecond)
	}

	// The first file must still hold exactly what it held before.
	got, err := os.ReadFile(filepath.Join(dir, "photo.jpg"))
	if err != nil {
		t.Fatalf("the first file is gone: %v", err)
	}
	if string(got) != "the original photo" {
		t.Errorf("the first file was modified: it now holds %q", got)
	}

	// And the second must be there too, with its own contents.
	got2, err := os.ReadFile(filepath.Join(dir, "photo (2).jpg"))
	if err != nil {
		t.Fatalf("the second file was not saved: %v", err)
	}
	if string(got2) != "a different photo, same name" {
		t.Errorf("the second file holds the wrong contents: %q", got2)
	}

	entries, err := os.ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	if len(entries) != 2 {
		names := make([]string, 0, len(entries))
		for _, e := range entries {
			names = append(names, e.Name())
		}
		t.Errorf("expected both files to be in the folder, found %d: %v", len(entries), names)
	}
}

// TestReceivingTheSameNameManyTimesKeepsCounting checks the counter carries on
// past the second collision rather than stopping at one spare name.
func TestReceivingTheSameNameManyTimesKeepsCounting(t *testing.T) {
	dir := t.TempDir()
	want := []string{"IMG_0001.JPG", "IMG_0001 (2).JPG", "IMG_0001 (3).JPG", "IMG_0001 (4).JPG"}

	for i, expect := range want {
		f, used, err := createWithoutReplacing(dir, "IMG_0001.JPG")
		if err != nil {
			t.Fatalf("upload %d: %v", i+1, err)
		}
		f.Close()
		if used != expect {
			t.Errorf("upload %d was saved as %q, want %q", i+1, used, expect)
		}
	}

	entries, err := os.ReadDir(dir)
	if err != nil {
		t.Fatal(err)
	}
	if len(entries) != len(want) {
		t.Errorf("expected %d files, found %d", len(want), len(entries))
	}
}

// TestReceivingNamesWithNoStem covers a name that is nothing but an extension,
// where there is no stem to put a number after.
func TestReceivingNamesWithNoStem(t *testing.T) {
	dir := t.TempDir()

	f, first, err := createWithoutReplacing(dir, ".gitignore")
	if err != nil {
		t.Fatalf("first save: %v", err)
	}
	f.Close()
	if first != ".gitignore" {
		t.Errorf("first save used %q", first)
	}

	f2, second, err := createWithoutReplacing(dir, ".gitignore")
	if err != nil {
		t.Fatalf("second save: %v", err)
	}
	f2.Close()
	if second != ".gitignore (2)" {
		t.Errorf("second save used %q, want %q", second, ".gitignore (2)")
	}
	if _, err := os.Stat(filepath.Join(dir, ".gitignore")); err != nil {
		t.Errorf("the first file did not survive: %v", err)
	}
}

// TestReceivingIntoAMissingFolderIsAnError checks that a genuine failure is
// still reported as one, rather than being mistaken for a name collision and
// counted past ten thousand times.
func TestReceivingIntoAMissingFolderIsAnError(t *testing.T) {
	missing := filepath.Join(t.TempDir(), "no-such-folder")
	if _, _, err := createWithoutReplacing(missing, "photo.jpg"); err == nil {
		t.Error("saving into a folder that does not exist should fail")
	}
}
