package auth

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/dataflow-service-ng/internal/cmd/bootstrap/server/wrapper"
	"github.com/tidbcloud/dataflow-service-ng/pkg/account/accountdb"
	"github.com/tidbcloud/dataflow-service-ng/pkg/account/accounttypes"
	"github.com/tidbcloud/dataflow-service-ng/pkg/log"
	accountAuth "github.com/tidbcloud/tiinf/sdk/auth"
	"go.uber.org/zap"
)

// OpenAPIMiddlewares is independent middlewares for openapi, including authN and authZ
var OpenAPIMiddlewares = []gin.HandlerFunc{
	func(c *gin.Context) {
		ctx := log.WithLogID(c.Request.Context())
		logger := log.GetLogger(ctx)
		err := SetAuthIDsWithChangefeed(c)
		if err != nil {
			logger.Warn("set auth orgId with changefeed failed", zap.Error(err))
			wrapper.NewAPIError(c, getHttpCode(err), err)
			return
		}
	},
	func(c *gin.Context) {
		ctx := log.WithLogID(c.Request.Context())
		logger := log.GetLogger(ctx)
		err := SetAuthIDsWithTiDBId(c)
		if err != nil {
			logger.Warn("set auth orgId with TiDB failed", zap.Error(err))
		}
	},
	accountAuth.GinGetIdentityInfoInterceptor(&accountAuth.IdentityAuthOption{
		EnableLog: true,
	}),
	accountAuth.GinPermissionCheckInterceptor(&accountAuth.PermissionCheckerCommonParam{
		IdentityF:               accountAuth.GinGetIdentityFunc,
		PathPattern2ResourceMap: openAPIURLResourceMap,
		GenAccessParam4GinF:     GenAccessParam4GinWithPrefixIDs,
	}),
	func(c *gin.Context) {
		ctx := c.Request.Context()
		logger := log.GetLogger(ctx)

		value, ok := c.Get(accountAuth.IdentityInfoKey)
		if !ok {
			logger.Info("identity info not found")
			wrapper.NewAPIError(c, http.StatusUnauthorized, APIKeyPermissionDeniedError)
			return
		}
		identityInfo, ok := value.(*accountAuth.AuthnIdentityInfo)
		if !ok {
			wrapper.NewAPIError(c, http.StatusUnauthorized, APIKeyPermissionDeniedError)
			return
		}
		if identityInfo.IdentityType != accountAuth.AuthnIdentityTypeApiKeyAK {
			logger.Info("unexpected identity type", zap.Any("identityType", identityInfo.IdentityType))
			wrapper.NewAPIError(c, http.StatusUnauthorized, APIKeyPermissionDeniedError)
			return
		}
		publicKey := identityInfo.ApiKeyRsp.ApiKey.AccessKey
		user := &accountdb.User{
			User: accounttypes.User{
				TenantID:    identityInfo.OrgID,
				FirstName:   "",
				LastName:    publicKey,
				Email:       "OpenAPI_" + publicKey,
				Status:      0,
				InvitedAt:   &time.Time{},
				Job:         "",
				PhoneNumber: "",
			},
			TenantID:       identityInfo.OrgID,
			IsImpersonated: false,
		}
		// populate this to make `DataflowUserPasser` work
		c.Set(DataflowDefineUser, user)
		c.Request.Header.Set(OpenAPIPublicKey, publicKey)
	},
	// populate the user info to the request header so that controller can get the user info from gRPC context event if this is via openAPI.
	DataflowUserPasser,
}
