package middleware

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type OpenAPIAuthResult struct {
	PublicKey string   `json:"public_key"`
	CIDRs     []string `json:"cidrs,omitempty"`
}

func isApiKeyRequest(c *gin.Context) (bool, string, error) {
	authType := c.GetHeader("X-Auth-Type")
	if authType != "openapi" {
		return false, "", nil
	}
	authContent := c.GetHeader("X-Auth-Content")
	var result OpenAPIAuthResult
	err := json.Unmarshal([]byte(authContent), &result)
	if err != nil {
		return false, "", err
	}
	return true, result.PublicKey, nil
}

func openAPIModifyPath(tenantID uint64, c *gin.Context, router *gin.Engine) {
	if strings.Contains(c.Request.URL.Path, "/v1beta1/msp/") {
		path := fmt.Sprintf("/v1beta1/msp/tenants/%s/", strconv.FormatUint(tenantID, 10))
		c.Request.URL.Path = strings.Replace(c.Request.URL.Path, "/v1beta1/msp/", path, 1)
		router.HandleContext(c)
		c.Abort()
	} else {
		path := fmt.Sprintf("/v1beta1/tenants/%s/", strconv.FormatUint(tenantID, 10))
		c.Request.URL.Path = strings.Replace(c.Request.URL.Path, "/v1beta1/", path, 1)
		router.HandleContext(c)
		c.Abort()
	}
}

func OpenAPIPathNormalize(router *gin.Engine) gin.HandlerFunc {

	return func(c *gin.Context) {
		if strings.Contains(c.Request.URL.Path, "/tenants/") ||
			strings.Contains(c.Request.URL.Path, "/api/v1/billing/") ||
			strings.Contains(c.Request.URL.Path, "/api/v1/unc/") {
			c.Next()
			return
		}
		isApiKeyReq, apiKey, err := isApiKeyRequest(c)
		ctx := c.Request.Context()

		if err != nil {
			c.JSON(http.StatusForbidden, map[string]string{
				"error": "failed to check api key request",
			})
			tilog.Error(ctx, "failed to check api key request", zap.Error(err))
			c.Abort()
			return

		}
		if isApiKeyReq {
			tenantID, err := centralhandler.GetCentralHandler().GetTenantIdByOpenAPIAccess(apiKey)
			if err != nil || tenantID == 0 {
				c.JSON(http.StatusForbidden, map[string]string{
					"error": "failed to get open api access",
				})
				tilog.Error(ctx, "failed to get open api access", zap.Error(err), zap.Uint64("tenantID", tenantID))
				c.Abort()
				return
			}
			openAPIModifyPath(tenantID, c, router)
			return
		} else {
			c.JSON(http.StatusForbidden, map[string]string{
				"error": " isApiKeyReq false but access",
			})
			c.Abort()
			return
		}
		c.Next()

	}

}
