package logredactionpolicy

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

	"github.com/samber/lo"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	tidbgrouprepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/tidbgroup"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

const (
	getLogRedactionPolicySQL       = "show global variables like '%tidb_redact_log%'"
	SetLogRedactionPolicySQL       = "set @@global.tidb_redact_log = %d"
	logRedactionPolicyVariableName = "tidb_redact_log"
)

func (svc *service) GetLogRedactionPolicy(
	ctx context.Context,
	clusterID uint64,
) (*globalngv1.LogRedactionPolicy, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "GetLogRedactionPolicy")
	defer span.End()

	cluster, err := svc.clusterRepo.Get(ctx, clusterID, false)
	if err != nil {
		return nil, err
	}

	region, err := svc.regionRepo.Get(ctx, cluster.RegionId)
	if err != nil {
		return nil, err
	}

	return svc.getLogRedactionPolicy(ctx, cluster, region)
}

func (svc *service) getLogRedactionPolicy(
	ctx context.Context,
	cluster *entity.Cluster,
	region *entity.Region,
) (*globalngv1.LogRedactionPolicy, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "getLogRedactionPolicy")
	defer span.End()

	policy, err := svc.logRedactionPolicyRepo.GetByClusterID(ctx, cluster.ClusterId)
	if err != nil && err.ErrCode() != bizerr.ErrCodeRecordNotFound {
		return nil, err
	}

	// if the policy is already set, return it.
	if policy != nil {
		return policy, nil
	}

	tidbgroups, err := svc.tidbGroupRepo.Query(ctx, &tidbgrouprepo.Filter{
		ClusterIds: []uint64{cluster.ClusterId},
	})
	if err != nil {
		return nil, err
	}

	if len(tidbgroups) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "no tidb group found")
	}

	// use the first tidb group as the log redaction policy.
	// we will make sure all tidb groups have the same log redaction policy.
	tidbGroup := tidbgroups[0]

	// Also gates log redaction policy via agent; flag kept unified to avoid extra rollout plumbing.
	if svc.cfg != nil && svc.cfg.BizConfig.EnableAuditLogViaClusterAgent {
		return svc.GetLogRedactionPolicyViaAgent(ctx, cluster, region, tidbGroup)
	}

	proxy, err := svc.GetPlatformAdminClusterProxy(ctx, cluster, region, tidbGroup)
	if err != nil {
		return nil, err
	}

	sqlResult, err := proxy.QuerySQL(ctx, getLogRedactionPolicySQL)
	if err != nil {
		return nil, err
	}

	type logRedactionResult struct {
		Value []string `json:"Value"`
	}

	var result logRedactionResult
	if err := json.Unmarshal(sqlResult, &result); err != nil {
		return nil, bizerr.WrapError(err, "failed to unmarshal log redaction policy")
	}
	if len(result.Value) != 1 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "failed to get log redaction policy")
	}

	decodeRet, nerr := base64.StdEncoding.DecodeString(result.Value[0])
	if nerr != nil {
		return nil, bizerr.WrapError(nerr, "failed to decode log redaction policy")
	}

	return &globalngv1.LogRedactionPolicy{
		ClusterId: cluster.ClusterId,
		RedactLog: lo.Ternary(
			string(decodeRet) == "OFF" || string(decodeRet) == "off",
			lo.ToPtr(false),
			lo.ToPtr(true),
		),
	}, nil
}
