package upload

import (
	"errors"
	"mime/multipart"
	"path"
	"strings"
	"time"

	"github.com/google/uuid"
)

var (
	ErrFileEmpty     = errors.New("文件为空")
	ErrFileTooLarge  = errors.New("文件过大")
	ErrFileTypeError = errors.New("文件类型不允许")
)

// OSS 对象存储接口
type OSS interface {
	UploadFile(file *multipart.FileHeader) (string, string, error)
	DeleteFile(key string) error
}

// Config 上传配置
type Config struct {
	Type    string // local, aliyun, tencent, qiniu, minio, aws-s3, huawei-obs, cloudflare-r2
	Path    string
	MaxSize int64
}

// OSSConfig 完整的OSS配置
type OSSConfig struct {
	OssType string

	// Local配置
	LocalPath      string
	LocalStorePath string

	// 七牛云配置
	QiniuZone          string
	QiniuBucket        string
	QiniuImgPath       string
	QiniuAccessKey     string
	QiniuSecretKey     string
	QiniuUseHTTPS      bool
	QiniuUseCdnDomains bool

	// 腾讯云COS配置
	TencentBucket     string
	TencentRegion     string
	TencentSecretID   string
	TencentSecretKey  string
	TencentBaseURL    string
	TencentPathPrefix string

	// 阿里云OSS配置
	AliyunEndpoint        string
	AliyunAccessKeyID     string
	AliyunAccessKeySecret string
	AliyunBucketName      string
	AliyunBucketURL       string
	AliyunBasePath        string

	// 华为云OBS配置
	HuaweiPath      string
	HuaweiBucket    string
	HuaweiEndpoint  string
	HuaweiAccessKey string
	HuaweiSecretKey string

	// AWS S3配置
	AwsBucket           string
	AwsRegion           string
	AwsEndpoint         string
	AwsSecretID         string
	AwsSecretKey        string
	AwsBaseURL          string
	AwsPathPrefix       string
	AwsS3ForcePathStyle bool
	AwsDisableSSL       bool

	// Cloudflare R2配置
	CloudflareBucket          string
	CloudflareBaseURL         string
	CloudflarePath            string
	CloudflareAccountID       string
	CloudflareAccessKeyID     string
	CloudflareSecretAccessKey string

	// MinIO配置
	MinioEndpoint        string
	MinioAccessKeyID     string
	MinioAccessKeySecret string
	MinioBucketName      string
	MinioUseSSL          bool
	MinioBasePath        string
	MinioBucketURL       string
}

// NewOSS 根据配置创建OSS实例
func NewOSS(cfg *OSSConfig) (OSS, error) {
	switch cfg.OssType {
	case "local":
		return NewLocal(cfg.LocalStorePath, cfg.LocalPath), nil
	case "qiniu":
		return NewQiniu(
			cfg.QiniuZone,
			cfg.QiniuBucket,
			cfg.QiniuImgPath,
			cfg.QiniuAccessKey,
			cfg.QiniuSecretKey,
			cfg.QiniuUseHTTPS,
			cfg.QiniuUseCdnDomains,
		), nil
	case "tencent-cos":
		return NewTencent(
			cfg.TencentBucket,
			cfg.TencentRegion,
			cfg.TencentSecretID,
			cfg.TencentSecretKey,
			cfg.TencentBaseURL,
			cfg.TencentPathPrefix,
		), nil
	case "aliyun-oss":
		return NewAliyun(
			cfg.AliyunEndpoint,
			cfg.AliyunAccessKeyID,
			cfg.AliyunAccessKeySecret,
			cfg.AliyunBucketName,
			cfg.AliyunBucketURL,
			cfg.AliyunBasePath,
		), nil
	case "huawei-obs":
		return NewHuaweiObs(
			cfg.HuaweiPath,
			cfg.HuaweiBucket,
			cfg.HuaweiEndpoint,
			cfg.HuaweiAccessKey,
			cfg.HuaweiSecretKey,
		), nil
	case "aws-s3":
		return NewAwsS3(
			cfg.AwsBucket,
			cfg.AwsRegion,
			cfg.AwsEndpoint,
			cfg.AwsSecretID,
			cfg.AwsSecretKey,
			cfg.AwsBaseURL,
			cfg.AwsPathPrefix,
			cfg.AwsS3ForcePathStyle,
			cfg.AwsDisableSSL,
		), nil
	case "cloudflare-r2":
		return NewCloudflareR2(
			cfg.CloudflareBucket,
			cfg.CloudflareBaseURL,
			cfg.CloudflarePath,
			cfg.CloudflareAccountID,
			cfg.CloudflareAccessKeyID,
			cfg.CloudflareSecretAccessKey,
		), nil
	case "minio":
		return NewMinio(
			cfg.MinioAccessKeyID,
			cfg.MinioAccessKeySecret,
			cfg.MinioBucketName,
			cfg.MinioEndpoint,
			cfg.MinioBasePath,
			cfg.MinioUseSSL,
		), nil
	default:
		return NewLocal(cfg.LocalStorePath, cfg.LocalPath), nil
	}
}

// GenerateFileName 生成文件名
func GenerateFileName(originalName string) string {
	ext := path.Ext(originalName)
	name := uuid.New().String()
	return name + ext
}

// GeneratePath 生成存储路径
func GeneratePath(basePath string) string {
	now := time.Now()
	return path.Join(basePath, now.Format("2006/01/02"))
}

// CheckFileSize 检查文件大小
func CheckFileSize(size int64, maxSize int64) error {
	if size > maxSize {
		return ErrFileTooLarge
	}
	return nil
}

// CheckFileType 检查文件类型
func CheckFileType(filename string, allowTypes []string) error {
	ext := strings.ToLower(path.Ext(filename))
	for _, t := range allowTypes {
		if ext == t || t == "*" {
			return nil
		}
	}
	return ErrFileTypeError
}

// DefaultAllowTypes 默认允许的文件类型
var DefaultAllowTypes = []string{
	".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp",
	".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
	".txt", ".md", ".json", ".xml", ".csv",
	".zip", ".rar", ".7z", ".tar", ".gz",
	".mp3", ".mp4", ".avi", ".mov", ".wmv",
}
