package auth

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	accountpb "github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/ecosystem-service/internal/service/grpc/mgmt"
	mgmtapi "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	tiinf_auth "github.com/tidbcloud/tiinf/sdk/auth"
	"go.uber.org/zap"
)

var (
	DefaultRBACPathPattern2ResourceMap = map[string]string{
		"post:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/vercel/connections":   "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/vercel/connections/create",
		"delete:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/vercel/connections": "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/vercel/connections/delete",
		"get:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/vercel/connections":    "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/vercel/connections/list",
		"get:/ecosystem/api/v1/orgs/(\\d+)/vercel/connections":                                    "orgs/{org_id}/vercel/connections/list",
		"post:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/serverless/clusters":                  "orgs/{org_id}/projects/{project_id}/serverless/clusters/create",
		"post:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/dataapps/(\\d+)/vercel/connections":   "orgs/{org_id}/projects/{project_id}/dataapps/{data_app_id}/vercel/connections/create",
		"delete:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/dataapps/(\\d+)/vercel/connections": "orgs/{org_id}/projects/{project_id}/dataapps/{data_app_id}/vercel/connections/delete",
		"post:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/databases":            "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/databases/create",
		"get:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/databases":             "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/databases/list",

		"post:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/git/connections":   "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/git/connections/create",
		"delete:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/git/connections": "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/git/connections/delete",
		"get:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/git/connections":                    "orgs/{org_id}/projects/{project_id}/git/connections/list",
		"get:/ecosystem/api/v1/orgs/(\\d+)/projects/(\\d+)/clusters/(\\d+)/git/connections":    "orgs/{org_id}/projects/{project_id}/tidbs/{cluster_id}/git/connections/get",
	}

	RBACPathWhitelist = []string{
		"/ecosystem/api/v1/connect/info",
		"/ecosystem/api/v1/vercel/access_token",
		"/ecosystem/api/v1/vercel/integration/configurations/(\\S+)/connections",
		"/ecosystem/api/v1/vercel/integration/configurations/(\\S+)/projects",
		"/ecosystem/api/v1/integration/github/search-repo-v2",
		"/ecosystem/api/v1/integration/github/accounts",
		"/ecosystem/api/v1/integration/github/exchange-token",

		"/ecosystem/api/internal/connect/info",
		"/ecosystem/api/internal/pingchat/chat",
	}

	PathsMapNoAuth = map[string]bool{
		"/ecosystem/api/v1/vercel/webhooks": true,
		"/metrics":                          true,
		"/ecosystem/api/v1/integration/github/webhooks": true,
	}
)

func CheckPathIDs(c *gin.Context, mgmtSvc mgmt.MgmtSvc) error {
	orgID := c.Param("org_id")
	projectID := c.Param("project_id")

	if clusterID := c.Param("cluster_id"); clusterID != "" {
		cID, err := strconv.ParseUint(clusterID, 10, 64)
		if err != nil {
			return errcode.NewForbiddenErr(fmt.Errorf("invalid cluster id"))
		}

		cluster, err := mgmtSvc.GetClusterIncludeDedicated(c.Request.Context(), cID, mgmtapi.ClusterFilter_CLUSTER_VIEW_LOCAL)
		if err != nil {
			log.Error("get cluster error", zap.Error(err))
			return errcode.NewForbiddenErr(fmt.Errorf("failed to get cluster"))
		}

		if orgID != "" {
			oID, err := strconv.ParseUint(orgID, 10, 64)
			if err != nil {
				return errcode.NewForbiddenErr(fmt.Errorf("invalid org id"))
			}
			if oID != cluster.OrganizationId {
				return errcode.NewForbiddenErr(fmt.Errorf("path org id not match with cluster's org id"))
			}
		}

		if projectID != "" {
			pID, err := strconv.ParseUint(projectID, 10, 64)
			if err != nil {
				return errcode.NewForbiddenErr(fmt.Errorf("invalid project id"))
			}
			if pID != cluster.ProjectId {
				return errcode.NewForbiddenErr(fmt.Errorf("path project id not match with cluster's project id"))
			}
		}
		log.Debug("check cluster success")
	}
	return nil
}

func GetUserFromContext(ctx *gin.Context) (*accountpb.User, error) {
	identityInfo, err := tiinf_auth.GetIdentityInfoFromGinContext(ctx)
	if err != nil {
		return nil, errors.Trace(err)
	}
	if identityInfo.IdentityType != tiinf_auth.AuthnIdentityTypeUserToken {
		return nil, errors.New("Invalid identity type")
	}
	if identityInfo.UserTokenRsp == nil {
		return nil, errors.New("nil api key response")
	}

	return identityInfo.UserTokenRsp.User, nil
}
