package billing

import (
	"database/sql"
	"encoding/base64"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/twinj/uuid"
	"github.com/uptrace/opentelemetry-go-extra/otelzap"
	"go.uber.org/zap"

	. "github.com/tidbcloud/devtier-store-service/utils"
)

func GetServerlessClusterNumFromMetrics(logger *zap.Logger, date, step string, active bool) (num string) {
	//logger := otelzap.L().Ctx(ctx)
	logger.Info("GetServerlessClusterNumFromMetrics start...")
	defer logger.Info("GetServerlessClusterNumFromMetrics end...")

	//q := fmt.Sprintf("count(sum_over_time(tidb_server_connections{kubernetes_namespace=~\"tidb-pool|%s.*\"}[%s]))", K8sNsPrefix, dimension)
	q := fmt.Sprintf("count(sum(increase(tidb_executor_statement_total{provider_type=\"aws-free-tier\"}[%s])) by (cluster_id))", step)
	if active {
		//q = fmt.Sprintf("count(sum_over_time(tidb_server_connections{kubernetes_namespace=~\"tidb-pool|%s.*\"}[%s]) > 0)", K8sNsPrefix, dimension)
		q = fmt.Sprintf("count(sum(increase(tidb_executor_statement_total{provider_type=\"aws-free-tier\"}[%s])) by (cluster_id) > 0)", step)
	}

	results, err := DoO11yQueryALL(logger, q, date)
	if err != nil {
		return
	}
	if len(results) == 0 {
		logger.Error("metricResult.Data.Result len 0", zap.Any("url", q))
		return
	}
	number := 0
	for _, result := range results {
		logger.Info("result details", zap.Any("metric", result.Metric), zap.Any("value", result.Value))
		value := result.Value
		if len(value) != 2 {
			logger.Error("metricResult.Data.Result.[0].Value format", zap.Any("error", value))
			continue
		}
		switch value[1].(type) {
		case string:
			n, _ := strconv.Atoi(value[1].(string))
			number += n
		}
	}
	return strconv.Itoa(number)
}

func FetchServerlessTenantAccessInfo() {
	logger := otelzap.L().With(zap.String("trace_id", uuid.NewV1().String()))
	logger.Info("FetchServerlessTenantAccessInfo job start...")
	defer logger.Info("FetchServerlessTenantAccessInfo job end...")
	var (
		date    = time.Now().AddDate(0, 0, -1).Format("2006-01-02")
		key     = fmt.Sprintf("max/tenant_info_active_%s.csv", time.Now().AddDate(0, 0, -1).Format("2006-01"))
		content string
	)
	if strings.HasSuffix(date, "-01") {
		content = "date,tenantId,cluster_id,region,action,type,value,timestmp\n"
		for i := 0; i < 3; i++ {
			err := S3NewObject(logger, Env+"-devtier-billing", key)
			if err == nil {
				break
			}
		}
	}

	var metrics = map[string]string{
		/* tidb_server_connections guage*/
		"tidb_connection": fmt.Sprintf("sum(max_over_time(tidb_server_connections{kubernetes_namespace=~\"tidb-pool|%s.*\"}[1d])) by (tenantId, region, cluster_id) > 0", K8sNsPrefix),
		/* tidb_executor_statement_total counter */
		"tidb_statement": fmt.Sprintf("sum(max_over_time(rate(tidb_executor_statement_total{kubernetes_namespace=~\"tidb-pool|%s.*\"}[1m])[1d:])) by (tenantId, region, cluster_id, type) > 0", K8sNsPrefix),
		/* tiflash_coprocessor_executor_count counter*/
		// "tiflash_executor": fmt.Sprintf("sum(max_over_time(rate(tiflash_coprocessor_executor_count{kubernetes_namespace=~\"tidb-pool|%s.*\"}[1m])[1d:])) by (tenantId, region, cluster_id, type) > 0", K8sNsPrefix),
	}

	now := time.Now().Format("2006-01-02_15:04:05")
	for k, q := range metrics {
		results, _ := DoO11yQueryALL(logger, q, date)
		if len(results) == 0 {
			logger.Error("metricResult.Data.Result len 0", zap.Any("result", q))
			continue
		}
		for _, v := range results {
			valueInt, _ := strconv.ParseFloat(v.Value[1].(string), 64)
			content += fmt.Sprintf("%v,%v,%v,%v,%v,%v,%.2f,%v\n", date, v.Metric.TenantId, v.Metric.ClusterId, v.Metric.Region, k, v.Metric.Type, valueInt, now)
		}
	}

	for i := 0; i < 3; i++ {
		err := S3KeyAppend(logger, Env+"-devtier-billing", key, content)
		if err == nil {
			return
		}
	}
}

func GetServerlessClusterByRegion(logger *zap.Logger) (regionCounts map[string]int, err error) {
	logger.Info("GetServerlessClusterByRegion start...")
	defer logger.Info("GetServerlessClusterByRegion end...", zap.Any("result", regionCounts), zap.Error(err))

	sDec, _ := base64.StdEncoding.DecodeString("ZVNKZjdwSkJZM01EZURW")
	dsn := "prod_serverless_ro:" + string(sDec) + "@tcp(prod-serverless-rds7fb4d62.czw95mtqfw67.us-west-2.rds.amazonaws.com:3306)/serverless_prod"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, err
	}
	defer db.Close()
	if err = db.Ping(); err != nil {
		return nil, err
	}

	query := `SELECT region_name, COUNT(*) FROM clusters GROUP BY region_name`
	regionCounts = make(map[string]int)
	rows, err := db.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var region string
		var count int
		if err = rows.Scan(&region, &count); err != nil {
			return nil, err
		}
		regionCounts[region] = count
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	return regionCounts, nil
}

func GetServerlessActiveClusterFromMetrics(logger *zap.Logger, date, step string) (map[string]int, error) {
	logger.Info("GetServerlessActiveClusterFromMetrics start...")
	defer logger.Info("GetServerlessActiveClusterFromMetrics end...")

	q := fmt.Sprintf("count(sum(increase(gateway_connection_sla_total{cluster_id!=\"\"}[%s])) by (cluster_id) > 0)", step)
	results, err := DoO11yQueryWithRegion(logger, q, date)
	if err != nil {
		logger.Error("query o11y failed", zap.Error(err))
		return nil, err
	}
	if len(results) == 0 {
		logger.Error("metricResult.Data.Result len 0, url=%s", zap.Any("url", q))
		return nil, errors.New("metricResult.Data.Result len 0")
	}
	activeClusters := make(map[string]int)
	for region, result := range results {
		if len(result) == 0 {
			logger.Warn(region, zap.Any("no result", step))
			continue
		}
		logger.Info(region, zap.Any("value", result[0].Value))
		value := result[0].Value
		if len(value) != 2 {
			logger.Error("metricResult.Data.Result.[0].Value format error", zap.Any("error", value))
			continue
		}
		switch value[1].(type) {
		case string:
			n, _ := strconv.Atoi(value[1].(string))
			activeClusters[region] = n
			logger.Info("details value", zap.Any(region, n))
		}
	}
	return activeClusters, nil
}
