package tidbtools

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"

	"google.golang.org/grpc/metadata"
)

// dataflowOpsUserKey is the gRPC metadata header key read by the
// dataflow-service UserPasserUnaryServerInterceptor.
// It matches appconfig.GrpcGatewayDataflowUserKey (lower-cased):
//
//	"Grpc-Metadata-X-Dataflow-User" → "grpc-metadata-x-dataflow-user"
//
// See: dataflow-service/app/controllers/interceptor/user_passer.go
const dataflowOpsUserKey = "grpc-metadata-x-dataflow-user"

// dataflowOpsProject is a minimal stand-in for accounttypes.Project.
// Only ID is required; it must match the cluster's projectID so that
// base.UserContext.CheckProjectPermission passes for DM / Import.
type dataflowOpsProject struct {
	ID uint64 `json:"ID"`
}

// dataflowOpsUser is a minimal stand-in for accountdb.User that satisfies
// the dataflow-service gRPC auth interceptors.
//
//   - IsImpersonated=true bypasses org/project checks in the backup and
//     import services that use account.CheckOrgIDAndProjectID.
//   - Projects must include the target projectID for DM services that
//     use base.UserContext.CheckProjectPermission (no IsImpersonated bypass).
type dataflowOpsUser struct {
	TenantID       uint64               `json:"TenantID"`
	IsImpersonated bool                 `json:"IsImpersonated"`
	Projects       []dataflowOpsProject `json:"Projects"`
}

// withDataflowOpsUser enriches ctx with an impersonated ops-user in the
// gRPC outgoing metadata.  The dataflow-service server-side interceptor
// reads this header and populates auth0.AuthTokenClaimKey in the handler
// context, satisfying calls to account.GetUserFromContext.
//
// Must be called before any dataflow-service gRPC method whose handler
// invokes account.GetUserFromContext (DM, Import, BackupSvc.ListLogBackups,
// BackupSvc.GetRestoreProgress).
func withDataflowOpsUser(ctx context.Context, orgID, projectID uint64) (context.Context, error) {
	user := dataflowOpsUser{
		TenantID:       orgID,
		IsImpersonated: true,
		Projects:       []dataflowOpsProject{{ID: projectID}},
	}
	b, err := json.Marshal(user)
	if err != nil {
		return ctx, fmt.Errorf("marshal dataflow ops user: %w", err)
	}
	encoded := base64.StdEncoding.EncodeToString(b)
	return metadata.AppendToOutgoingContext(ctx, dataflowOpsUserKey, encoded), nil
}
