package regional_util_service

import (
	"fmt"
	"net/http"
	"strconv"
	"tcidm/commons/config"
	"tcidm/commons/http/tidb_management_service"
	"tcidm/internal/dto"
	"tcidm/internal/entity"
	service "tcidm/internal/service/regional_util_service"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	regionalpb "github.com/tidbcloud/tidb-management-service/api/spec/regional/dedicated/v1"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

// @Time    : 2024/8/21 15:12
// @Author  : lvhongmeng

// @BasePath /api/v1/tcidm

// Check Azure Blob Access godoc
// @Summary Check Azure Blob Access
// @Schemes
// @Description Check Azure Blob Access
// @Tags regional_util_service
// @Accept json
// @Produce json
// @Success 200 {object} dto.SuccessRes
// @Router /azblob/check_access [post]
func CheckAzblobAccess(c *gin.Context) {
	ctx := c.Request.Context()

	timeOut := c.GetHeader(entity.TimeoutHeader)
	timeOutInt, err := strconv.ParseInt(timeOut, 10, 64)
	if err != nil {
		tilog.Warn(ctx, "Convert timeout failed!", zap.String("timeout", timeOut))
		timeOutInt = dto.DefaultAgentTimeOut
	}
	input := &entity.CheckAzblobAccessParam{}
	err = c.ShouldBindBodyWith(input, binding.JSON)
	if err != nil {
		tilog.Error(ctx, "CheckAzblobAccess param bind failed!", zap.Any("param", input), zap.Error(err))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	tilog.Info(ctx, "CheckAzblobAccess payload", zap.Any("input", input))

	resp, err := service.CheckAzblobAccess(ctx, input.ClusterId, timeOutInt, input.CheckAzblobAccess)
	if err != nil {
		c.Error(err)
		tilog.Error(ctx, fmt.Sprintf("CheckAzblobAccess Failed!"), zap.Error(err))
		return
	}
	res := &dto.CheckAzblobAccessRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, res)
}

// Check S3 Access godoc
// @Summary Check S3 Access
// @Schemes
// @Description Check S3 Access
// @Tags regional_util_service
// @Param request_body body entity.CheckS3AccessParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.CheckS3AccessRes
// @Router /s3/check_access [post]
func CheckS3Access(c *gin.Context) {
	ctx := c.Request.Context()

	timeOut := c.GetHeader(entity.TimeoutHeader)
	timeOutInt, err := strconv.ParseInt(timeOut, 10, 64)
	if err != nil {
		tilog.Warn(ctx, "Convert timeout failed!", zap.String("timeout", timeOut))
		timeOutInt = dto.DefaultAgentTimeOut
	}
	input := &entity.CheckS3AccessParam{}
	err = c.ShouldBindBodyWith(input, binding.JSON)
	if err != nil {
		tilog.Error(ctx, "CheckS3Access param bind failed!", zap.Any("param", input), zap.Error(err))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	tilog.Info(ctx, "CheckS3Access payload", zap.Any("input", input))

	resp, err := service.CheckS3Access(ctx, input.ClusterId, timeOutInt, input.CheckS3Access)
	if err != nil {
		c.Error(err)
		tilog.Error(ctx, fmt.Sprintf("CheckS3Access Failed!"), zap.Error(err))
		return
	}
	res := &dto.CheckS3AccessRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, res)
}

// Check GCS Access godoc
// @Summary Check GCS Access
// @Schemes
// @Description Check GCS Access
// @Tags regional_util_service
// @Param request_body body entity.CheckGcsAccessParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.CheckGcsAccessRes
// @Router /gcs/check_access [post]
func CheckGcsAccess(c *gin.Context) {
	ctx := c.Request.Context()

	timeOut := c.GetHeader(entity.TimeoutHeader)
	timeOutInt, err := strconv.ParseInt(timeOut, 10, 64)
	if err != nil {
		tilog.Warn(ctx, "Convert timeout failed!", zap.String("timeout", timeOut))
		timeOutInt = dto.DefaultAgentTimeOut
	}
	input := &entity.CheckGcsAccessParam{}
	err = c.ShouldBindBodyWith(input, binding.JSON)
	if err != nil {
		tilog.Error(ctx, "CheckGcsAccess param bind failed!", zap.Any("param", input), zap.Error(err))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	tilog.Info(ctx, "CheckGcsAccess payload", zap.Any("input", input))

	resp, err := service.CheckGcsAccess(ctx, input.ClusterId, timeOutInt, input.CheckGcsAccess)
	if err != nil {
		c.Error(err)
		tilog.Error(ctx, fmt.Sprintf("CheckGcsAccess Failed!"), zap.Error(err))
		return
	}
	res := &dto.CheckGcsAccessRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, res)
}

// Get Cluster Tables godoc
// @Summary Get Cluster Tables
// @Schemes
// @Description Get Cluster Tables
// @Tags regional_util_service
// @Param request_body body entity.GetClusterTablesParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GetClusterTablesRes
// @Router /cluster-tables [post]
func GetClusterTables(c *gin.Context) {
	ctx := c.Request.Context()

	timeOut := c.GetHeader(entity.TimeoutHeader)
	timeOutInt, err := strconv.ParseInt(timeOut, 10, 64)
	if err != nil {
		tilog.Warn(ctx, "Convert timeout failed!", zap.String("timeout", timeOut))
		timeOutInt = dto.DefaultAgentTimeOut
	}
	input := &entity.GetClusterTablesParam{}
	err = c.ShouldBindBodyWith(input, binding.JSON)
	if err != nil {
		tilog.Error(ctx, "GetClusterTables param bind failed!", zap.Any("param", input), zap.Error(err))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	tilog.Info(ctx, "GetClusterTables payload", zap.Any("input", input))

	resp, err := service.GetClusterTables(ctx, input.Cluster.ClusterId, timeOutInt, input.Cluster)
	if err != nil {
		c.Error(err)
		tilog.Error(ctx, fmt.Sprintf("GetClusterTables Failed!"), zap.Error(err))
		return
	}
	res := &dto.GetClusterTablesRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, res)
}

// Exec SQL On Cluster godoc
// @Summary Exec SQL On Cluster
// @Schemes
// @Description Exec SQL On Cluster
// @Tags regional_util_service
// @Param request_body body entity.ExecSQLOnClusterParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.ExecSQLOnClusterRes
// @Router /cluster/execsql [post]
func ExecSQLOnCluster(c *gin.Context) {
	ctx := c.Request.Context()

	timeOut := c.GetHeader(entity.TimeoutHeader)
	timeOutInt, err := strconv.ParseInt(timeOut, 10, 64)
	if err != nil {
		tilog.Warn(ctx, "Convert timeout failed!", zap.String("timeout", timeOut))
		timeOutInt = dto.DefaultAgentTimeOut
	}
	input := &entity.ExecSQLOnClusterParam{}
	err = c.ShouldBindBodyWith(input, binding.JSON)
	if err != nil {
		tilog.Error(ctx, "ExecSQLOnCluster param bind failed!", zap.Any("param", input), zap.Error(err))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	tilog.Info(ctx, "ExecSQLOnCluster payload", zap.Any("input", input))

	resp, err := service.ExecSQLOnCluster(ctx, input.ExecSQL.Cluster.GetClusterId(), timeOutInt, input.ExecSQL)
	if err != nil {
		c.Error(err)
		tilog.Error(ctx, fmt.Sprintf("ExecSQLOnCluster Failed!"), zap.Error(err))
		return
	}
	res := &dto.ExecSQLOnClusterRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, res)
}

// ListVpcs godoc
// @Summary List Vpcs
// @Schemes
// @Description List Vpcs
// @Tags regional_util_service
// @Accept json
// @Produce json
// @Success 200 {object} dto.ListVpcsRes
// @Router /vpc/list [post]
func ListVpcs(c *gin.Context) {
	ctx := c.Request.Context()

	req := &regionalpb.ListVpcsRequest{}
	err := c.ShouldBindBodyWith(req, binding.JSON)
	if err != nil {
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}

	regionalHost := config.Global().ServerConfig.TiDBManagementServiceEndpoint
	clusterServiceHTTPClient := tidb_management_service.NewVpcServiceHTTPClient(regionalHost)

	resp, err := clusterServiceHTTPClient.ListVpcs(ctx, req)
	if err != nil {
		c.Error(err)
		return
	}

	res := &dto.ListVpcsRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, res)
}

// GetClusterRole
// @Summary get cluster role
// @Schemes
// @Tags regional_util_service
// @Accept json
// @Produce json
// @Success 200 {object} dto.ClusterRoleRes
// @Router /cluster/role [get]
func GetClusterRole(c *gin.Context) {
	ctx := c.Request.Context()

	clusterID := c.Query("cluster_id")
	clusterIDUint64, err := strconv.ParseUint(clusterID, 10, 64)
	if err != nil {
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	} else if clusterIDUint64 == 0 {
		c.Error(errs.NewBadRequest("Bind Params Failed", fmt.Errorf("cluster_id is 0")))
		return
	}

	clusterRole, err := service.GetClusterRole(ctx, clusterIDUint64)
	if err != nil {
		c.Error(err)
		return
	}

	res := &dto.ClusterRoleRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     clusterRole,
	}
	c.JSON(http.StatusOK, res)
}
