package marketing

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
)

type ClinicHandlerImpl struct{}

func (h *ClinicHandlerImpl) GetTenantClusterIDs(tenantID uint) ([]string, error) {
	log.Info("clinic: GetTenantClusterIDs called", zap.Uint("tenantID", tenantID))

	baseURL := config.Get().GetString(config.PromotionClinicAPIBase)
	apiKey := config.Get().GetString(config.PromotionClinicAPIKey)
	if baseURL == "" || apiKey == "" {
		log.Error("clinic: API not configured",
			zap.Bool("hasBaseURL", baseURL != ""), zap.Bool("hasAPIKey", apiKey != ""))
		return nil, fmt.Errorf("clinic API not configured")
	}

	u := fmt.Sprintf("%s/clinic/api/v1/dashboard/clusters?query=%d&limit=100", baseURL, tenantID)
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		log.Error("clinic: failed to create request", zap.String("url", u), zap.Error(err))
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+apiKey)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Error("clinic: HTTP request failed",
			zap.Uint("tenantID", tenantID), zap.Error(err))
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error("clinic: failed to read response body",
			zap.Uint("tenantID", tenantID), zap.Error(err))
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		log.Error("clinic: API returned non-200 status",
			zap.Uint("tenantID", tenantID),
			zap.Int("statusCode", resp.StatusCode),
			zap.String("body", string(body)))
		return nil, fmt.Errorf("clinic API returned status %d: %s", resp.StatusCode, string(body))
	}

	var result struct {
		Items []struct {
			ClusterID string `json:"clusterID"`
			TenantID  string `json:"tenantID"`
			Status    string `json:"status"`
		} `json:"items"`
	}
	if err := json.Unmarshal(body, &result); err != nil {
		log.Error("clinic: failed to unmarshal response",
			zap.Uint("tenantID", tenantID), zap.Error(err))
		return nil, err
	}

	tenantIDStr := strconv.FormatUint(uint64(tenantID), 10)
	var clusterIDs []string
	for _, item := range result.Items {
		if item.TenantID == tenantIDStr && item.Status == "active" {
			clusterIDs = append(clusterIDs, item.ClusterID)
		}
	}

	log.Info("clinic: GetTenantClusterIDs done",
		zap.Uint("tenantID", tenantID),
		zap.Int("totalItems", len(result.Items)),
		zap.Int("activeClusterCount", len(clusterIDs)))
	return clusterIDs, nil
}

func (h *ClinicHandlerImpl) GetClusterRequests(clusterID string, start, end time.Time) (float64, error) {
	log.Info("clinic: GetClusterRequests called",
		zap.String("clusterID", clusterID),
		zap.Time("start", start), zap.Time("end", end))

	baseURL := config.Get().GetString(config.PromotionClinicAPIBase)
	apiKey := config.Get().GetString(config.PromotionClinicAPIKey)
	if baseURL == "" || apiKey == "" {
		log.Error("clinic: API not configured",
			zap.Bool("hasBaseURL", baseURL != ""), zap.Bool("hasAPIKey", apiKey != ""))
		return 0, fmt.Errorf("clinic API not configured")
	}

	duration := end.Sub(start)
	query := fmt.Sprintf("sum(increase(tidb_server_query_total[%ds]))", int(duration.Seconds()))

	u, err := url.Parse(fmt.Sprintf("%s/data-proxy/metrics/%s/api/v1/query", baseURL, clusterID))
	if err != nil {
		log.Error("clinic: failed to parse URL", zap.String("clusterID", clusterID), zap.Error(err))
		return 0, err
	}
	q := u.Query()
	q.Set("query", query)
	q.Set("time", end.UTC().Format(time.RFC3339))
	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		log.Error("clinic: failed to create metrics request",
			zap.String("clusterID", clusterID), zap.Error(err))
		return 0, err
	}
	req.Header.Set("Authorization", "Bearer "+apiKey)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Error("clinic: metrics HTTP request failed",
			zap.String("clusterID", clusterID), zap.Error(err))
		return 0, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Error("clinic: failed to read metrics response body",
			zap.String("clusterID", clusterID), zap.Error(err))
		return 0, err
	}
	if resp.StatusCode != http.StatusOK {
		log.Error("clinic: metrics API returned non-200 status",
			zap.String("clusterID", clusterID),
			zap.Int("statusCode", resp.StatusCode),
			zap.String("body", string(body)))
		return 0, fmt.Errorf("metrics API returned status %d: %s", resp.StatusCode, string(body))
	}

	var promResp struct {
		Status string `json:"status"`
		Data   struct {
			ResultType string `json:"resultType"`
			Result     []struct {
				Value []interface{} `json:"value"`
			} `json:"result"`
		} `json:"data"`
	}
	if err := json.Unmarshal(body, &promResp); err != nil {
		log.Error("clinic: failed to unmarshal metrics response",
			zap.String("clusterID", clusterID), zap.Error(err))
		return 0, err
	}
	if promResp.Status != "success" {
		log.Error("clinic: metrics query returned non-success status",
			zap.String("clusterID", clusterID),
			zap.String("promStatus", promResp.Status))
		return 0, fmt.Errorf("metrics query failed: status=%s", promResp.Status)
	}

	var total float64
	for _, r := range promResp.Data.Result {
		if len(r.Value) >= 2 {
			if valStr, ok := r.Value[1].(string); ok {
				if val, err := decimal.NewFromString(valStr); err == nil {
					f, _ := val.Float64()
					total += f
				}
			}
		}
	}

	log.Info("clinic: GetClusterRequests done",
		zap.String("clusterID", clusterID),
		zap.Float64("totalRequests", total))
	return total, nil
}
