package handlers

import (
	"context"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/dto"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/observability"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/service"
)

// ServerlessParentMetricsHandler handles parent/control-plane metrics requests.
type ServerlessParentMetricsHandler struct {
	service serverlessParentMetricsQueryService
}

type serverlessParentMetricsQueryService interface {
	InstantQuery(ctx context.Context, req *dto.ServerlessParentInstantQueryRequest) *dto.PrometheusResponse
	RangeQuery(ctx context.Context, req *dto.ServerlessParentRangeQueryRequest) *dto.PrometheusResponse
	Series(ctx context.Context, req *dto.ServerlessParentSeriesRequest) *dto.PrometheusResponse
}

// NewServerlessParentMetricsHandler creates a new ServerlessParentMetricsHandler.
func NewServerlessParentMetricsHandler(svc *service.ServerlessParentMetricsService) *ServerlessParentMetricsHandler {
	return &ServerlessParentMetricsHandler{service: svc}
}

// InstantQuery handles GET/POST /data-proxy/serverless-parent-metrics/api/v1/query.
//
// An upstream Prometheus HTTP 422 is returned as a safe structured 422 response.
func (h *ServerlessParentMetricsHandler) InstantQuery(c *gin.Context) {
	obsStart := time.Now()
	rec := &observability.DataProxyRecord{
		Endpoint:  "serverless_parent_metrics_instant",
		Caller:    observability.CallerFromGin(c),
		QueryType: "serverless_parent_metrics_instant",
	}
	defer func() { observability.RecordDataProxy(c, obsStart, rec) }()

	query := c.Query("query")
	if query == "" {
		query = c.PostForm("query")
	}

	var queryTime time.Time
	timeStr := c.Query("time")
	if timeStr == "" {
		timeStr = c.PostForm("time")
	}
	if timeStr != "" {
		var err error
		queryTime, err = parseTime(timeStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, dto.PrometheusResponse{
				Status:    "error",
				ErrorType: "bad_data",
				Error:     "invalid time parameter: " + err.Error(),
			})
			return
		}
	}

	req := &dto.ServerlessParentInstantQueryRequest{
		ControlPlaneInfo: controlPlaneInfoFromRequest(c),
		Query:            query,
		Time:             queryTime,
		Timeout:          c.DefaultQuery("timeout", c.PostForm("timeout")),
	}

	result := h.service.InstantQuery(c.Request.Context(), req)
	applyMetricsRouteObservability(rec, result)
	rec.ErrCode = errCodeFromPrometheusResponse(result)
	c.JSON(metricsHTTPStatusCode(result, false), result)
}

// RangeQuery handles GET/POST /data-proxy/serverless-parent-metrics/api/v1/query_range.
//
// An upstream Prometheus HTTP 422 is returned as a safe structured 422 response.
func (h *ServerlessParentMetricsHandler) RangeQuery(c *gin.Context) {
	obsStart := time.Now()
	rec := &observability.DataProxyRecord{
		Endpoint:  "serverless_parent_metrics_range",
		Caller:    observability.CallerFromGin(c),
		QueryType: "serverless_parent_metrics_range",
	}
	defer func() { observability.RecordDataProxy(c, obsStart, rec) }()

	query := c.Query("query")
	if query == "" {
		query = c.PostForm("query")
	}

	startStr := c.Query("start")
	if startStr == "" {
		startStr = c.PostForm("start")
	}
	start, err := parseTime(startStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, dto.PrometheusResponse{
			Status:    "error",
			ErrorType: "bad_data",
			Error:     "invalid start parameter: " + err.Error(),
		})
		return
	}

	endStr := c.Query("end")
	if endStr == "" {
		endStr = c.PostForm("end")
	}
	end, err := parseTime(endStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, dto.PrometheusResponse{
			Status:    "error",
			ErrorType: "bad_data",
			Error:     "invalid end parameter: " + err.Error(),
		})
		return
	}

	stepStr := c.Query("step")
	if stepStr == "" {
		stepStr = c.PostForm("step")
	}
	step, err := parseDuration(stepStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, dto.PrometheusResponse{
			Status:    "error",
			ErrorType: "bad_data",
			Error:     "invalid step parameter: " + err.Error(),
		})
		return
	}

	req := &dto.ServerlessParentRangeQueryRequest{
		ControlPlaneInfo: controlPlaneInfoFromRequest(c),
		Query:            query,
		Start:            start,
		End:              end,
		Step:             step,
		Timeout:          c.DefaultQuery("timeout", c.PostForm("timeout")),
	}

	result := h.service.RangeQuery(c.Request.Context(), req)
	applyMetricsRouteObservability(rec, result)
	rec.ErrCode = errCodeFromPrometheusResponse(result)
	c.JSON(metricsHTTPStatusCode(result, false), result)
}

// Series handles GET/POST /data-proxy/serverless-parent-metrics/api/v1/series.
//
// An upstream Prometheus HTTP 422 is returned as a safe structured 422 response.
func (h *ServerlessParentMetricsHandler) Series(c *gin.Context) {
	obsStart := time.Now()
	rec := &observability.DataProxyRecord{
		Endpoint:  "serverless_parent_metrics_series",
		Caller:    observability.CallerFromGin(c),
		QueryType: "serverless_parent_metrics_series",
	}
	defer func() { observability.RecordDataProxy(c, obsStart, rec) }()

	matchParams := c.QueryArray("match[]")
	if len(matchParams) == 0 {
		matchParams = c.PostFormArray("match[]")
	}

	var start time.Time
	startStr := c.Query("start")
	if startStr == "" {
		startStr = c.PostForm("start")
	}
	if startStr != "" {
		var err error
		start, err = parseTime(startStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, dto.PrometheusResponse{
				Status:    "error",
				ErrorType: "bad_data",
				Error:     "invalid start parameter: " + err.Error(),
			})
			return
		}
	}

	var end time.Time
	endStr := c.Query("end")
	if endStr == "" {
		endStr = c.PostForm("end")
	}
	if endStr != "" {
		var err error
		end, err = parseTime(endStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, dto.PrometheusResponse{
				Status:    "error",
				ErrorType: "bad_data",
				Error:     "invalid end parameter: " + err.Error(),
			})
			return
		}
	}

	req := &dto.ServerlessParentSeriesRequest{
		ControlPlaneInfo: controlPlaneInfoFromRequest(c),
		Match:            matchParams,
		Start:            start,
		End:              end,
	}

	result := h.service.Series(c.Request.Context(), req)
	applyMetricsRouteObservability(rec, result)
	rec.ErrCode = errCodeFromPrometheusResponse(result)
	c.JSON(metricsHTTPStatusCode(result, false), result)
}

// RegisterRoutes registers serverless parent metrics query routes.
func (h *ServerlessParentMetricsHandler) RegisterRoutes(r *gin.Engine) {
	group := r.Group("/data-proxy/serverless-parent-metrics/api/v1")
	{
		group.GET("/query", h.InstantQuery)
		group.POST("/query", h.InstantQuery)
		group.GET("/query_range", h.RangeQuery)
		group.POST("/query_range", h.RangeQuery)
		group.GET("/series", h.Series)
		group.POST("/series", h.Series)
	}
}

func controlPlaneInfoFromRequest(c *gin.Context) string {
	if value := c.Query("control_plane_info"); value != "" {
		return value
	}
	if value := c.PostForm("control_plane_info"); value != "" {
		return value
	}
	if value := c.GetHeader("control_plane_info"); value != "" {
		return value
	}
	return c.GetHeader("X-Control-Plane-Info")
}
