package utils

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	cos "github.com/tencentyun/cos-go-sdk-v5"
)

// StorageType 存储类型
type StorageType string

const (
	StorageLocal  StorageType = "local"
	StorageQCloud StorageType = "qcloud"
	StorageAliyun StorageType = "aliyun"
	StorageQiniu  StorageType = "qiniu"
)

// OSSConfig OSS配置
type OSSConfig struct {
	StorageType string
	// 通用字段（向后兼容）
	AccessKey string
	SecretKey string
	Bucket    string
	Region    string
	Domain    string
	// 阿里云OSS专用字段
	OSSAccessKeyID     string
	OSSAccessKeySecret string
	OSSEndpoint        string
	OSSBucket          string
	OSSDomain          string
	// 腾讯云COS专用字段
	QCloudSecretID  string
	QCloudSecretKey string
	QCloudRegion    string
	QCloudBucket    string
	QCloudDomain    string
	// 七牛云专用字段
	QiniuAccessKey string
	QiniuSecretKey string
	QiniuBucket    string
	QiniuRegion    string
	QiniuDomain    string
}

// OSSUploader OSS上传接口
type OSSUploader interface {
	Upload(file multipart.File, filename string, size int64) (string, string, error) // 返回 filePath, fileURL, error
	Delete(filePath string) error
}

// GetOSSUploader 根据存储类型获取上传器
func GetOSSUploader(config *OSSConfig) (OSSUploader, error) {
	switch StorageType(config.StorageType) {
	case StorageLocal:
		// 从环境变量读取配置，如果未设置则使用默认值
		basePath := os.Getenv("UPLOADS_BASE_PATH")
		if basePath == "" {
			basePath = "./uploads"
		}
		baseURL := os.Getenv("UPLOADS_BASE_URL")
		if baseURL == "" {
			baseURL = "/uploads"
		}
		return &LocalUploader{
			BasePath: basePath,
			BaseURL:  baseURL,
		}, nil
	case StorageQCloud:
		// 优先使用专用字段，如果为空则使用通用字段（向后兼容）
		secretID := config.QCloudSecretID
		secretKey := config.QCloudSecretKey
		region := config.QCloudRegion
		bucket := config.QCloudBucket
		domain := config.QCloudDomain

		if secretID == "" {
			secretID = config.AccessKey
		}
		if secretKey == "" {
			secretKey = config.SecretKey
		}
		if region == "" {
			region = config.Region
		}
		if bucket == "" {
			bucket = config.Bucket
		}
		if domain == "" {
			domain = config.Domain
		}

		if secretID == "" || secretKey == "" || bucket == "" || region == "" {
			return nil, fmt.Errorf("qcloud config incomplete: secretID, secretKey, bucket, region are required")
		}

		u, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
		if err != nil {
			return nil, fmt.Errorf("invalid qcloud config: %v", err)
		}
		b := &cos.BaseURL{BucketURL: u}
		client := cos.NewClient(b, &http.Client{
			Transport: &cos.AuthorizationTransport{
				SecretID:  secretID,
				SecretKey: secretKey,
			},
		})
		return &QCloudUploader{
			Client: client,
			Bucket: bucket,
			Domain: domain,
		}, nil
	case StorageAliyun:
		// 优先使用专用字段，如果为空则使用通用字段（向后兼容）
		accessKeyID := config.OSSAccessKeyID
		accessKeySecret := config.OSSAccessKeySecret
		endpoint := config.OSSEndpoint
		bucket := config.OSSBucket
		domain := config.OSSDomain

		if accessKeyID == "" {
			accessKeyID = config.AccessKey
		}
		if accessKeySecret == "" {
			accessKeySecret = config.SecretKey
		}
		if bucket == "" {
			bucket = config.Bucket
		}
		if domain == "" {
			domain = config.Domain
		}

		if accessKeyID == "" || accessKeySecret == "" || bucket == "" {
			return nil, fmt.Errorf("aliyun config incomplete: accessKeyID, accessKeySecret, bucket are required")
		}

		// 如果endpoint为空，从region构建（向后兼容）
		if endpoint == "" {
			region := config.Region
			if region == "" {
				return nil, fmt.Errorf("aliyun config incomplete: endpoint or region is required")
			}
			endpoint = fmt.Sprintf("https://oss-%s.aliyuncs.com", region)
		}

		client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
		if err != nil {
			return nil, fmt.Errorf("failed to create aliyun oss client: %v", err)
		}
		ossBucket, err := client.Bucket(bucket)
		if err != nil {
			return nil, fmt.Errorf("failed to get aliyun bucket: %v", err)
		}
		return &AliyunUploader{
			Bucket: ossBucket,
			Domain: domain,
		}, nil
	case StorageQiniu:
		// 优先使用专用字段，如果为空则使用通用字段（向后兼容）
		accessKey := config.QiniuAccessKey
		secretKey := config.QiniuSecretKey
		bucket := config.QiniuBucket
		region := config.QiniuRegion
		domain := config.QiniuDomain

		if accessKey == "" {
			accessKey = config.AccessKey
		}
		if secretKey == "" {
			secretKey = config.SecretKey
		}
		if bucket == "" {
			bucket = config.Bucket
		}
		if domain == "" {
			domain = config.Domain
		}

		if accessKey == "" || secretKey == "" || bucket == "" {
			return nil, fmt.Errorf("qiniu config incomplete: accessKey, secretKey, bucket are required")
		}

		mac := qbox.NewMac(accessKey, secretKey)

		// 根据region选择Zone（如果region为空，使用通用字段的region，否则默认华东）
		var zone *storage.Zone
		if region != "" {
			switch region {
			case "z0", "华东":
				zone = &storage.ZoneHuadong
			case "z1", "华北":
				zone = &storage.ZoneHuabei
			case "z2", "华南":
				zone = &storage.ZoneHuanan
			case "na0", "北美":
				zone = &storage.ZoneBeimei
			case "as0", "东南亚":
				zone = &storage.ZoneXinjiapo
			default:
				zone = &storage.ZoneHuadong // 默认华东
			}
		} else if config.Region != "" {
			// 使用通用字段的region
			switch config.Region {
			case "z0", "华东":
				zone = &storage.ZoneHuadong
			case "z1", "华北":
				zone = &storage.ZoneHuabei
			case "z2", "华南":
				zone = &storage.ZoneHuanan
			case "na0", "北美":
				zone = &storage.ZoneBeimei
			case "as0", "东南亚":
				zone = &storage.ZoneXinjiapo
			default:
				zone = &storage.ZoneHuadong
			}
		} else {
			zone = &storage.ZoneHuadong // 默认华东
		}

		cfg := storage.Config{
			Zone:          zone,
			UseHTTPS:      true,
			UseCdnDomains: false,
		}
		formUploader := storage.NewFormUploader(&cfg)
		bucketManager := storage.NewBucketManager(mac, &cfg)
		return &QiniuUploader{
			FormUploader:  formUploader,
			BucketManager: bucketManager,
			Bucket:        bucket,
			Domain:        domain,
			Mac:           mac,
		}, nil
	default:
		return nil, fmt.Errorf("unsupported storage type: %s", config.StorageType)
	}
}

// LocalUploader 本地存储上传器
type LocalUploader struct {
	BasePath string
	BaseURL  string
}

// Upload 上传文件到本地
func (l *LocalUploader) Upload(file multipart.File, filename string, size int64) (string, string, error) {
	// 生成唯一文件名
	ext := filepath.Ext(filename)
	timestamp := time.Now().Unix()
	randomStr := fmt.Sprintf("%d", timestamp)
	newFilename := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filename, ext), randomStr, ext)

	// 按日期创建目录
	dateDir := time.Now().Format("2006/01/02")
	uploadDir := filepath.Join(l.BasePath, dateDir)

	// 创建目录
	if err := os.MkdirAll(uploadDir, 0755); err != nil {
		return "", "", fmt.Errorf("failed to create upload directory: %v", err)
	}

	// 完整文件路径
	filePath := filepath.Join(uploadDir, newFilename)

	// 创建目标文件
	dst, err := os.Create(filePath)
	if err != nil {
		return "", "", fmt.Errorf("failed to create file: %v", err)
	}
	defer dst.Close()

	// 复制文件内容
	if _, err := io.Copy(dst, file); err != nil {
		return "", "", fmt.Errorf("failed to copy file: %v", err)
	}

	// 生成访问URL
	fileURL := fmt.Sprintf("%s/%s/%s", l.BaseURL, dateDir, newFilename)

	return filePath, fileURL, nil
}

// Delete 删除本地文件
func (l *LocalUploader) Delete(filePath string) error {
	// 确保文件路径在BasePath内（安全措施）
	absBasePath, err := filepath.Abs(l.BasePath)
	if err != nil {
		return err
	}

	absFilePath, err := filepath.Abs(filePath)
	if err != nil {
		return err
	}

	if !strings.HasPrefix(absFilePath, absBasePath) {
		return fmt.Errorf("invalid file path: outside base directory")
	}

	return os.Remove(filePath)
}

// AliyunUploader 阿里云OSS上传器
type AliyunUploader struct {
	Bucket *oss.Bucket
	Domain string
}

// Upload 上传文件到阿里云OSS
func (a *AliyunUploader) Upload(file multipart.File, filename string, size int64) (string, string, error) {
	// 生成唯一文件名
	ext := filepath.Ext(filename)
	timestamp := time.Now().Unix()
	randomStr := fmt.Sprintf("%d", timestamp)
	newFilename := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filename, ext), randomStr, ext)

	// 按日期创建目录
	dateDir := time.Now().Format("2006/01/02")
	objectKey := fmt.Sprintf("%s/%s", dateDir, newFilename)

	// 上传文件
	err := a.Bucket.PutObject(objectKey, file)
	if err != nil {
		return "", "", fmt.Errorf("failed to upload to aliyun oss: %v", err)
	}

	// 生成访问URL
	var fileURL string
	if a.Domain != "" {
		fileURL = fmt.Sprintf("%s/%s", strings.TrimSuffix(a.Domain, "/"), objectKey)
	} else {
		// 从endpoint中提取region，格式为 https://oss-region.aliyuncs.com
		endpoint := a.Bucket.Client.Config.Endpoint
		fileURL = fmt.Sprintf("%s/%s", endpoint, objectKey)
	}

	return objectKey, fileURL, nil
}

// Delete 删除阿里云OSS文件
func (a *AliyunUploader) Delete(objectKey string) error {
	err := a.Bucket.DeleteObject(objectKey)
	if err != nil {
		return fmt.Errorf("failed to delete from aliyun oss: %v", err)
	}
	return nil
}

// QCloudUploader 腾讯云COS上传器
type QCloudUploader struct {
	Client *cos.Client
	Bucket string
	Domain string
}

// Upload 上传文件到腾讯云COS
func (q *QCloudUploader) Upload(file multipart.File, filename string, size int64) (string, string, error) {
	// 生成唯一文件名
	ext := filepath.Ext(filename)
	timestamp := time.Now().Unix()
	randomStr := fmt.Sprintf("%d", timestamp)
	newFilename := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filename, ext), randomStr, ext)

	// 按日期创建目录
	dateDir := time.Now().Format("2006/01/02")
	objectKey := fmt.Sprintf("%s/%s", dateDir, newFilename)

	// 上传文件
	_, err := q.Client.Object.Put(context.Background(), objectKey, file, nil)
	if err != nil {
		return "", "", fmt.Errorf("failed to upload to qcloud cos: %v", err)
	}

	// 生成访问URL
	var fileURL string
	if q.Domain != "" {
		fileURL = fmt.Sprintf("%s/%s", strings.TrimSuffix(q.Domain, "/"), objectKey)
	} else {
		// 使用BucketURL生成URL
		fileURL = fmt.Sprintf("%s/%s", q.Client.BaseURL.BucketURL.String(), objectKey)
	}

	return objectKey, fileURL, nil
}

// Delete 删除腾讯云COS文件
func (q *QCloudUploader) Delete(objectKey string) error {
	_, err := q.Client.Object.Delete(context.Background(), objectKey)
	if err != nil {
		return fmt.Errorf("failed to delete from qcloud cos: %v", err)
	}
	return nil
}

// QiniuUploader 七牛云上传器
type QiniuUploader struct {
	FormUploader  *storage.FormUploader
	BucketManager *storage.BucketManager
	Bucket        string
	Domain        string
	Mac           *qbox.Mac
}

// Upload 上传文件到七牛云
func (q *QiniuUploader) Upload(file multipart.File, filename string, size int64) (string, string, error) {
	// 生成唯一文件名
	ext := filepath.Ext(filename)
	timestamp := time.Now().Unix()
	randomStr := fmt.Sprintf("%d", timestamp)
	newFilename := fmt.Sprintf("%s_%s%s", strings.TrimSuffix(filename, ext), randomStr, ext)

	// 按日期创建目录
	dateDir := time.Now().Format("2006/01/02")
	key := fmt.Sprintf("%s/%s", dateDir, newFilename)

	// 生成上传凭证
	putPolicy := storage.PutPolicy{
		Scope: q.Bucket,
	}
	upToken := putPolicy.UploadToken(q.Mac)

	// 读取文件内容
	fileData := make([]byte, size)
	_, err := file.Read(fileData)
	if err != nil {
		return "", "", fmt.Errorf("failed to read file: %v", err)
	}

	// 上传文件
	ret := storage.PutRet{}
	err = q.FormUploader.Put(context.Background(), &ret, upToken, key, bytes.NewReader(fileData), size, nil)
	if err != nil {
		return "", "", fmt.Errorf("failed to upload to qiniu: %v", err)
	}

	// 生成访问URL
	var fileURL string
	if q.Domain != "" {
		fileURL = fmt.Sprintf("%s/%s", strings.TrimSuffix(q.Domain, "/"), key)
	} else {
		// 七牛云需要配置域名，如果没有配置则返回key
		fileURL = key
	}

	return key, fileURL, nil
}

// Delete 删除七牛云文件
func (q *QiniuUploader) Delete(key string) error {
	err := q.BucketManager.Delete(q.Bucket, key)
	if err != nil {
		return fmt.Errorf("failed to delete from qiniu: %v", err)
	}
	return nil
}

// GetFileType 根据MIME类型判断文件类型
func GetFileType(mimeType string) string {
	if strings.HasPrefix(mimeType, "image/") {
		return "image"
	} else if strings.HasPrefix(mimeType, "video/") {
		return "video"
	} else if strings.HasPrefix(mimeType, "application/pdf") ||
		strings.HasPrefix(mimeType, "application/msword") ||
		strings.HasPrefix(mimeType, "application/vnd.openxmlformats") ||
		strings.HasPrefix(mimeType, "text/") {
		return "document"
	}
	return "other"
}
