package controller

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"

	"dataservices/commons/ginparser"
	dto "dataservices/internal/dto/dataapi_control"
	service "dataservices/internal/service/dataapi_control"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

func HandleCreatePrivateLink(c *gin.Context) {
	ctx := c.Request.Context()
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	startTime := time.Now()
	req := &dto.CreatePrivateLinkReq{}
	failResAndLog := func(err errs.Error) {
		c.JSON(err.HTTPCode(), dto.NewErrorToBaseResp(err.HTTPCode(), err.ErrMsg()))
		tilog.Error(ctx, "DS-Region-Dataapi-Control-/projects/:project_id/clusters/:cluster_id/privatelink/create[post]-HandleCreatePrivateLink create private link failed",
			zap.Uint64("project-id", params.ProjectID),
			zap.Uint64("cluster-id", params.ClusterID),
			zap.Any("req", req),
			zap.Duration("cost", time.Since(startTime)),
			zap.Error(err),
			zap.String("user", createdBy))
	}

	err := parseParamFromBody(c, req)
	if err != nil {
		failResAndLog(err)
		return
	}

	// Use a new context here since the req.ctx may time out soon. We need enough time to create private link.
	ctx2, cancel := context.WithTimeout(context.Background(), time.Minute*10)
	defer cancel()
	resp, err := GlobalControlServer.CreatePrivateLink(ctx2, params.ProjectID, params.ClusterID, req.ServiceName)
	if err != nil {
		failResAndLog(err)
		return
	}
	c.JSON(resp.Code, resp)
}

func HandleDeletePrivateLink(c *gin.Context) {
	ctx := c.Request.Context()
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	startTime := time.Now()
	failResAndLog := func(err errs.Error) {
		c.JSON(err.HTTPCode(), dto.NewErrorToBaseResp(err.HTTPCode(), err.ErrMsg()))
		tilog.Error(ctx, "DS-Region-Dataapi-Control-/projects/:project_id/clusters/:cluster_id/privatelink/delete[post]-HandleDeletePrivateLink delete private link failed",
			zap.Uint64("project-id", params.ProjectID),
			zap.Uint64("cluster-id", params.ClusterID),
			zap.Duration("cost", time.Since(startTime)),
			zap.Error(err),
			zap.String("user", createdBy))
	}
	isDeleteClusterStr := c.DefaultQuery("is_delete_cluster", "false")
	isDeleteCluster, _ := strconv.ParseBool(isDeleteClusterStr)

	resp, err := GlobalControlServer.DeletePrivateLink(ctx, params.ProjectID, params.ClusterID, isDeleteCluster)
	if err != nil {
		failResAndLog(err)
		return
	}
	c.JSON(resp.Code, resp)
}

func parseParamFromBody(c *gin.Context, param interface{}) errs.Error {
	if c.ContentType() != binding.MIMEJSON {
		return errs.NewBadRequest("Parse param failed, Only json formatted content is supported", nil)
	}
	err := c.ShouldBindBodyWith(param, binding.JSON)
	if err != nil && err != io.EOF {
		return errs.NewBadRequest(fmt.Sprintf("Parse param failed, error: %v", err.Error()), err)
	}
	return nil
}

func HandleCreateCustomDomain(c *gin.Context) {
	ctx := c.Request.Context()
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	startTime := time.Now()
	failResAndLog := func(err errs.Error) {
		c.JSON(err.HTTPCode(), dto.NewErrorToBaseResp(err.HTTPCode(), err.ErrMsg()))
		tilog.Error(ctx, "DS-Region-Dataapi-Control-/apps/:app_id/custom-domains[post]-HandleCreateCustomDomain failed",
			zap.Uint64("app-id", params.AppID),
			zap.Duration("cost", time.Since(startTime)),
			zap.Error(err),
			zap.String("user", createdBy))
	}

	var req dto.CreateCustomDomainRequest
	err := parseParamFromBody(c, &req)
	if err != nil {
		failResAndLog(err)
		return
	}

	tilog.Info(ctx, "receive create customdomain request", zap.String("host", req.Host), zap.String("cname", req.Cname))
	resp, err := service.KubeCreateCustomDomain(ctx, req.Host, req.Cname)
	if err != nil {
		failResAndLog(err)
		return
	}
	c.JSON(http.StatusOK, resp)
}

func HandleGetCustomDomain(c *gin.Context) {
	ctx := c.Request.Context()
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	startTime := time.Now()
	failResAndLog := func(err errs.Error) {
		c.JSON(err.HTTPCode(), dto.NewErrorToBaseResp(err.HTTPCode(), err.ErrMsg()))
		tilog.Error(ctx, "DS-Region-Dataapi-Control-/apps/:app_id/custom-domains/:host[get]-HandleGetCustomDomain failed",
			zap.Uint64("app-id", params.AppID),
			zap.Duration("cost", time.Since(startTime)),
			zap.Error(err),
			zap.String("user", createdBy))
	}

	tilog.Info(ctx, "receive get customdomain request", zap.String("host", c.Param("host")))
	resp, err := service.KubeGetCustomDomain(ctx, c.Param("host"))
	if err != nil {
		failResAndLog(err)
		return
	}
	c.JSON(http.StatusOK, resp)
}

func HandleDeleteCustomDomain(c *gin.Context) {
	ctx := c.Request.Context()
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	startTime := time.Now()
	failResAndLog := func(err errs.Error) {
		c.JSON(err.HTTPCode(), dto.NewErrorToBaseResp(err.HTTPCode(), err.ErrMsg()))
		tilog.Error(ctx, "DS-Region-Dataapi-Control-/apps/:app_id/custom-domains/:host[delete]-HandleDeleteCustomDomain failed",
			zap.Uint64("app-id", params.AppID),
			zap.Duration("cost", time.Since(startTime)),
			zap.Error(err),
			zap.String("user", createdBy))
	}

	tilog.Info(ctx, "receive delete customdomain request", zap.String("host", c.Param("host")))
	err := service.KubeDeleteCustomDomain(ctx, c.Param("host"))
	if err != nil {
		failResAndLog(err)
		return
	}
	c.JSON(http.StatusOK, dto.BaseResp{Code: http.StatusOK})
}
