package repository_ddb

import (
	"context"
	"dataservices/commons/errs"
	"dataservices/ddb"
	"dataservices/internal/entity"
	"fmt"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/stretchr/testify/require"
)

func TestEndpointTable(t *testing.T) {
	ctx := context.Background()
	region1 := "us-west-1"
	region2 := "us-west-2"
	cli1, err := newDynamoDBClientForTestByRegion(ctx, region1)
	require.NoError(t, err)
	cli2, err := newDynamoDBClientForTestByRegion(ctx, region2)
	require.NoError(t, err)

	defer func() {
		ddb.DeleteTable(ctx, cli1, entity.TableEndpoints)
		ddb.DeleteTable(ctx, cli2, entity.TableEndpoints)
	}()

	err = CreateEndpointRegionTable(ctx, cli1)
	fmt.Printf("CreateEndpointRegionTable: %v\n", err)
	err = CreateEndpointRegionTable(ctx, cli2)
	fmt.Printf("CreateEndpointRegionTable: %v\n", err)
	err = CreateEndpointGlobalTable(ctx, cli1, []string{region1, region2})
	fmt.Printf("CreateEndpointGlobalTable: %v\n", err)

	endpoint1 := &entity.Endpoint{
		ID:          1,
		DataappId:   1,
		Name:        "test-endpoint",
		Type:        "draft",
		Endpoint:    "/testcs",
		Method:      "get",
		ClusterId:   1000,
		Database:    "test",
		SqlTemplate: "select * from t;",
	}
	err = PutEndpoint(ctx, cli1, endpoint1)
	require.NoError(t, err)

	getEndpoint, err := GetEndpoint(ctx, cli1, endpoint1.ID)
	require.NoError(t, err)
	fmt.Printf("getEndpoint: %v \n", getEndpoint)
	require.Equal(t, *endpoint1, *getEndpoint)

	start := time.Now()
	for {
		getEndpoint, err = GetEndpoint(ctx, cli2, endpoint1.ID)
		if err == errs.ErrEndpointNotFound {
			continue
		}
		require.NoError(t, err)
		if getEndpoint.Name == endpoint1.Name {
			break
		}
		time.Sleep(time.Second)
	}
	fmt.Printf("sync cost: %v\n", time.Since(start))
	require.Equal(t, *endpoint1, *getEndpoint)
}

func TestQueryEndpoint(t *testing.T) {
	ctx := context.Background()
	region1 := "us-west-1"
	region2 := "us-west-2"
	cli1, err := newDynamoDBClientForTestByRegion(ctx, region1)
	require.NoError(t, err)
	cli2, err := newDynamoDBClientForTestByRegion(ctx, region2)
	require.NoError(t, err)

	defer func() {
		ddb.DeleteTable(ctx, cli1, entity.TableEndpoints)
		ddb.DeleteTable(ctx, cli2, entity.TableEndpoints)
	}()

	err = CreateEndpointRegionTable(ctx, cli1)
	fmt.Printf("CreateEndpointRegionTable: %v\n", err)
	err = CreateEndpointRegionTable(ctx, cli2)
	fmt.Printf("CreateEndpointRegionTable: %v\n", err)
	err = CreateEndpointGlobalTable(ctx, cli1, []string{region1, region2})
	fmt.Printf("CreateEndpointGlobalTable: %v\n", err)

	endpoint1 := entity.Endpoint{
		ID:          1,
		DataappId:   1,
		Name:        "test-endpoint",
		Type:        "draft",
		Endpoint:    "/test1",
		Method:      "get",
		ClusterId:   1000,
		Database:    "test",
		SqlTemplate: "select * from t;",
	}
	err = PutEndpoint(ctx, cli1, &endpoint1)
	require.NoError(t, err)

	endpoint2 := endpoint1
	endpoint2.ID = 2
	endpoint2.Endpoint = "test2"
	err = PutEndpoint(ctx, cli1, &endpoint2)
	require.NoError(t, err)

	getEndpoint, err := GetEndpoint(ctx, cli1, endpoint1.ID)
	require.NoError(t, err)
	require.Equal(t, endpoint1, *getEndpoint)

	getEndpoint, err = GetEndpoint(ctx, cli1, endpoint2.ID)
	require.NoError(t, err)
	require.Equal(t, endpoint2, *getEndpoint)

	getEndpoint, err = GetEndpoint(ctx, cli1, 1234567)
	require.Nil(t, getEndpoint)
	require.Equal(t, errs.ErrEndpointNotFound, err)

	getEndpoints, err := GetEndpointByName(ctx, cli1, endpoint1.DataappId, endpoint1.Endpoint)
	require.NoError(t, err)
	require.Equal(t, 1, len(getEndpoints))
	require.Equal(t, endpoint1, *getEndpoints[0])

	getEndpoints, err = GetEndpointByName(ctx, cli1, endpoint2.DataappId, endpoint2.Endpoint)
	require.NoError(t, err)
	require.Equal(t, 1, len(getEndpoints))
	require.Equal(t, endpoint2, *getEndpoints[0])

	getEndpoints, err = GetEndpointByName(ctx, cli1, endpoint2.DataappId, "not_exist")
	require.NoError(t, err)
	require.Equal(t, 0, len(getEndpoints))
}

func TestQueryTables(t *testing.T) {
	ctx := context.Background()
	region1 := "us-west-1"
	cli, err := newDynamoDBClientForTestByRegion(ctx, region1)
	require.NoError(t, err)
	defer func() {
		ddb.DeleteTable(ctx, cli, entity.TableEndpoints)
		ddb.DeleteTable(ctx, cli, entity.TableEndpointPublish)
		ddb.DeleteTable(ctx, cli, entity.TableDataApps)
	}()

	err = CreateDataAppRegionTable(ctx, cli)
	fmt.Printf("CreateDataAppRegionTable: %v\n", err)
	err = CreateEndpointRegionTable(ctx, cli)
	fmt.Printf("CreateEndpointRegionTable: %v\n", err)
	err = CreateEndpointPublishRegionTable(ctx, cli)
	fmt.Printf("CreateEndpointPublishRegionTable: %v\n", err)

	app1 := entity.DataApp{
		ID:            1,
		OrgId:         2,
		ProjectId:     3,
		ExternalAppID: "app1",
		AppName:       "app_cs",
	}
	err = PutDataApp(ctx, cli, &app1)
	require.NoError(t, err)

	getApp, err := GetDataAppByExtId(ctx, cli, app1.ExternalAppID)
	require.NoError(t, err)
	require.Equal(t, app1, *getApp)

	getApp, err = GetDataAppByExtId(ctx, cli, "not_exist")
	require.Equal(t, errs.ErrDataappNotFound, err)

	endpoint1 := entity.Endpoint{
		ID:          1,
		DataappId:   1,
		Name:        "test-endpoint",
		Type:        "draft",
		Endpoint:    "/test1",
		Method:      "get",
		ClusterId:   1000,
		Database:    "test",
		SqlTemplate: "select * from t;",
	}
	err = PutEndpoint(ctx, cli, &endpoint1)
	require.NoError(t, err)

	getEndpoint, err := GetEndpoint(ctx, cli, endpoint1.ID)
	require.NoError(t, err)
	require.Equal(t, endpoint1, *getEndpoint)

	err = DeleteEndpoint(ctx, cli, endpoint1.ID)
	require.NoError(t, err)

	getEndpoint, err = GetEndpoint(ctx, cli, endpoint1.ID)
	require.Equal(t, errs.ErrEndpointNotFound, err)

	getEndpoint, err = GetEndpoint(ctx, cli, 123456789)
	require.Equal(t, errs.ErrEndpointNotFound, err)

	endpointPublish := entity.EndpointPublish{
		ID:          1,
		DataappId:   2,
		EndpointId:  3,
		Version:     "v1",
		Type:        "type-0",
		Endpoint:    "/test-1",
		Method:      "get",
		ClusterId:   40093,
		Database:    "test",
		SqlTemplate: "select * from t2;",
		CreatedBy:   "chenshuang@pingcap.com",
	}
	err = PutPublishedEndpoint(ctx, cli, &endpointPublish)
	require.NoError(t, err)

	getPublishedEndpoints, err := GetPublishedEndpointByName(ctx, cli, endpointPublish.DataappId, endpointPublish.Endpoint)
	require.NoError(t, err)
	require.Equal(t, 1, len(getPublishedEndpoints))
	require.Equal(t, endpointPublish, *getPublishedEndpoints[0])

	getPublishedEndpoints, err = GetPublishedEndpointByName(ctx, cli, endpointPublish.DataappId, "not_exist")
	require.NoError(t, err)
	require.Equal(t, 0, len(getPublishedEndpoints))
}

func newDynamoDBClientForTestByRegion(ctx context.Context, region string) (*dynamodb.Client, error) {
	roleArn := "arn:aws:iam::385595570414:role/dev-staging-data-api-dynamodb-role"
	roleSessionName := "dev-role-session-name"
	return ddb.NewAssumeDynamoDBClient(ctx, region, roleArn, roleSessionName)
}
