package database

import (
	"context"
	"fmt"
	"reflect"
	"testing"
	"time"

	"git.anthrove.art/Anthrove/otter-space-sdk/v5/pkg/models"
	"git.anthrove.art/Anthrove/otter-space-sdk/v5/test"
	"go.opentelemetry.io/contrib/bridges/otellogrus"
	"go.opentelemetry.io/otel"
	"gorm.io/gorm"
)

func TestCreatePool(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PoolgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests

	// -- Create Pool to test with
	validPool := models.Pool{
		BaseModel: models.BaseModel[models.PoolID]{
			ID:        models.PoolID(fmt.Sprintf("%025s", "Pool1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Name:     "Test Pool 01",
		Category: models.Series,
	}
	// --

	// -- -- Tests
	type args struct {
		ctx  context.Context
		post models.Pool
	}
	tests := []struct {
		name    string
		args    args
		want    models.Pool
		wantErr bool
	}{
		{
			name: "Test 01: Valid Pool",
			args: args{
				ctx:  ctx,
				post: validPool,
			},
			want:    validPool,
			wantErr: false,
		},
		{
			name: "Test 02: Duplicate Pool",
			args: args{
				ctx:  ctx,
				post: validPool,
			},
			want:    models.Pool{},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := CreatePool(tt.args.ctx, tt.args.post)
			if (err != nil) != tt.wantErr {
				t.Errorf("CreatePool() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("CreatePool() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestGetPoolByID(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PoolgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests

	// -- Create Pool to test with
	validPool := models.Pool{
		BaseModel: models.BaseModel[models.PoolID]{
			ID:        models.PoolID(fmt.Sprintf("%025s", "Pool1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Name:     "Test Pool 01",
		Category: models.Series,
	}

	validPool, err = CreatePool(ctx, validPool)
	if err != nil {
		logger.Fatal(err)
	}
	// --

	// -- -- Tests
	type args struct {
		ctx context.Context
		id  models.PoolID
	}
	tests := []struct {
		name    string
		args    args
		want    models.Pool
		wantErr bool
	}{
		{
			name: "Test 01: Valid PoolID",
			args: args{
				ctx: ctx,
				id:  validPool.ID,
			},
			want:    validPool,
			wantErr: false,
		},
		{
			name: "Test 03: Empty PoolID",
			args: args{
				ctx: ctx,
				id:  "",
			},
			wantErr: true,
		},
		{
			name: "Test 04: Short PoolID",
			args: args{
				ctx: ctx,
				id:  "111",
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := GetPoolByID(tt.args.ctx, tt.args.id)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetPoolByID() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !checkPoolID(got, tt.want) {
				t.Errorf("GetPoolByID() got = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestUpdatePool(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PoolgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests

	// -- Create Pool to test with
	validPool := models.Pool{
		BaseModel: models.BaseModel[models.PoolID]{
			ID:        models.PoolID(fmt.Sprintf("%025s", "Pool1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Name:     "Test Pool 01",
		Category: models.Series,
	}

	validPool, err = CreatePool(ctx, validPool)
	if err != nil {
		logger.Fatal(err)
	}
	// --

	// -- Create Updates models for Pool
	validUpdatePool := validPool
	validUpdatePool.Name = "Updated Test Pool"
	validUpdatePool.Category = models.Collection

	invalidUpdatePool := models.Pool{}
	// --

	// -- -- Tests
	type args struct {
		ctx          context.Context
		anthrovePool models.Pool
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "Test 01: Valid Update for Pool",
			args: args{
				ctx:          ctx,
				anthrovePool: validUpdatePool,
			},
			wantErr: false,
		},
		{
			name: "Test 02: Invalid Update for Pool",
			args: args{
				ctx:          ctx,
				anthrovePool: invalidUpdatePool,
			},
			wantErr: true,
		},
		{
			name: "Test 03: Empty ID for Update for Pool",
			args: args{
				ctx:          ctx,
				anthrovePool: models.Pool{BaseModel: models.BaseModel[models.PoolID]{ID: ""}},
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := UpdatePool(tt.args.ctx, tt.args.anthrovePool); (err != nil) != tt.wantErr {
				t.Errorf("UpdatePool() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestDeletePool(t *testing.T) {
	// Setup trow away container
	ctx := context.Background()
	container, gormDB, err := test.StartPostgresContainer(ctx)
	if err != nil {
		logger.Fatalf("Could not start PoolgreSQL container: %v", err)
	}

	client = gormDB

	// Setup open telemetry
	tracer = otel.Tracer(tracingName)

	hook := otellogrus.NewHook(tracingName)
	logger.AddHook(hook)

	defer container.Terminate(ctx)

	// -- -- Setup Tests

	// -- Create Pool to test with
	validPool := models.Pool{
		BaseModel: models.BaseModel[models.PoolID]{
			ID:        models.PoolID(fmt.Sprintf("%025s", "Pool1")),
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
			DeletedAt: gorm.DeletedAt{},
		},
		Name:     "Test Pool 01",
		Category: models.Series,
	}

	validPool, err = CreatePool(ctx, validPool)
	if err != nil {
		logger.Fatal(err)
	}
	// --

	// -- -- Tests
	type args struct {
		ctx context.Context
		id  models.PoolID
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "Test 01: Delete Valid Pool",
			args: args{
				ctx: ctx,
				id:  validPool.ID,
			},
			wantErr: false,
		},
		{
			name: "Test 02: Delete not existed Pool",
			args: args{
				ctx: ctx,
				id:  validPool.ID,
			},
			wantErr: false,
		},
		{
			name: "Test 03: Empty PoolID",
			args: args{
				ctx: ctx,
				id:  "",
			},
			wantErr: true,
		},
		{
			name: "Test 04: Short PoolID",
			args: args{
				ctx: ctx,
				id:  "111",
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := DeletePool(tt.args.ctx, tt.args.id); (err != nil) != tt.wantErr {
				t.Errorf("DeletePool() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func checkPoolID(got models.Pool, want models.Pool) bool {
	if got.ID != want.ID {
		return false
	}

	return true
}