package store

import (
	"reflect"
	"testing"

	"github.com/google/uuid"

	query "code.fbi.h-da.de/danet/gosdn/controller/store"
)

type testItem struct {
	ID uuid.UUID
}

func (t testItem) GetID() uuid.UUID {
	return t.ID
}

func getTestItem() testItem {
	return testItem{
		ID: uuid.MustParse("44fb4aa4-c53c-4cf9-a081-5aabc61c7610"),
	}
}

func getEmptyTestItem() testItem {
	return testItem{}
}

func TestNewGenericStore(t *testing.T) {
	tests := []struct {
		name string
		want *GenericStore[testItem]
	}{
		{
			name: "should create generic store",
			want: NewGenericStore[testItem](),
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := NewGenericStore[testItem](); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("NewGenericStore() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGenericStore_Get(t *testing.T) {
	type fields struct {
		items []testItem
	}
	type args struct {
		query query.Query
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    testItem
		wantErr bool
	}{
		{
			name: "should error if item with uuid is not in store",
			fields: fields{
				items: []testItem{},
			},
			args: args{
				query: query.Query{
					ID: getTestItem().ID,
				},
			},
			want:    getEmptyTestItem(),
			wantErr: true,
		},
		{
			name: "should return item that is in the store",
			fields: fields{
				items: []testItem{getTestItem()},
			},
			args: args{
				query: query.Query{
					ID: getTestItem().ID,
				},
			},
			want:    getTestItem(),
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := NewGenericStore[testItem]()

			for _, itemToAdd := range tt.fields.items {
				err := s.Add(itemToAdd)
				if err != nil {
					t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr)
					return
				}
			}

			got, err := s.Get(tt.args.query)
			if (err != nil) != tt.wantErr {
				t.Errorf("GenericStore.Get() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("GenericStore.Get() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGenericStore_GetAll(t *testing.T) {
	type fields struct {
		items []testItem
	}
	type args struct {
		query query.Query
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		want    []testItem
		wantErr bool
	}{
		{
			name: "should return all items that are in the store",
			fields: fields{
				items: []testItem{getTestItem()},
			},
			args: args{
				query: query.Query{
					ID: getTestItem().ID,
				},
			},
			want:    []testItem{getTestItem()},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := NewGenericStore[testItem]()

			for _, itemToAdd := range tt.fields.items {
				err := s.Add(itemToAdd)
				if err != nil {
					t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr)
					return
				}
			}

			got, err := s.GetAll()
			if (err != nil) != tt.wantErr {
				t.Errorf("GenericStore.GetAll() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("GenericStore.GetAll() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGenericStore_Add(t *testing.T) {
	type fields struct {
		items []testItem
	}
	type args struct {
		item testItem
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{
			name: "should add a item",
			args: args{
				item: getTestItem(),
			},
			wantErr: false,
		},
		{
			name: "should fail when adding a already existing item",
			fields: fields{
				items: []testItem{getTestItem()},
			},
			args: args{
				item: getTestItem(),
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := NewGenericStore[testItem]()

			for _, itemToAdd := range tt.fields.items {
				err := s.Add(itemToAdd)
				if err != nil {
					t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr)
					return
				}
			}

			if err := s.Add(tt.args.item); (err != nil) != tt.wantErr {
				t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestGenericStore_Update(t *testing.T) {
	type fields struct {
		items []testItem
	}
	type args struct {
		item testItem
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{
			name: "should fail when item does not exist",
			args: args{
				item: getTestItem(),
			},
			wantErr: true,
		},
		{
			name: "should update a existing a item",
			fields: fields{
				items: []testItem{getTestItem()},
			},
			args: args{
				item: getTestItem(),
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := NewGenericStore[testItem]()

			for _, itemToAdd := range tt.fields.items {
				err := s.Add(itemToAdd)
				if err != nil {
					t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr)
					return
				}
			}

			if err := s.Update(tt.args.item); (err != nil) != tt.wantErr {
				t.Errorf("GenericStore.Update() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestGenericStore_Delete(t *testing.T) {
	type fields struct {
		items []testItem
	}
	type args struct {
		item testItem
	}
	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
	}{
		{
			name: "should fail when item does not exist",
			args: args{
				item: getTestItem(),
			},
			wantErr: true,
		},
		{
			name: "should delete a existing a item",
			fields: fields{
				items: []testItem{getTestItem()},
			},
			args: args{
				item: getTestItem(),
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			s := NewGenericStore[testItem]()

			for _, itemToAdd := range tt.fields.items {
				err := s.Add(itemToAdd)
				if err != nil {
					t.Errorf("GenericStore.Add() error = %v, wantErr %v", err, tt.wantErr)
					return
				}
			}

			if err := s.Delete(tt.args.item); (err != nil) != tt.wantErr {
				t.Errorf("GenericStore.Delete() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}
