package frostfs

// Tests for our FrostFS client code

import (
	"testing"

	"context"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"fmt"
	"os"
	"sync"

	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
)

// Initialize storage backend for tests
func openStorage(t *testing.T) *Storage {
	cid := os.Getenv("FROSTFS_CID")           // example: 348WWfBKbS79Wbmm38MRE3oBoEDM5Ga1XXbGKGNyisDM
	endpoint := os.Getenv("FROSTFS_ENDPOINT") // example: grpc://localhost:8802
	if cid == "" || endpoint == "" {
		t.Skipf("one or more environment variables not set: FROSTFS_ENDPOINT, FROSTFS_CID")
	}
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // TODO: using ephemeral keys for now, later read from env vars
	if err != nil {
		t.Fatal(err)
	}
	storage, err := Open(endpoint, cid, key)
	if err != nil {
		t.Fatal(err)
	}
	return storage
}

// Save some bytes to FrostFS and clean up after
func TestObjectPutDelete(t *testing.T) {
	var payload = []byte("Hello FrostFS!\n")
	storage := openStorage(t)
	ctx, cancel := context.WithCancel(context.Background())
	t.Cleanup(cancel)
	oid, err := storage.Save(ctx, payload, "FileName", "hello_from_sdk.txt", "Tag", "foobar")
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("saved object: %s", oid)
	err = storage.Delete(ctx, oid)
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("deleted object: %s", oid)
}

// Check that FrostFS is in fact a content addressable storage
func TestMulipleObjects(t *testing.T) {
	var payload = []byte("Multiple objects with same content should get the same object ID")

	ctx, cancel := context.WithCancel(context.Background())
	t.Cleanup(cancel)
	var wg sync.WaitGroup

	objects := make(chan string)
	go func() {
		baseline := ""
		for {
			select {
			case <-ctx.Done():
				return
			case obj, ok := <-objects:
				if !ok {
					return
				}
				if baseline == "" {
					baseline = obj
					continue
				}
				if obj != baseline {
					t.Fatalf("non-identical object id: %s != %s", baseline, obj)
				}
			}
		}
	}()

	const testCount = 5
	storage := openStorage(t)
	for i := 0; i < testCount; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			oid, err := storage.Save(ctx, payload, "FileName", "CAS.txt", "Tag", "test")
			if err != nil {
				t.Fatal(err)
			}
			err = storage.Delete(ctx, oid)
			if err != nil {
				t.Fatal(err)
			}
			t.Log(oid)
		}()
	}
	wg.Wait()
}

// Check opening wallet from file system
func TestLoadWallet(t *testing.T) {
	const (
		walletPath    = "client_test_wallet.json"
		walletAccount = "NWZnjbTKbzwtX6w1q5R3kbEKrnJ5bp1kn7"
	)
	key, err := getKey(walletPath, "", "")
	if err != nil {
		t.Fatal(err)
	}
	if key2addr(key) != walletAccount {
		t.Fatalf("incorrect address for default account: want %s, got %s", walletAccount, key2addr(key))
	}
	key, err = getKey(walletPath, walletAccount, "")
	if err != nil {
		t.Fatal(err)
	}
	if key2addr(key) != walletAccount {
		t.Fatalf("incorrect address for specific account: want %s, got %s", walletAccount, key2addr(key))
	}
}

func key2addr(k *ecdsa.PrivateKey) string {
	var owner user.ID
	user.IDFromKey(&owner, k.PublicKey)
	return fmt.Sprint(owner)
}