package k8s

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"k8s.io/apimachinery/pkg/util/net"
	"k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
)

const (
	// DefaultHTTPTimeout is the default timeout for HTTP requests through Kubernetes proxy
	// This prevents requests from hanging indefinitely when the target pod/cluster is unavailable
	DefaultHTTPTimeout = 60 * time.Second
)

// HttpLinkByPodProxy implements HttpLink using Kubernetes Pod proxy.
// Unlike HttpLinkByKubeProxy which proxies through Services, this directly
// proxies to individual Pods, allowing precise control over which Pod receives the request.
//
// This is useful when you need to send requests to specific Pods in a StatefulSet
// rather than load-balancing across all replicas via a Service.
type HttpLinkByPodProxy struct {
	cli     kubernetes.Interface
	ns      string
	podName string
	port    string
	ctx     context.Context
}

// NewHttpLinkByPodProxy creates a new HttpLink that proxies directly to a specific Pod.
func NewHttpLinkByPodProxy(ctx context.Context, cli kubernetes.Interface, ns, podName, port string) *HttpLinkByPodProxy {
	return &HttpLinkByPodProxy{
		cli:     cli,
		ns:      ns,
		podName: podName,
		port:    port,
		ctx:     ctx,
	}
}

// Get performs a GET request to the Pod via Kubernetes API proxy
func (p *HttpLinkByPodProxy) Get(path string) ([]byte, error) {
	// Create a context with timeout to prevent hanging when the pod/cluster is unavailable
	ctx, cancel := context.WithTimeout(p.ctx, DefaultHTTPTimeout)
	defer cancel()

	resp := p.cli.CoreV1().Pods(p.ns).ProxyGet("", p.podName, p.port, path, nil)

	body, err := resp.DoRaw(ctx)
	if err != nil {
		return nil, errors.AddStack(err)
	}
	return body, nil
}

// Post performs a POST request to the Pod via Kubernetes API proxy
func (p *HttpLinkByPodProxy) Post(path string, data interface{}) ([]byte, error) {
	// Create a context with timeout to prevent hanging when the pod/cluster is unavailable
	ctx, cancel := context.WithTimeout(p.ctx, DefaultHTTPTimeout)
	defer cancel()

	request := p.makeRequest(path, fmt.Sprint(data))
	body, err := request.DoRaw(ctx)
	if err != nil {
		log.S().Errorf("POST request to pod %s failed: %s, data: %s", p.podName, err, data)
		return nil, err
	}

	return body, nil
}

// makeRequest creates a REST request that proxies to the Pod
func (p *HttpLinkByPodProxy) makeRequest(api string, body string) restclient.ResponseWrapper {
	// Build the proxy request:
	// POST /api/v1/namespaces/{ns}/pods/{podName}:{port}/proxy/{api}
	// IMPORTANT: Set Content-Type to application/x-www-form-urlencoded for form data
	request := p.cli.CoreV1().RESTClient().Post().
		Namespace(p.ns).
		Resource("pods").
		SubResource("proxy").
		Name(net.JoinSchemeNamePort("", p.podName, p.port)).
		Suffix(api).
		SetHeader("Content-Type", "application/x-www-form-urlencoded").
		Body(strings.NewReader(body))

	return request
}
