package tsm1_test

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/influxdata/influxdb/tsdb/engine/tsm1"
)

func TestTombstoner_Add(t *testing.T) {
	dir := MustTempDir()
	defer func() { os.RemoveAll(dir) }()

	f := MustTempFile(dir)
	ts := &tsm1.Tombstoner{Path: f.Name()}

	entries, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 0; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	stats := ts.TombstoneFiles()
	if got, exp := len(stats), 0; got != exp {
		t.Fatalf("stat length mismatch: got %v, exp %v", got, exp)
	}

	ts.Add([]string{"foo"})

	entries, err = ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	stats = ts.TombstoneFiles()
	if got, exp := len(stats), 1; got != exp {
		t.Fatalf("stat length mismatch: got %v, exp %v", got, exp)
	}

	if stats[0].Size == 0 {
		t.Fatalf("got size %v, exp > 0", stats[0].Size)
	}

	if stats[0].LastModified == 0 {
		t.Fatalf("got lastModified %v, exp > 0", stats[0].LastModified)
	}

	if stats[0].Path == "" {
		t.Fatalf("got path %v, exp != ''", stats[0].Path)
	}

	if got, exp := len(entries), 1; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	if got, exp := entries[0].Key, "foo"; got != exp {
		t.Fatalf("value mismatch: got %v, exp %v", got, exp)
	}

	// Use a new Tombstoner to verify values are persisted
	ts = &tsm1.Tombstoner{Path: f.Name()}
	entries, err = ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 1; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	if got, exp := entries[0].Key, "foo"; got != exp {
		t.Fatalf("value mismatch: got %v, exp %v", got, exp)
	}
}

func TestTombstoner_Add_Empty(t *testing.T) {
	dir := MustTempDir()
	defer func() { os.RemoveAll(dir) }()

	f := MustTempFile(dir)
	ts := &tsm1.Tombstoner{Path: f.Name()}

	entries, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 0; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	ts.Add([]string{})

	// Use a new Tombstoner to verify values are persisted
	ts = &tsm1.Tombstoner{Path: f.Name()}
	entries, err = ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 0; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	stats := ts.TombstoneFiles()
	if got, exp := len(stats), 0; got != exp {
		t.Fatalf("stat length mismatch: got %v, exp %v", got, exp)
	}

}

func TestTombstoner_Delete(t *testing.T) {
	dir := MustTempDir()
	defer func() { os.RemoveAll(dir) }()

	f := MustTempFile(dir)
	ts := &tsm1.Tombstoner{Path: f.Name()}

	ts.Add([]string{"foo"})

	// Use a new Tombstoner to verify values are persisted
	ts = &tsm1.Tombstoner{Path: f.Name()}
	entries, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 1; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	if got, exp := entries[0].Key, "foo"; got != exp {
		t.Fatalf("value mismatch: got %v, exp %v", got, exp)
	}

	if err := ts.Delete(); err != nil {
		fatal(t, "delete tombstone", err)
	}

	stats := ts.TombstoneFiles()
	if got, exp := len(stats), 0; got != exp {
		t.Fatalf("stat length mismatch: got %v, exp %v", got, exp)
	}

	ts = &tsm1.Tombstoner{Path: f.Name()}
	entries, err = ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 0; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}
}

func TestTombstoner_ReadV1(t *testing.T) {
	dir := MustTempDir()
	defer func() { os.RemoveAll(dir) }()

	f := MustTempFile(dir)
	if err := ioutil.WriteFile(f.Name(), []byte("foo\n"), 0x0600); err != nil {
		t.Fatalf("write v1 file: %v", err)
	}
	f.Close()

	if err := os.Rename(f.Name(), f.Name()+".tombstone"); err != nil {
		t.Fatalf("rename tombstone failed: %v", err)
	}

	ts := &tsm1.Tombstoner{Path: f.Name()}

	_, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	entries, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 1; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	if got, exp := entries[0].Key, "foo"; got != exp {
		t.Fatalf("value mismatch: got %v, exp %v", got, exp)
	}

	// Use a new Tombstoner to verify values are persisted
	ts = &tsm1.Tombstoner{Path: f.Name()}
	entries, err = ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 1; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}

	if got, exp := entries[0].Key, "foo"; got != exp {
		t.Fatalf("value mismatch: got %v, exp %v", got, exp)
	}
}

func TestTombstoner_ReadEmptyV1(t *testing.T) {
	dir := MustTempDir()
	defer func() { os.RemoveAll(dir) }()

	f := MustTempFile(dir)
	f.Close()

	if err := os.Rename(f.Name(), f.Name()+".tombstone"); err != nil {
		t.Fatalf("rename tombstone failed: %v", err)
	}

	ts := &tsm1.Tombstoner{Path: f.Name()}

	_, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	entries, err := ts.ReadAll()
	if err != nil {
		fatal(t, "ReadAll", err)
	}

	if got, exp := len(entries), 0; got != exp {
		t.Fatalf("length mismatch: got %v, exp %v", got, exp)
	}
}
