package cluster

import (
	"context"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

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

const (
	// Set response-content-disposition to 'attachment' to make browser don't attempt to render file
	respContentDispositionAttachment string = "attachment"

	// The default duration time to expires presigned URL
	defaultPresignExpires time.Duration = 1 * time.Minute
)

func (s *serviceImpl) GetCaCertificateDownloadUrl(
	ctx context.Context,
	request *globalngv1.GetCaCertificateDownloadUrlRequest,
) (*entity.CaCertificateDownloadUrl, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "ClusterService.Get")
	defer span.End()

	if request.ClusterId == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster_id is required")
	}
	cluster, err := s.clusterRepo.Get(ctx, request.ClusterId, false)
	if err != nil {
		return nil, err
	}
	if cluster == nil {
		tilog.Warn(ctx, "cluster not found", zap.Uint64("cluster_id", request.ClusterId))
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster not found")
	}

	bucket := s.cfg.BizConfig.ClusterCertsConfig.Bucket
	if bucket == "" {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "cluster certs S3 bucket is not configured")
	}

	key := s.cfg.BizConfig.ClusterCertsConfig.DefaultKey

	// Priority check: if the cluster's resource pool has a PCA ARN, use the
	// PcaArnCertKeyMappings config to determine the cert key.
	pcaArnResolved := false
	if cluster.SharedPoolId != "" {
		poolId, parseErr := strconv.ParseUint(cluster.SharedPoolId, 10, 64)
		if parseErr != nil {
			return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "invalid shared_pool_id: %s", cluster.SharedPoolId)
		}

		resourcePool, loadErr := s.resourcePoolRepo.Load(ctx, poolId, false)
		if loadErr != nil {
			return nil, loadErr
		}

		if resourcePool != nil && resourcePool.GetPcaArn() != "" {
			pcaArn := resourcePool.GetPcaArn()
			if s.cfg.BizConfig.ClusterCertsConfig.PcaArnCertKeyMappings == nil {
				return nil, bizerr.New(bizerr.GeneralInternalErrorCode,
					"PcaArnCertKeyMappings is not configured")
			}
			certKey, ok := s.cfg.BizConfig.ClusterCertsConfig.PcaArnCertKeyMappings[pcaArn]
			if !ok {
				return nil, bizerr.New(bizerr.GeneralInternalErrorCode,
					"no cert key mapping found for PCA ARN: %s", pcaArn)
			}
			key = certKey
			pcaArnResolved = true
		}
	}

	// Fallback: use ResourcePoolCertKeyMappings if PCA ARN was not resolved.
	if !pcaArnResolved && s.cfg.BizConfig.ClusterCertsConfig.ResourcePoolCertKeyMappings != nil {
		if specificKey, ok := s.cfg.BizConfig.ClusterCertsConfig.ResourcePoolCertKeyMappings[cluster.SharedPoolId]; ok {
			key = specificKey
		}
	}

	if key == "" {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "cluster certs S3 key is not configured")
	}

	preSignResp, err := s.s3PresignClient.PresignGetObject(ctx, &s3.GetObjectInput{
		Bucket:                     aws.String(bucket),
		Key:                        aws.String(key),
		ResponseContentDisposition: aws.String(respContentDispositionAttachment),
	}, s3.WithPresignExpires(defaultPresignExpires))

	if err != nil {
		return nil, bizerr.New(bizerr.GeneralInternalErrorCode, "failed to get CA certificate presign URL: %v", err)
	}

	return &entity.CaCertificateDownloadUrl{
		Uri: preSignResp.URL,
	}, nil
}
