package cost

import (
	"fmt"
	"math/big"
	"strings"

	"cloud.google.com/go/bigquery"
	"github.com/pingcap/log"
	"github.com/spf13/viper"
	"github.com/tidbcloud/cost-collect/pkg/global"
	"go.uber.org/zap"
)

const (
	AWSUserDefinedLabelPrefix = "resource_tags_user_"
)

func awsDataExtract(row map[string]bigquery.Value, request global.Request) ([]*global.Item, error) {
	var out []*global.Item

	item := global.Item{
		StartTime: request.StartTime,
		EndTime:   request.EndTime,
	}

	labels := map[string]string{}
	labelPrefix := AWSUserDefinedLabelPrefix
	amountKey := "line_item_usage_amount"
	costKey := "line_item_unblended_cost"

	for key, rawValue := range row {
		value := fmt.Sprintf("%v", rawValue)

		item.DataVersion = DataVersionV2

		if strings.HasPrefix(key, labelPrefix) {
			key := strings.TrimPrefix(key, labelPrefix)
			if key != "usedby" && key != "servicetype" {
				key += "_id"
			}
			labels[key] = value
		} else {
			switch key {
			case costKey: // cost
				r := new(big.Rat)
				r, ok := r.SetString(value)
				if !ok {
					return nil, fmt.Errorf("failed to convert cost to big.Rat, value=%s", value)
				}
				item.Cost, _ = r.Float64()
			case amountKey: // amount
				r := new(big.Rat)
				r, ok := r.SetString(value)
				if !ok {
					return nil, fmt.Errorf("failed to convert amount to big.Rat, value=%s", value)
				}
				item.Amount, _ = r.Float64()
			case "pricing_unit":
				item.Unit = value
			case "service_name":
				item.ServiceName = value
			case "line_item_operation":
				item.Description = value
			case "product_region_code":
				item.Location = value
			}
		}
	}

	err := global.Coverage(labels, &item.Labels)
	if err != nil {
		return nil, err
	}
	item.Labels.Provider = "aws"
	item.Labels.UsedBy = "customer" // compatibility old tags, it uses customer-tidb

	// classify for biz flag
	if strings.HasPrefix(item.ServiceName, "AWSELB") || strings.HasPrefix(item.ServiceName, "AmazonEC2") {
		item.ResourceType = global.CloudTypeNetwork

		// for resource subtype
		if strings.HasPrefix(item.Description, "InterZone-") || strings.HasPrefix(item.Description, "VPCPeering-") {
			// cross az or use peering connect in same region
			item.ResourceSubtype = global.CloudNetworkSubTypeCrossZone
		} else if strings.HasPrefix(item.Description, "LoadBalancing-PublicIP-") || strings.HasPrefix(item.Description, "LoadBalancing-NLB-PublicIP-") {
			// internet
			item.ResourceSubtype = global.CloudNetworkSubTypeInternet
		} else if item.Description == "LoadBalancing" || strings.HasPrefix(item.Description, "LoadBalancing:") {
			// load balancer
			item.ResourceSubtype = global.CloudNetworkSubTypeLoadBalancer

			ipv4Item := buildIPv4Item(item)
			if ipv4Item != nil {
				enableIPv4Metering := viper.GetBool("server.enable_ipv4_metering")
				if enableIPv4Metering {
					log.Info("IPv4 cost usage report", zap.Any("item", ipv4Item))
					out = append(out, ipv4Item)
				} else {
					log.Info("IPv4 metering is disabled")
				}
			}
		} else {
			log.S().Errorf("unexpected resourceSubType in Network, description=%s", item.Description)
		}
	} else if strings.HasPrefix(item.ServiceName, "AmazonS3") {
		item.ResourceType = global.CloudTypeStorage
	} else {
		log.S().Errorf("unexpected serviceName, name=%s", item.ServiceName)
	}

	out = append(out, &item)
	return out, nil
}

// buildIPv4Item builds the charge item for IPv4 address, as the cost usage report in cloud provider has no billing labels.
// So we need to build the item by ourselves. The charge is 0.005 per IP per hour, and 3 IPs per NLB, Hrs amount is the same as NLB.
func buildIPv4Item(item global.Item) *global.Item {
	if strings.HasPrefix(item.ServiceName, "AWSELB") &&
		strings.HasPrefix(item.Description, "LoadBalancing:") &&
		item.Unit == "Hrs" &&
		item.Labels.ComponentID == "nlb" &&
		item.Amount > 0 {
		// 3 Public IPv4 Address per NLB
		amount := item.Amount * 3
		cost := global.AwsPublicIPv4AddressPricing * amount

		out := global.Item{
			Labels: global.Labels{
				TenantID:    item.Labels.TenantID,
				ProjectID:   item.Labels.ProjectID,
				ClusterID:   item.Labels.ClusterID,
				ComponentID: item.Labels.ComponentID,
				UsedBy:      item.Labels.UsedBy,
				ServiceType: item.Labels.ServiceType,
				Provider:    item.Labels.Provider,
			},
			Amount:          amount,
			Unit:            "Hrs",
			Cost:            cost,
			ResourceType:    global.CloudTypeNetwork,
			ResourceSubtype: global.CloudNetworkSubTypeIPv4,
			DataVersion:     DataVersionV2,
			ServiceName:     "AmazonVPC:Amazon Virtual Private Cloud",
			Description:     "Hourly charge for In-use Public IPv4 Addresses",
			Location:        item.Location,
			StartTime:       item.StartTime,
			EndTime:         item.EndTime,
		}
		return &out
	}
	return nil
}
