package auditlog

import (
	"context"
	"strconv"

	clusterngv1beta "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	pkgclusterproxy "github.com/tidbcloud/pkgv2/clusterproxy"
	"github.com/tidbcloud/pkgv2/db"
	"go.opentelemetry.io/otel"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/clusterproxy"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/remoteencryptor"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	auditlogrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/auditlog"
	clusterrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster"
	regionrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	tidbgrouprepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/tidbgroup"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

var tracer = otel.Tracer("domain.auditlog")

type Service interface {
	GetAuditLogSetting(ctx context.Context, clusterID uint64) (*globalngv1.AuditLogSetting, bizerr.BizError)
	UpdateAuditLogSetting(
		ctx context.Context,
		config *globalngv1.AuditLogSetting,
		validateOnly bool,
	) (*globalngv1.AuditLogSetting, bizerr.BizError)

	ListAuditLogFilterRules(
		ctx context.Context,
		req *globalngv1.ListAuditLogFilterRulesRequest,
	) ([]*globalngv1.AuditLogFilterRule, bizerr.BizError)
	CreateAuditLogFilterRule(
		ctx context.Context,
		rule *globalngv1.AuditLogFilterRule,
		validateOnly bool,
	) (*globalngv1.AuditLogFilterRule, bizerr.BizError)
	DeleteAuditLogFilterRule(
		ctx context.Context,
		request *globalngv1.DeleteAuditLogFilterRuleRequest,
	) bizerr.BizError
	UpdateAuditLogFilterRule(
		ctx context.Context,
		rule *globalngv1.AuditLogFilterRule,
		validateOnly bool,
	) (*globalngv1.AuditLogFilterRule, bizerr.BizError)
	UpdateAuditLogSettingViaAgent(
		ctx context.Context,
		cluster *entity.Cluster,
		region *entity.Region,
		tidbgroup *entity.TidbGroup,
		setting *globalngv1.AuditLogSetting,
	) bizerr.BizError
	CreateAuditLogFilterRuleViaAgent(
		ctx context.Context,
		cluster *entity.Cluster,
		region *entity.Region,
		tidbgroup *entity.TidbGroup,
		rule *globalngv1.AuditLogFilterRule,
	) bizerr.BizError
	DeleteAuditLogFilterRuleViaAgent(
		ctx context.Context,
		cluster *entity.Cluster,
		region *entity.Region,
		tidbgroup *entity.TidbGroup,
		userExpr string,
	) bizerr.BizError
	UpdateAuditLogFilterRuleViaAgent(
		ctx context.Context,
		cluster *entity.Cluster,
		region *entity.Region,
		tidbgroup *entity.TidbGroup,
		rule *globalngv1.AuditLogFilterRule,
	) bizerr.BizError
	GetPlatformAdminClusterProxy(
		ctx context.Context,
		cluster *entity.Cluster,
		regionEn *entity.Region,
		tidbgroup *entity.TidbGroup,
	) (clusterproxy.ClusterProxy, bizerr.BizError)
}

type service struct {
	clusterRepo            clusterrepo.Repo
	tidbGroupRepo          tidbgrouprepo.Repo
	regionRepo             regionrepo.Repo
	auditLogSettingRepo    auditlogrepo.SettingRepo
	auditLogFilterRuleRepo auditlogrepo.FilterRuleRepo
	regionalServerCli      regionalserver.Client
	clusterProxyProvider   clusterproxy.Provider
	encryptor              remoteencryptor.Client
	wf                     workflow.Workflow
	transaction            db.Transactioner
	resourcePoolRepo       resourcepoolrepo.Repo
}

func NewService(
	clusterRepo clusterrepo.Repo,
	tidbGroupRepo tidbgrouprepo.Repo,
	regionRepo regionrepo.Repo,
	auditLogSettingRepo auditlogrepo.SettingRepo,
	auditLogFilterRuleRepo auditlogrepo.FilterRuleRepo,
	regionalServerCli regionalserver.Client,
	clusterProxyProvider clusterproxy.Provider,
	encryptor remoteencryptor.Client,
	wf workflow.Workflow,
	transaction db.Transactioner,
	resourcePoolRepo resourcepoolrepo.Repo,
) Service {
	return &service{
		clusterRepo:            clusterRepo,
		tidbGroupRepo:          tidbGroupRepo,
		regionRepo:             regionRepo,
		auditLogFilterRuleRepo: auditLogFilterRuleRepo,
		auditLogSettingRepo:    auditLogSettingRepo,
		regionalServerCli:      regionalServerCli,
		clusterProxyProvider:   clusterProxyProvider,
		encryptor:              encryptor,
		wf:                     wf,
		transaction:            transaction,
		resourcePoolRepo:       resourcePoolRepo,
	}
}

func (svc *service) GetPlatformAdminClusterProxy(
	ctx context.Context,
	cluster *entity.Cluster,
	regionEn *entity.Region,
	tidbgroup *entity.TidbGroup,
) (clusterproxy.ClusterProxy, bizerr.BizError) {
	// Get audit log status from cluster via cluster proxy.
	platformAdminPwd, err := svc.encryptor.DecryptBase64(ctx, cluster.EncryptedPlatformAdminPassword)
	if err != nil {
		return nil, err
	}

	regionalTidbGroup, err := svc.regionalServerCli.GetTidbGroup(
		ctx,
		regionEn,
		&clusterngv1beta.GetTiDBGroupRequest{
			ClusterId:   strconv.FormatUint(cluster.ClusterId, 10),
			TidbGroupId: strconv.FormatUint(tidbgroup.Id, 10),
		},
	)
	if err != nil {
		return nil, err
	}

	proxyBaseUrl := clusterproxy.BuildProxyBaseUrl(
		regionEn.ServiceAddress,
		regionalTidbGroup.GetSharedPoolId(),
		cluster.ClusterId,
		tidbgroup.Id,
	)
	proxy, err := svc.clusterProxyProvider.GetClusterProxy(
		ctx,
		cluster.ClusterId,
		proxyBaseUrl,
		cluster.Version,
		&pkgclusterproxy.ClusterOperator{
			Username:   clusterproxy.PlatformAdminUser,
			AuthMethod: pkgclusterproxy.AuthMethodPassword,
			Credential: platformAdminPwd,
		},
	)
	if err != nil {
		return nil, err
	}

	return proxy, nil
}
