package dataplane

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	pipetype "github.com/tidbcloud/metrics-integration/global/pkg/helm/pipeline"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const pipelineRuntimeConfigDataKey = "config.yaml"

func RefreshDedicatedClusterCPUConfig(ctx context.Context, restConfig *rest.Config, pipelineID string, components []pipetype.ClusterComponent) error {
	clientset, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		return fmt.Errorf("failed to create kubernetes client: %w", err)
	}

	namespace := fmt.Sprintf("pipeline-%s", pipelineID)
	name := fmt.Sprintf("pipeline-%s-meta-config", pipelineID)
	cm, err := clientset.CoreV1().ConfigMaps(namespace).Get(ctx, name, metav1.GetOptions{})
	if err != nil {
		return fmt.Errorf("failed to get runtime configmap %s/%s: %w", namespace, name, err)
	}
	if cm.Data == nil {
		return fmt.Errorf("runtime configmap %s/%s has no data", namespace, name)
	}

	configYAML, ok := cm.Data[pipelineRuntimeConfigDataKey]
	if !ok {
		return fmt.Errorf("runtime configmap %s/%s missing %s", namespace, name, pipelineRuntimeConfigDataKey)
	}

	patchedYAML, err := PatchDedicatedClusterCPUConfigYAML(configYAML, components)
	if err != nil {
		return err
	}
	cm.Data[pipelineRuntimeConfigDataKey] = patchedYAML

	if _, err := clientset.CoreV1().ConfigMaps(namespace).Update(ctx, cm, metav1.UpdateOptions{}); err != nil {
		return fmt.Errorf("failed to update runtime configmap %s/%s: %w", namespace, name, err)
	}
	return nil
}

func RemoteRefreshDedicatedClusterCPUConfig(ctx context.Context, pipelineID, executorURL string, components []pipetype.ClusterComponent) error {
	requestBody, err := json.Marshal(pipetype.NewDedicatedCPURefreshRequest(pipelineID, components))
	if err != nil {
		return fmt.Errorf("failed to marshal dedicated cpu refresh request: %w", err)
	}

	httpClient := &http.Client{
		Timeout: 5 * time.Minute,
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/api/v1/pipeline/dedicated-cpu", executorURL), bytes.NewBuffer(requestBody))
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send request: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	responseBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("remote dedicated cpu refresh failed with status %d: %s", resp.StatusCode, string(responseBody))
	}
	return nil
}

func PatchDedicatedClusterCPUConfigYAML(configYAML string, components []pipetype.ClusterComponent) (string, error) {
	return pipetype.PatchDedicatedClusterCPUConfigYAML(configYAML, components)
}
