package repository_ddb

import (
	"context"
	"dataservices/commons/errs"
	"dataservices/commons/utils"
	"dataservices/ddb"
	"dataservices/internal/entity"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
	tierrs "github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

const (
	datappIndexExternalAppID = "idx_external_app_id"
)

func CreateDataAppTable(ctx context.Context, cli *dynamodb.Client, replicaRegions []string) error {
	createTableInput := genTableSchema(dataappsTable(), types.AttributeDefinition{
		AttributeName: aws.String("id"),
		AttributeType: types.ScalarAttributeTypeN,
	}, nil)
	createTableInput.AttributeDefinitions = append(createTableInput.AttributeDefinitions, types.AttributeDefinition{
		AttributeName: aws.String("external_app_id"),
		AttributeType: types.ScalarAttributeTypeS,
	})
	createTableInput.GlobalSecondaryIndexes = append(createTableInput.GlobalSecondaryIndexes, types.GlobalSecondaryIndex{
		IndexName: aws.String(datappIndexExternalAppID),
		KeySchema: []types.KeySchemaElement{
			{
				AttributeName: aws.String("external_app_id"),
				KeyType:       types.KeyTypeHash,
			},
		},
		Projection: &types.Projection{
			ProjectionType: types.ProjectionTypeAll,
		},
	})
	return createGlobalTable(ctx, cli, createTableInput, replicaRegions)
}

func DeleteDataAppTable(ctx context.Context, cli *dynamodb.Client, replicaRegions []string) error {
	return deleteGlobalTable(ctx, cli, dataappsTable(), replicaRegions)
}

func CreateDataAppRegionTable(ctx context.Context, cli *dynamodb.Client) error {
	createTableInput := &dynamodb.CreateTableInput{
		AttributeDefinitions: []types.AttributeDefinition{{
			AttributeName: aws.String("id"),
			AttributeType: types.ScalarAttributeTypeN,
		}},
		KeySchema: []types.KeySchemaElement{{
			AttributeName: aws.String("id"),
			KeyType:       types.KeyTypeHash,
		}},
		TableName: aws.String(dataappsTable()),
		ProvisionedThroughput: &types.ProvisionedThroughput{
			ReadCapacityUnits:  aws.Int64(10),
			WriteCapacityUnits: aws.Int64(10),
		},
	}
	_, err := ddb.CreateTableForGlobal(ctx, cli, createTableInput)
	return err
}

func GetDataAppByExtId(ctx context.Context, cli *dynamodb.Client, appExtId string) (*entity.DataApp, error) {
	appExtIdExp := expression.Key("external_app_id").Equal(expression.Value(appExtId))
	expr, err := expression.NewBuilder().WithKeyCondition(appExtIdExp).Build()
	if err != nil {
		return nil, err
	}
	response, err := cli.Query(ctx, &dynamodb.QueryInput{
		TableName:                 aws.String(dataappsTable()),
		IndexName:                 aws.String(datappIndexExternalAppID),
		KeyConditionExpression:    expr.KeyCondition(),
		ExpressionAttributeNames:  expr.Names(),
		ExpressionAttributeValues: expr.Values(),
	})
	if err != nil {
		return nil, err
	}

	var apps []*entity.DataApp
	err = attributevalue.UnmarshalListOfMaps(response.Items, &apps)
	if err != nil {
		return nil, err
	} else if len(apps) == 0 {
		return nil, errs.ErrDataappNotFound
	}
	app := apps[0]
	return app, nil
}

func PutDataApp(ctx context.Context, cli *dynamodb.Client, app *entity.DataApp) error {
	err := ddb.PutItem(ctx, cli, dataappsTable(), app)
	if err != nil {
		return tierrs.NewInternalDB("Failed to save dataapp", err)
	}
	return nil
}

func BatchPutDataApp(ctx context.Context, cli *dynamodb.Client, infos []*entity.DataApp) (int, error) {
	written, err := ddb.BatchPutItem(ctx, cli, dataappsTable(), utils.ToGeneralArr(infos...)...)
	if err != nil {
		tilog.Error(ctx, "BatchPutDataApp failed", zap.Error(err))
		tierrs.NewInternalDB("Failed to batch put dataapp", err)
	}
	return written, err
}

func DeleteDataApp(ctx context.Context, cli *dynamodb.Client, dataappID uint64) error {
	dataappIDValue, err := attributevalue.Marshal(dataappID)
	if err != nil {
		return tierrs.NewInternalDB("Failed to marshal dataapp id", err)
	}
	key := map[string]types.AttributeValue{"id": dataappIDValue}
	err = ddb.DeleteItem(ctx, cli, dataappsTable(), key)
	if err != nil {
		return tierrs.NewInternalDB("Failed to delete dataapp", err)
	}
	return nil
}

func UpdateDataAppSetting(ctx context.Context, cli *dynamodb.Client, dataappID uint64, val interface{}) error {
	dataappIDValue, err := attributevalue.Marshal(dataappID)
	if err != nil {
		return tierrs.NewInternalDB("Failed to marshal dataapp id", err)
	}
	key := map[string]types.AttributeValue{"id": dataappIDValue}

	updateMap := map[string]interface{}{
		"settings": val,
	}
	err = ddb.UpdateItem(ctx, cli, dataappsTable(), key, updateMap)
	return err
}
