package storage

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"github.com/google/uuid"

	"github.com/break/junhong_cmp_fiber/pkg/config"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

type Service struct {
	provider Provider
	config   *config.StorageConfig
}

func NewService(provider Provider, cfg *config.StorageConfig) *Service {
	return &Service{
		provider: provider,
		config:   cfg,
	}
}

func (s *Service) GenerateFileKey(purpose, fileName string) (string, error) {
	mapping, ok := PurposeMappings[purpose]
	if !ok {
		return "", errors.New(errors.CodeInvalidParam, "不支持的文件用途: %s", purpose)
	}

	ext := filepath.Ext(fileName)
	if ext == "" {
		ext = ".bin"
	}
	if (purpose == constants.StoragePurposeAssetPackageBatchOrder || purpose == constants.StoragePurposeDeviceBatchAllocation) && !strings.EqualFold(ext, ".csv") {
		return "", errors.New(errors.CodeInvalidParam, "批量业务文件必须为CSV格式")
	}

	now := time.Now()
	id := uuid.New().String()

	key := fmt.Sprintf("%s/%04d/%02d/%02d/%s%s",
		mapping.Prefix,
		now.Year(),
		now.Month(),
		now.Day(),
		id,
		strings.ToLower(ext),
	)

	return key, nil
}

func (s *Service) GetUploadURL(ctx context.Context, purpose, fileName, contentType string) (*PresignResult, error) {
	fileKey, err := s.GenerateFileKey(purpose, fileName)
	if err != nil {
		return nil, err
	}

	if contentType == "" {
		if mapping, ok := PurposeMappings[purpose]; ok && mapping.ContentType != "" {
			contentType = mapping.ContentType
		} else {
			contentType = "application/octet-stream"
		}
	}

	expires := s.config.Presign.UploadExpires
	if expires == 0 {
		expires = 15 * time.Minute
	}

	url, err := s.provider.GetUploadURL(ctx, fileKey, contentType, expires)
	if err != nil {
		return nil, err
	}

	return &PresignResult{
		URL:       url,
		FileKey:   fileKey,
		ExpiresIn: int(expires.Seconds()),
	}, nil
}

func (s *Service) GetDownloadURL(ctx context.Context, fileKey string) (*PresignResult, error) {
	expires := s.config.Presign.DownloadExpires
	if expires == 0 {
		expires = 24 * time.Hour
	}

	url, err := s.provider.GetDownloadURL(ctx, fileKey, expires)
	if err != nil {
		return nil, err
	}

	return &PresignResult{
		URL:       url,
		FileKey:   fileKey,
		ExpiresIn: int(expires.Seconds()),
	}, nil
}

func (s *Service) DownloadToTemp(ctx context.Context, fileKey string) (string, func(), error) {
	return s.provider.DownloadToTemp(ctx, fileKey)
}

func (s *Service) Provider() Provider {
	return s.provider
}

func (s *Service) Bucket() string {
	return s.config.S3.Bucket
}
