package repoter

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/sqs"
	"github.com/tidbcloud/metering/pkg/common/constants"
	"github.com/tidbcloud/metering/pkg/common/metrics"
	"github.com/tidbcloud/metering/pkg/common/models"
)

const (
	REGION   = "us-west-2"
	EnvToken = "BILLING_TOKEN"
)

var SQSQueueURLMap = map[string]string{
	constants.EnvDev:     "https://sqs.us-west-2.amazonaws.com/385595570414/billing-report-nightly",
	constants.EnvStaging: "https://sqs.us-west-2.amazonaws.com/385595570414/billing-report-staging",
	constants.EnvProd:    "https://sqs.us-west-2.amazonaws.com/380838443567/billing-report-prod",
}

var APIQueueURLMap = map[string]string{
	constants.EnvDev:     "https://inner-central.dev.tidbcloud.com/inner/billing/orgs/{tenant_id}/measures",
	constants.EnvStaging: "https://inner-central.staging.tidbcloud.com/inner/billing/orgs/{tenant_id}/measures",
	constants.EnvProd:    "https://inner-central.prod.tidbcloud.com/inner/billing/orgs/{tenant_id}/measures",
}

type MeasureSQSImpl struct {
	sqsClient     sqsClient
	queueURL      string
	vendor        string
	skipclusterid bool
}

func (m *MeasureSQSImpl) InitMeasureHandler(envType string, vendor string, skipclusterid bool, queueUrl ...string) error {
	if len(envType) < 1 {
		return errors.New("envType param is null")
	}
	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(REGION))
	if err != nil {
		return fmt.Errorf("unable to load SDK config, %v", err)
	}
	m.sqsClient = sqs.NewFromConfig(cfg)
	url, ok := SQSQueueURLMap[envType]
	if !ok {
		return errors.New("envType param  is invalid")
	}
	// use app cfg
	if len(queueUrl) > 0 {
		url = queueUrl[0]
	}
	m.queueURL = url
	m.vendor = vendor
	m.skipclusterid = skipclusterid
	return nil
}
func (m *MeasureSQSImpl) SendMeasure(ctx context.Context, billingMeasure models.BillingReport) error {
	err := m.CheckMeasure(billingMeasure)
	if err != nil {
		return err
	}
	tmp := models.BillingReportSender{
		TenantID:      billingMeasure.TenantID,
		CostID:        billingMeasure.CostID,
		Cost:          billingMeasure.Cost,
		CostTimestamp: billingMeasure.CostTimestamp,
		Unit:          billingMeasure.Unit,
	}
	lables, err := fromjson(billingMeasure.Labels)
	if err != nil {
		return err
	}
	if m.vendor == constants.VendorAzure || m.vendor == constants.VendorGCP {
		// remove cluster labels from lables when vendor is azure or gcp
		if m.skipclusterid {
			delete(lables, constants.TagBillingClusterID)
		}
	}
	tmp.Labels = lables
	jsonData, err := json.Marshal(tmp)
	if err != nil {
		return err
	}
	_, err = m.sqsClient.SendMessage(ctx, &sqs.SendMessageInput{
		MessageBody:  aws.String(string(jsonData)),
		QueueUrl:     aws.String(m.queueURL),
		DelaySeconds: 0,
	})
	if err != nil {
		return err
	}

	return nil
}
func (m *MeasureSQSImpl) CheckMeasure(billingMeasure models.BillingReport) error {
	if billingMeasure.TenantID < 1 {
		return errors.New("TenantID param invalid")
	}
	if len(billingMeasure.CostID) < 1 {
		return errors.New("CostID param invalid")
	}
	if billingMeasure.Cost < 1 {
		return errors.New("cost param invalid")
	}
	if billingMeasure.CostTimestamp < 1 {
		return errors.New("CostTimestamp param invalid")
	}
	if len(billingMeasure.Unit) < 1 {
		return errors.New("unit param invalid")
	}

	return nil
}

type MeasureAPIImpl struct {
	MeasureSQSImpl
	sqsClient httpClient
	apiURL    string
	useSQS    bool
	token     string
}

func (m *MeasureAPIImpl) InitMeasureHandler(envType string, vendor string, skipclusterid, usesqs bool, apiurl ...string) error {
	if usesqs {
		m.useSQS = true
		return m.MeasureSQSImpl.InitMeasureHandler(envType, vendor, skipclusterid, apiurl...)
	}
	m.sqsClient = &http.Client{}
	url, ok := APIQueueURLMap[envType]
	if !ok {
		return errors.New("envType param  is invalid")
	}
	// use app cfg
	if len(apiurl) > 0 {
		m.apiURL = apiurl[0]
	} else {
		m.apiURL = url
	}
	// get token from env
	m.token = os.Getenv(EnvToken)
	m.vendor = vendor
	m.skipclusterid = skipclusterid
	return nil
}

func (m *MeasureAPIImpl) SendMeasure(ctx context.Context, billingMeasure models.BillingReport) error {
	metricslabels := map[string]string{
		constants.MetricsAPIName:         "SendMeasure",
		constants.MetricsAPIResult:       "success",
		constants.MetricsLblProviderType: m.vendor,
	}
	defer func() {
		metrics.MetricsRquestAPICount.With(metricslabels).Inc()
	}()
	if m.useSQS {
		err := m.MeasureSQSImpl.SendMeasure(ctx, billingMeasure)
		if err != nil {
			metricslabels[constants.MetricsAPIResult] = "failed"
			return err
		}
		return nil
	}
	return fmt.Errorf("we only support sqs now")
}

func fromjson(m string) (map[string]string, error) {
	result := make(map[string]string)
	err := json.Unmarshal([]byte(m), &result)
	if err != nil {
		return result, err
	}
	return result, nil
}
