package repository_ddb

import (
	"context"
	"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"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

const customDomainIndexDomainName = "idx_custom_domain_name"

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

func PutCustomDomain(ctx context.Context, cli *dynamodb.Client, info *entity.CustomDomain) error {
	err := ddb.PutItem(ctx, cli, customDomainTable(), info)
	if err != nil {
		tilog.Error(ctx, "PutEndpoint failed", zap.Error(err))
	}
	return err
}

func DeleteCustomDomain(ctx context.Context, cli *dynamodb.Client, customDomainId uint64) error {
	value, err := attributevalue.Marshal(customDomainId)
	if err != nil {
		return errs.NewInternalDB("Failed to marshal customDomainId", err)
	}
	key := map[string]types.AttributeValue{"id": value}
	return ddb.DeleteItem(ctx, cli, customDomainTable(), key)
}

func GetCustomDomain(ctx context.Context, cli *dynamodb.Client, customDomainName, externalAppId string) (*entity.CustomDomain, error) {
	exp := expression.Key("custom_domain_name").Equal(expression.Value(customDomainName)).And(
		expression.Key("external_app_id").Equal(expression.Value(externalAppId)))
	expr, err := expression.NewBuilder().WithKeyCondition(exp).Build()
	if err != nil {
		return nil, err
	}
	response, err := cli.Query(ctx, &dynamodb.QueryInput{
		TableName:                 aws.String(customDomainTable()),
		IndexName:                 aws.String(customDomainIndexDomainName),
		KeyConditionExpression:    expr.KeyCondition(),
		ExpressionAttributeNames:  expr.Names(),
		ExpressionAttributeValues: expr.Values(),
	})
	if err != nil {
		return nil, err
	}

	var domains []*entity.CustomDomain
	err = attributevalue.UnmarshalListOfMaps(response.Items, &domains)
	if err != nil {
		return nil, err
	} else if len(domains) == 0 {
		return nil, errs.NewNotFound("not found", nil)
	}
	return domains[0], nil
}

func ListCustomDomains(ctx context.Context, cli *dynamodb.Client, customDomainName string) ([]*entity.CustomDomain, error) {
	exp := expression.Key("custom_domain_name").Equal(expression.Value(customDomainName))
	expr, err := expression.NewBuilder().WithKeyCondition(exp).Build()
	if err != nil {
		return nil, err
	}
	response, err := cli.Query(ctx, &dynamodb.QueryInput{
		TableName:                 aws.String(customDomainTable()),
		IndexName:                 aws.String(customDomainIndexDomainName),
		KeyConditionExpression:    expr.KeyCondition(),
		ExpressionAttributeNames:  expr.Names(),
		ExpressionAttributeValues: expr.Values(),
	})
	if err != nil {
		return nil, err
	}

	var domains []*entity.CustomDomain
	err = attributevalue.UnmarshalListOfMaps(response.Items, &domains)
	if err != nil {
		return nil, err
	}
	return domains, nil
}
