package compat

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc"

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/app/common"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-portal/pkg/config"
)

// Deprecated: SetupCompatApiHandler is deprecated, please use api(s) with "DataPlaneService" tag in mgmt inner api package.
//
//	These http api(s) are already used by other team, we cannot remove them now.
func SetupCompatApiHandler(eng *gin.Engine, timeout time.Duration) error {
	address := config.Global().TiDBMgmtServiceConfig.Address

	if !config.Global().CompatAPI.Enable {
		return nil
	}

	conn, err := common.CreateGrpcConn(address, timeout)
	if err != nil {
		return fmt.Errorf("failed to dial server: %w", err)
	}

	if err := registerHandlers(eng, conn); err != nil {
		return fmt.Errorf("failed to register gateway: %w", err)
	}

	return nil
}

func registerHandlers(eng *gin.Engine, conn *grpc.ClientConn) error {
	gwmux := common.MakeGatewayMux()

	if err := tidbmgmtservicev1.RegisterDataPlaneServiceHandler(context.Background(), gwmux, conn); err != nil {
		return err
	}

	compatApi := eng.Group("/inner/mgmt/compat/v1")

	// Write the old compat http path to new mgmt inner api path
	compatApi.Use(func(ctx *gin.Context) {
		originalPath := ctx.Request.URL.Path
		newPath, err := rewriteOldCompatApiPath(ctx)
		if err != nil {
			tilog.Error(ctx, "failed to rewrite old compat api path", zap.String("path", originalPath))
		}
		tilog.Info(
			ctx,
			"rewrite old compat api path",
			zap.String("old_path", originalPath),
			zap.String("new_path", newPath),
		)
		ctx.Request.URL.Path = newPath
		ctx.Next()
		ctx.Request.URL.Path = originalPath
	})

	compatApi.Any("/*any", gin.WrapF(gwmux.ServeHTTP))

	return nil
}

func rewriteOldCompatApiPath(ctx *gin.Context) (string, error) {
	originalPath := ctx.Request.URL.Path
	method := ctx.Request.Method
	/*
		- /inner/mgmt/compat/v1/clusters/{cluster_id} -> /dataplane/clusters/{cluster_id}
		- /inner/mgmt/compat/v1/networks/{project_id} -> /dataplane/networks/{project_id}/{provider_name}/{region_name}
		- /inner/mgmt/compat/v1/providers/{provider_name} -> /dataplane/providers/{provider_name}
	*/
	switch {
	case method == http.MethodGet && strings.HasPrefix(originalPath, "/inner/mgmt/compat/v1/clusters/"):
		return strings.Replace(originalPath, "/inner/mgmt/compat/v1/clusters/", "/dataplane/clusters/", 1), nil
	case method == http.MethodGet && strings.HasPrefix(originalPath, "/inner/mgmt/compat/v1/networks/"):
		p := strings.Replace(originalPath, "/inner/mgmt/compat/v1/networks/", "/dataplane/networks/", 1)
		// Add provider_name and region_name to the path
		p, err := url.JoinPath(p, ctx.Query("providerName"), ctx.Query("regionName"))
		if err != nil {
			return originalPath, err
		}
		// add provider_name and region_name to the path
		return p, nil
	case method == http.MethodGet && strings.HasPrefix(originalPath, "/inner/mgmt/compat/v1/providers/"):
		return strings.Replace(originalPath, "/inner/mgmt/compat/v1/providers/", "/dataplane/providers/", 1), nil
	default:
		return originalPath, nil
	}
}
