package auditlog

import (
	"context"
	"strconv"

	"github.com/samber/lo"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"github.com/tidbcloud/tiinf/sdk/auditlog"
	"go.uber.org/zap"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	openapidedicatedv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/dedicated/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/eventbus"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func (s serverImpl) QueryAuditLogFiles(
	ctx context.Context,
	request *openapidedicatedv1beta1.QueryAuditLogFilesRequest,
) (*openapidedicatedv1beta1.QueryAuditLogFilesResponse, error) {
	ctx, span := tracer.Start(ctx, "ListAuditLogs")
	defer span.End()

	// Extract request meta.
	_, err := metadata.ExtractRequestMetaWithCheckPermission(ctx)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "failed to extract request metadata")
	}

	clusterId, err := strconv.ParseUint(request.GetClusterId(), 10, 64)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "invalid cluster id")
	}

	resp, err := s.dedicatedCli.QueryAuditLogFiles(ctx, &globaldedicatedv1.QueryAuditLogFilesRequest{
		ClusterId: clusterId,
		StartTime: request.GetStartTime(),
		EndTime:   request.GetEndTime(),
	})
	if err != nil {
		return nil, err
	}

	auditlogs := lo.Map(
		resp.GetAuditLogFiles(),
		func(log *globaldedicatedv1.QueryAuditLogFilesResponse_AuditLogFile, _ int) *openapidedicatedv1beta1.QueryAuditLogFilesResponse_AuditLogFile {
			return &openapidedicatedv1beta1.QueryAuditLogFilesResponse_AuditLogFile{
				Filename:   log.GetFilename(),
				Uri:        log.GetUri(),
				Bucket:     log.GetBucket(),
				SizeByte:   log.GetSizeByte(),
				CreateTime: log.GetCreateTime(),
			}
		},
	)
	tilog.Info(ctx, "QueryAuditLogFiles", zap.Any("request", request), zap.Any("response", auditlogs))
	pagedRes, nextToken, err := common.LocalPagination(
		auditlogs,
		request,
		common.ListDefaultPageSize,
		common.ListMaxPageSize,
	)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeUnknown, "failed to paginate audit logs")
	}

	return &openapidedicatedv1beta1.QueryAuditLogFilesResponse{
		AuditLogFiles: pagedRes,
		TotalSize:     int32(len(resp.GetAuditLogFiles())),
		NextPageToken: nextToken,
	}, nil
}

func (s serverImpl) GenerateAuditLogFileDownloadAddress(
	ctx context.Context,
	request *openapidedicatedv1beta1.GenerateAuditLogFileDownloadAddressRequest,
) (*openapidedicatedv1beta1.GenerateAuditLogFileDownloadAddressResponse, error) {
	ctx, span := tracer.Start(ctx, "GenerateAuditLogFileDownloadAddress")
	defer span.End()

	var (
		err       error
		reqMeta   *metadata.RequestMeta
		clusterId uint64
	)

	defer func() {
		s.auditLogSender.SubmitAuditLog(
			ctx,
			auditlog.EventGenerateAuditLogFileDownloadAddress,
			reqMeta,
			func() auditlog.Details {
				details := eventbus.NewAuditLogDetails(eventbus.TierTypeDedicated)
				details = eventbus.AppendDbAuditLogDetails(details, clusterId, "", request)
				return details
			},
			err,
		)
	}()

	clusterId, err = strconv.ParseUint(request.GetClusterId(), 10, 64)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "invalid cluster id")
	}

	// Extract request meta.
	reqMeta, err = metadata.ExtractRequestMetaWithCheckPermission(ctx)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "failed to extract request metadata")
	}

	resp, err := s.dedicatedCli.GenerateAuditLogFileDownloadAddress(
		ctx,
		&globaldedicatedv1.GenerateAuditLogFileDownloadAddressRequest{
			ClusterId:       clusterId,
			AuditLogFileUri: request.GetAuditLogFileUri(),
		},
	)
	if err != nil {
		return nil, err
	}
	return &openapidedicatedv1beta1.GenerateAuditLogFileDownloadAddressResponse{
		DownloadAddress: resp.GetDownloadAddress(),
	}, nil
}
