package ticdc

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

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/log"
	"go.uber.org/zap"
	"google.golang.org/grpc/metadata"
)

const dataflowUserMetadataKey = "grpc-metadata-x-dataflow-user"

type dataflowProject struct {
	ID uint64 `json:"ID"`
}

type dataflowUser struct {
	TenantID       uint64            `json:"TenantID"`
	IsImpersonated bool              `json:"IsImpersonated"`
	Projects       []dataflowProject `json:"Projects"`
}

func withDataflowUser(ctx context.Context, orgID, projectID uint64) (context.Context, error) {
	encodedUser, err := encodeDataflowUser(orgID, projectID)
	if err != nil {
		return ctx, err
	}
	// Preserve transport metadata such as tracing, but always replace the
	// Dataflow identity with the value derived from canonical Clinic metadata.
	md, _ := metadata.FromOutgoingContext(ctx)
	md = md.Copy()
	md.Delete(dataflowUserMetadataKey)
	md.Set(dataflowUserMetadataKey, encodedUser)
	log.WithContextL(ctx).Info("TiCDC upstream authentication metadata prepared",
		zap.String("authentication_method", "grpc_metadata"),
		zap.String("metadata_key", dataflowUserMetadataKey),
		zap.Uint64("tenant_id", orgID),
		zap.Uint64("project_id", projectID),
		zap.Bool("is_impersonated", true),
		zap.Bool("credential_value_logged", false),
	)
	return metadata.NewOutgoingContext(ctx, md), nil
}

func encodeDataflowUser(orgID, projectID uint64) (string, error) {
	payload, err := json.Marshal(dataflowUser{
		TenantID:       orgID,
		IsImpersonated: true,
		Projects:       []dataflowProject{{ID: projectID}},
	})
	if err != nil {
		return "", fmt.Errorf("marshal Dataflow user: %w", err)
	}
	return base64.StdEncoding.EncodeToString(payload), nil
}
