package auditlog

import (
	"context"
	"strconv"

	"github.com/samber/lo"
	clusterdedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-server/v1"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func (svc *service) QueryAuditLogFiles(
	ctx context.Context,
	req *globaldedicatedv1.QueryAuditLogFilesRequest,
) (*globaldedicatedv1.QueryAuditLogFilesResponse, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "Service.ListAuditLogs")
	defer span.End()

	// Get the cluster and region.
	cluster, err := svc.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}

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

	auditlogs := make([]*globaldedicatedv1.QueryAuditLogFilesResponse_AuditLogFile, 0)
	continuationToken := ""
	for {
		resp, err := svc.regionServerCli.ListAuditLogs(ctx, region, &clusterdedicatedv1.ListAuditLogsRequest{
			ClusterId: strconv.FormatUint(req.GetClusterId(), 10),
			Filter: &clusterdedicatedv1.ListAuditLogsFilter{
				CreatedAtGte: req.GetStartTime(),
				CreatedAtLt:  req.GetEndTime(),
			},
			ContinuationToken: lo.EmptyableToPtr(continuationToken),
		})
		if err != nil {
			if err.ErrCode() == bizerr.ErrCodeRecordNotFound {
				tilog.Info(ctx, "no more audit logs files",
					zap.Uint64("cluster_id", req.GetClusterId()),
					zap.String("continuation_token", continuationToken),
				)
				break
			}
			return nil, err
		}

		auditlogs = append(auditlogs, lo.Map(
			resp.GetFiles(),
			func(log *clusterdedicatedv1.AuditLog, _ int) *globaldedicatedv1.QueryAuditLogFilesResponse_AuditLogFile {
				return cvtGlobalAuditLog(log)
			},
		)...)
		// if no more files, or no continuation token, break
		if resp.ContinuationToken == nil || len(resp.GetFiles()) == 0 {
			break
		}
		continuationToken = lo.FromPtr(resp.ContinuationToken)
	}
	return &globaldedicatedv1.QueryAuditLogFilesResponse{
		AuditLogFiles: auditlogs,
		Total:         uint32(len(auditlogs)),
	}, nil
}

func (svc *service) GenerateAuditLogFileDownloadAddress(
	ctx context.Context,
	req *globaldedicatedv1.GenerateAuditLogFileDownloadAddressRequest,
) (*globaldedicatedv1.GenerateAuditLogFileDownloadAddressResponse, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "Service.GenerateAuditLogFileDownloadAddress")
	defer span.End()

	// Get the cluster and region.
	cluster, err := svc.clusterRepo.Get(ctx, req.GetClusterId(), false)
	if err != nil {
		return nil, err
	}
	region, err := svc.regionRepo.SelectRegionById(ctx, cluster.RegionId, cluster.OrganizationId)
	if err != nil {
		return nil, err
	}

	resp, err := svc.regionServerCli.GetAuditLogDownloadUrl(
		ctx,
		region,
		&clusterdedicatedv1.GetAuditLogDownloadURLRequest{
			ClusterId: strconv.FormatUint(req.GetClusterId(), 10),
			Uri:       req.GetAuditLogFileUri(),
		},
	)
	if err != nil {
		return nil, err
	}

	return &globaldedicatedv1.GenerateAuditLogFileDownloadAddressResponse{
		DownloadAddress: resp.GetPresignedUrl(),
	}, nil
}

func cvtGlobalAuditLog(
	auditlog *clusterdedicatedv1.AuditLog,
) *globaldedicatedv1.QueryAuditLogFilesResponse_AuditLogFile {
	return &globaldedicatedv1.QueryAuditLogFilesResponse_AuditLogFile{
		Filename:   auditlog.GetFilename(),
		SizeByte:   int32(auditlog.GetSize()),
		Bucket:     auditlog.GetBucketName(),
		Uri:        auditlog.GetUri(),
		CreateTime: auditlog.GetCreatedAt(),
	}
}
