package paymenthandler

import (
	"errors"
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/marketplacemetering"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/log"
	"github.com/shopspring/decimal"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)

func getAwsDefaultSession() (*session.Session, error) {
	region := viper.GetString(config.AwsMarketplaceRegion)
	sess, err := session.NewSession()
	if err != nil {
		return nil, err
	}
	creds := stscreds.NewCredentials(sess, viper.GetString(config.AWSRole))
	sess = sess.Copy(&aws.Config{Credentials: creds, Region: &region})
	return sess, nil
}

func getAwsMeteringClient() (*marketplacemetering.MarketplaceMetering, error) {
	sess, err := getAwsDefaultSession()
	if err != nil {
		return nil, err
	}

	return marketplacemetering.New(sess), nil
}

type AwsPaymentHandlerImpl struct {
}

const (
	AwsAccountProvider = "aws_account_provider"
)

func (aws *AwsPaymentHandlerImpl) GetUserCode(tenant forms.Tenant) (string, error) {
	if !viper.GetBool(config.AWSPaymentEnable) {
		return fmt.Sprintf("mock_aws_%v", tenant.TenantID), nil
	}
	if tenant.ThirdPartyAccountProvider != AwsAccountProvider {
		return "", billing_errors.NewSystemError(errors.New("invalid third_party_account_handler"))
	}
	return tenant.ThirdPartyAccountCode, nil
}

func (aws *AwsPaymentHandlerImpl) Payable(userCode string) (bool, error) {
	return true, nil
}

func (aws *AwsPaymentHandlerImpl) Pay(userCode string, invoice *forms.Invoice) error {
	if !viper.GetBool(config.AWSPaymentEnable) {
		return nil
	}
	client, err := getAwsMeteringClient()
	if err != nil {
		return err
	}

	productCode := viper.GetString(config.AWSProductCode)
	productUnit := viper.GetString(config.AWSProductUnit)
	productPriceFactor := viper.GetInt(config.AWSProudctUnitPriceFactor)
	quantity := invoice.CostPrice.Mul(decimal.NewFromInt(int64(productPriceFactor))).IntPart()
	now := time.Now()
	awsRecords := []*marketplacemetering.UsageRecord{
		{
			CustomerIdentifier: &userCode,
			Dimension:          &productUnit,
			Quantity:           &quantity,
			Timestamp:          &now,
		},
	}

	input := marketplacemetering.BatchMeterUsageInput{}
	input.SetUsageRecords(awsRecords)
	input.SetProductCode(productCode)

	output, err := client.BatchMeterUsage(&input)
	if err != nil {
		return err
	}
	if len(output.UnprocessedRecords) > 0 {
		unMatchedKeys := make([]string, len(output.UnprocessedRecords))
		for index, record := range output.UnprocessedRecords {
			unMatchedKeys[index] = *record.Dimension
		}
		log.Error("aws marketplace metering usage records unprocessed", zap.Strings("UnprocessedRecords", unMatchedKeys))
	}
	return nil
}
